From 35b145389526ffd7b0f7c6df830245e3ea51353b Mon Sep 17 00:00:00 2001 From: dswij Date: Tue, 1 Mar 2022 12:47:55 +0800 Subject: [PATCH 01/82] `map_identity` checks for unneeded `.map_err` --- clippy_lints/src/methods/map_identity.rs | 3 ++- clippy_lints/src/methods/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index f112b500d3d2..862a9578e6ff 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -13,6 +13,7 @@ pub(super) fn check( expr: &hir::Expr<'_>, caller: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, + name: &str, _map_span: Span, ) { let caller_ty = cx.typeck_results().expr_ty(caller); @@ -29,7 +30,7 @@ pub(super) fn check( MAP_IDENTITY, sugg_span, "unnecessary map of the identity function", - "remove the call to `map`", + &format!("remove the call to `{}`", name), String::new(), Applicability::MachineApplicable, ) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3021a40fae14..fd00ac7380de 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2334,7 +2334,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } } }, - ("map", [m_arg]) => { + (name @ ("map" | "map_err"), [m_arg]) => { if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { match (name, args) { ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), @@ -2346,7 +2346,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio _ => {}, } } - map_identity::check(cx, expr, recv, m_arg, span); + map_identity::check(cx, expr, recv, m_arg, name, span); }, ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), (name @ "next", args @ []) => { From 3d1f83efbd7227da1f259f071ab1af294df8d36e Mon Sep 17 00:00:00 2001 From: dswij Date: Tue, 1 Mar 2022 12:54:08 +0800 Subject: [PATCH 02/82] `map_identity` Add tests for needless `.map_err` --- tests/ui/map_identity.fixed | 2 ++ tests/ui/map_identity.rs | 2 ++ tests/ui/map_identity.stderr | 8 +++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/ui/map_identity.fixed b/tests/ui/map_identity.fixed index 4a1452b25f34..2256e51f2d09 100644 --- a/tests/ui/map_identity.fixed +++ b/tests/ui/map_identity.fixed @@ -16,6 +16,8 @@ fn main() { let _: Result = Err(2.3).map(|x: i8| { return x + 3; }); + let _: Result = Ok(1); + let _: Result = Ok(1).map_err(|a: u32| a * 42); } fn not_identity(x: &u16) -> u16 { diff --git a/tests/ui/map_identity.rs b/tests/ui/map_identity.rs index 65c7e6e1ea55..ccfdc9ea76d5 100644 --- a/tests/ui/map_identity.rs +++ b/tests/ui/map_identity.rs @@ -18,6 +18,8 @@ fn main() { let _: Result = Err(2.3).map(|x: i8| { return x + 3; }); + let _: Result = Ok(1).map_err(|a| a); + let _: Result = Ok(1).map_err(|a: u32| a * 42); } fn not_identity(x: &u16) -> u16 { diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr index e4a0320cbda5..b6a77281f6de 100644 --- a/tests/ui/map_identity.stderr +++ b/tests/ui/map_identity.stderr @@ -33,5 +33,11 @@ LL | | return x; LL | | }); | |______^ help: remove the call to `map` -error: aborting due to 5 previous errors +error: unnecessary map of the identity function + --> $DIR/map_identity.rs:21:36 + | +LL | let _: Result = Ok(1).map_err(|a| a); + | ^^^^^^^^^^^^^^^ help: remove the call to `map_err` + +error: aborting due to 6 previous errors From a944ccb67745106a434b9708d010a156acda4d21 Mon Sep 17 00:00:00 2001 From: Tianyi Song <42670338+tysg@users.noreply.github.com> Date: Fri, 11 Mar 2022 11:31:17 +0800 Subject: [PATCH 03/82] Check if lhs < rhs in modulos and emit --- clippy_lints/src/identity_op.rs | 37 ++++++++++++++----- tests/ui/identity_op.rs | 6 +++ tests/ui/identity_op.stderr | 20 +++++++++- tests/ui/modulo_arithmetic_integral_const.rs | 7 +++- .../modulo_arithmetic_integral_const.stderr | 34 ++++++++--------- 5 files changed, 75 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index f824f20ca40a..0b1ab4dfa5a0 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -5,7 +5,7 @@ use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{clip, unsext}; @@ -54,6 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { check(cx, left, -1, e.span, right.span); check(cx, right, -1, e.span, left.span); }, + BinOpKind::Rem => check_modulo(cx, left, right, e.span, left.span), _ => (), } } @@ -70,6 +71,18 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_ && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))) } +fn check_modulo(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { + let lhs_const = constant_full_int(cx, cx.typeck_results(), left); + let rhs_const = constant_full_int(cx, cx.typeck_results(), right); + if match (lhs_const, rhs_const) { + (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv, + (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, + _ => return, + } { + span_ineffective_operation(cx, span, arg); + } +} + fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { @@ -83,15 +96,19 @@ fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { 1 => v == 1, _ => unreachable!(), } { - span_lint( - cx, - IDENTITY_OP, - span, - &format!( - "the operation is ineffective. Consider reducing it to `{}`", - snippet(cx, arg, "..") - ), - ); + span_ineffective_operation(cx, span, arg); } } } + +fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) { + span_lint( + cx, + IDENTITY_OP, + span, + &format!( + "the operation is ineffective. Consider reducing it to `{}`", + snippet(cx, arg, "..") + ), + ); +} diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 12bbda71f434..5e120947646a 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -66,4 +66,10 @@ fn main() { let b = a << 0; // no error: non-integer 1 * Meter; // no error: non-integer + + 2 % 3; + -2 % 3; + x + 1 % 3; + (x + 1) % 3; // no error + 4 % 3; // no error } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 0103cf5457e8..cb9757d1158d 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -78,5 +78,23 @@ error: the operation is ineffective. Consider reducing it to `x` LL | x >> &0; | ^^^^^^^ -error: aborting due to 13 previous errors +error: the operation is ineffective. Consider reducing it to `2` + --> $DIR/identity_op.rs:70:5 + | +LL | 2 % 3; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `-2` + --> $DIR/identity_op.rs:71:5 + | +LL | -2 % 3; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:72:9 + | +LL | x + 1 % 3; + | ^^^^^ + +error: aborting due to 16 previous errors diff --git a/tests/ui/modulo_arithmetic_integral_const.rs b/tests/ui/modulo_arithmetic_integral_const.rs index 047a29fa1e32..3ebe46bc5be7 100644 --- a/tests/ui/modulo_arithmetic_integral_const.rs +++ b/tests/ui/modulo_arithmetic_integral_const.rs @@ -1,5 +1,10 @@ #![warn(clippy::modulo_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::modulo_one, + clippy::identity_op +)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/tests/ui/modulo_arithmetic_integral_const.stderr b/tests/ui/modulo_arithmetic_integral_const.stderr index 64335f35f0f8..11b5f77461ba 100644 --- a/tests/ui/modulo_arithmetic_integral_const.stderr +++ b/tests/ui/modulo_arithmetic_integral_const.stderr @@ -1,5 +1,5 @@ error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:6:5 + --> $DIR/modulo_arithmetic_integral_const.rs:11:5 | LL | -1 % 2; | ^^^^^^ @@ -9,7 +9,7 @@ LL | -1 % 2; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:7:5 + --> $DIR/modulo_arithmetic_integral_const.rs:12:5 | LL | 1 % -2; | ^^^^^^ @@ -18,7 +18,7 @@ LL | 1 % -2; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 3` - --> $DIR/modulo_arithmetic_integral_const.rs:8:5 + --> $DIR/modulo_arithmetic_integral_const.rs:13:5 | LL | (1 - 2) % (1 + 2); | ^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | (1 - 2) % (1 + 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `3 % -1` - --> $DIR/modulo_arithmetic_integral_const.rs:9:5 + --> $DIR/modulo_arithmetic_integral_const.rs:14:5 | LL | (1 + 2) % (1 - 2); | ^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | (1 + 2) % (1 - 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-35 % 300000` - --> $DIR/modulo_arithmetic_integral_const.rs:10:5 + --> $DIR/modulo_arithmetic_integral_const.rs:15:5 | LL | 35 * (7 - 4 * 2) % (-500 * -600); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | 35 * (7 - 4 * 2) % (-500 * -600); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:12:5 + --> $DIR/modulo_arithmetic_integral_const.rs:17:5 | LL | -1i8 % 2i8; | ^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | -1i8 % 2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:13:5 + --> $DIR/modulo_arithmetic_integral_const.rs:18:5 | LL | 1i8 % -2i8; | ^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | 1i8 % -2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:14:5 + --> $DIR/modulo_arithmetic_integral_const.rs:19:5 | LL | -1i16 % 2i16; | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | -1i16 % 2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:15:5 + --> $DIR/modulo_arithmetic_integral_const.rs:20:5 | LL | 1i16 % -2i16; | ^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL | 1i16 % -2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:16:5 + --> $DIR/modulo_arithmetic_integral_const.rs:21:5 | LL | -1i32 % 2i32; | ^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | -1i32 % 2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:17:5 + --> $DIR/modulo_arithmetic_integral_const.rs:22:5 | LL | 1i32 % -2i32; | ^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | 1i32 % -2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:18:5 + --> $DIR/modulo_arithmetic_integral_const.rs:23:5 | LL | -1i64 % 2i64; | ^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL | -1i64 % 2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:19:5 + --> $DIR/modulo_arithmetic_integral_const.rs:24:5 | LL | 1i64 % -2i64; | ^^^^^^^^^^^^ @@ -117,7 +117,7 @@ LL | 1i64 % -2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:20:5 + --> $DIR/modulo_arithmetic_integral_const.rs:25:5 | LL | -1i128 % 2i128; | ^^^^^^^^^^^^^^ @@ -126,7 +126,7 @@ LL | -1i128 % 2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:21:5 + --> $DIR/modulo_arithmetic_integral_const.rs:26:5 | LL | 1i128 % -2i128; | ^^^^^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | 1i128 % -2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:22:5 + --> $DIR/modulo_arithmetic_integral_const.rs:27:5 | LL | -1isize % 2isize; | ^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | -1isize % 2isize; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:23:5 + --> $DIR/modulo_arithmetic_integral_const.rs:28:5 | LL | 1isize % -2isize; | ^^^^^^^^^^^^^^^^ From 65f96e2b53369f6b403b655b2212272c2ae13d42 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 19 Feb 2022 17:56:42 -0500 Subject: [PATCH 04/82] Rework `undocumented_unsafe_blocks` --- clippy_lints/src/lib.rs | 3 +- .../src/undocumented_unsafe_blocks.rs | 293 +++++++----------- tests/ui/crashes/ice-7868.stderr | 6 +- tests/ui/undocumented_unsafe_blocks.rs | 42 ++- tests/ui/undocumented_unsafe_blocks.stderr | 150 ++++----- 5 files changed, 220 insertions(+), 274 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 504235d0d1ef..49fc82c0ac7a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,5 +1,6 @@ // error-pattern:cargo-clippy +#![feature(array_windows)] #![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] #![feature(control_flow_enum)] @@ -846,7 +847,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: enable_raw_pointer_heuristic_for_send, )) }); - store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default())); + store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch)); store.register_late_pass(move || Box::new(format_args::FormatArgs)); store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray)); diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index e42c6c63ede0..59c3d42b3538 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,16 +1,13 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_lint_allowed; -use clippy_utils::source::{indent_of, reindent_multiline, snippet}; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, UnsafeSource}; -use rustc_lexer::TokenKind; -use rustc_lint::{LateContext, LateLintPass}; +use clippy_utils::source::walk_span_to_context; +use rustc_hir::{Block, BlockCheckMode, UnsafeSource}; +use rustc_lexer::{tokenize, TokenKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::TyCtxt; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{BytePos, Span}; -use std::borrow::Cow; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Pos, SyntaxContext}; +use std::rc::Rc; declare_clippy_lint! { /// ### What it does @@ -44,179 +41,127 @@ declare_clippy_lint! { "creating an unsafe block without explaining why it is safe" } -impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]); - -#[derive(Default)] -pub struct UndocumentedUnsafeBlocks { - pub local_level: u32, - pub local_span: Option, - // The local was already checked for an overall safety comment - // There is no need to continue checking the blocks in the local - pub local_checked: bool, - // Since we can only check the blocks from expanded macros - // We have to omit the suggestion due to the actual definition - // Not being available to us - pub macro_expansion: bool, -} +declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]); impl LateLintPass<'_> for UndocumentedUnsafeBlocks { fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) { - if_chain! { - if !self.local_checked; - if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id); - if !in_external_macro(cx.tcx.sess, block.span); - if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules; - if let Some(enclosing_scope_hir_id) = cx.tcx.hir().get_enclosing_scope(block.hir_id); - if self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, block.span) == Some(false); - then { - let mut span = block.span; + if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + && !in_external_macro(cx.tcx.sess, block.span) + && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) + && !block_has_safety_comment(cx, block) + { + let source_map = cx.tcx.sess.source_map(); + let span = if source_map.is_multiline(block.span) { + source_map.span_until_char(block.span, '\n') + } else { + block.span + }; - if let Some(local_span) = self.local_span { - span = local_span; - - let result = self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, span); - - if result.unwrap_or(true) { - self.local_checked = true; - return; - } - } - - self.lint(cx, span); - } - } - } - - fn check_local(&mut self, cx: &LateContext<'_>, local: &'_ Local<'_>) { - if_chain! { - if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, local.hir_id); - if !in_external_macro(cx.tcx.sess, local.span); - if let Some(init) = local.init; - then { - self.visit_expr(init); - - if self.local_level > 0 { - self.local_span = Some(local.span); - } - } - } - } - - fn check_block_post(&mut self, _: &LateContext<'_>, _: &'_ Block<'_>) { - self.local_level = self.local_level.saturating_sub(1); - - if self.local_level == 0 { - self.local_checked = false; - self.local_span = None; - } - } -} - -impl<'v> Visitor<'v> for UndocumentedUnsafeBlocks { - fn visit_expr(&mut self, ex: &'v Expr<'v>) { - match ex.kind { - ExprKind::Block(_, _) => self.local_level = self.local_level.saturating_add(1), - _ => walk_expr(self, ex), - } - } -} - -impl UndocumentedUnsafeBlocks { - fn block_has_safety_comment(&mut self, tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span: Span) -> Option { - let map = tcx.hir(); - let source_map = tcx.sess.source_map(); - - let enclosing_scope_span = map.opt_span(enclosing_hir_id)?; - - let between_span = if block_span.from_expansion() { - self.macro_expansion = true; - enclosing_scope_span.with_hi(block_span.hi()).source_callsite() - } else { - self.macro_expansion = false; - enclosing_scope_span.to(block_span).source_callsite() - }; - - let file_name = source_map.span_to_filename(between_span); - let source_file = source_map.get_source_file(&file_name)?; - - let lex_start = (between_span.lo().0 - source_file.start_pos.0 + 1) as usize; - let lex_end = (between_span.hi().0 - source_file.start_pos.0) as usize; - let src_str = source_file.src.as_ref()?[lex_start..lex_end].to_string(); - - let source_start_pos = source_file.start_pos.0 as usize + lex_start; - - let mut pos = 0; - let mut comment = false; - - for token in rustc_lexer::tokenize(&src_str) { - match token.kind { - TokenKind::LineComment { doc_style: None } - | TokenKind::BlockComment { - doc_style: None, - terminated: true, - } => { - let comment_str = src_str[pos + 2..pos + token.len].to_ascii_uppercase(); - - if comment_str.contains("SAFETY:") { - comment = true; - } - }, - // We need to add all whitespace to `pos` before checking the comment's line number - TokenKind::Whitespace => {}, - _ => { - if comment { - // Get the line number of the "comment" (really wherever the trailing whitespace ended) - let comment_line_num = source_file - .lookup_file_pos(BytePos((source_start_pos + pos).try_into().unwrap())) - .0; - // Find the block/local's line number - let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line; - - // Check the comment is immediately followed by the block/local - if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num { - return Some(true); - } - - comment = false; - } - }, - } - - pos += token.len; - } - - Some(false) - } - - fn lint(&self, cx: &LateContext<'_>, mut span: Span) { - let source_map = cx.tcx.sess.source_map(); - - if source_map.is_multiline(span) { - span = source_map.span_until_char(span, '\n'); - } - - if self.macro_expansion { span_lint_and_help( - cx, - UNDOCUMENTED_UNSAFE_BLOCKS, - span, - "unsafe block in macro expansion missing a safety comment", - None, - "consider adding a safety comment in the macro definition", - ); - } else { - let block_indent = indent_of(cx, span); - let suggestion = format!("// SAFETY: ...\n{}", snippet(cx, span, "..")); - - span_lint_and_sugg( cx, UNDOCUMENTED_UNSAFE_BLOCKS, span, "unsafe block missing a safety comment", - "consider adding a safety comment", - reindent_multiline(Cow::Borrowed(&suggestion), true, block_indent).to_string(), - Applicability::HasPlaceholders, + None, + "consider adding a safety comment on the preceding line", ); } } } + +/// Checks if the lines immediately preceding the block contain a safety comment. +fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { + // This intentionally ignores text before the start of a function so something like: + // ``` + // // SAFETY: reason + // fn foo() { unsafe { .. } } + // ``` + // won't work. This is to avoid dealing with where such a comment should be place relative to + // attributes and doc comments. + + let source_map = cx.sess().source_map(); + let ctxt = block.span.ctxt(); + if ctxt != SyntaxContext::root() { + // From a macro expansion. Get the text from the start of the macro declaration to start of the unsafe block. + // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; } + // ^--------------------------------------------^ + if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo()) + && Rc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + macro_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true + } + } else if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + && let Some(body) = cx.enclosing_body + && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root()) + && let Ok(body_line) = source_map.lookup_line(body_span.lo()) + && Rc::ptr_eq(&unsafe_line.sf, &body_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + // Get the text from the start of function body to the unsafe block. + // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } + // ^-------------^ + body_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true + } +} + +/// Checks if the given text has a safety comment for the immediately proceeding line. +fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool { + let mut lines = line_starts + .array_windows::<2>() + .rev() + .map_while(|[start, end]| { + src.get(start.to_usize() - offset..end.to_usize() - offset) + .map(|text| (start.to_usize(), text.trim_start())) + }) + .filter(|(_, text)| !text.is_empty()); + + let Some((line_start, line)) = lines.next() else { + return false; + }; + // Check for a sequence of line comments. + if line.starts_with("//") { + let mut line = line; + loop { + if line.to_ascii_uppercase().contains("SAFETY:") { + return true; + } + match lines.next() { + Some((_, x)) if x.starts_with("//") => line = x, + _ => return false, + } + } + } + // No line comments; look for the start of a block comment. + // This will only find them if they are at the start of a line. + let (mut line_start, mut line) = (line_start, line); + loop { + if line.starts_with("/*") { + let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start(); + let mut tokens = tokenize(src); + return src[..tokens.next().unwrap().len] + .to_ascii_uppercase() + .contains("SAFETY:") + && tokens.all(|t| t.kind == TokenKind::Whitespace); + } + match lines.next() { + Some(x) => (line_start, line) = x, + None => return false, + } + } +} diff --git a/tests/ui/crashes/ice-7868.stderr b/tests/ui/crashes/ice-7868.stderr index 111350a6280d..1a33e647588f 100644 --- a/tests/ui/crashes/ice-7868.stderr +++ b/tests/ui/crashes/ice-7868.stderr @@ -5,11 +5,7 @@ LL | unsafe { 0 }; | ^^^^^^^^^^^^ | = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL ~ unsafe { 0 }; - | + = help: consider adding a safety comment on the preceding line error: aborting due to previous error diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index 380303d8152a..fcfb49dcdd44 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -89,11 +89,6 @@ fn block_comment_newlines() { unsafe {} } -#[rustfmt::skip] -fn inline_block_comment() { - /* Safety: */unsafe {} -} - fn block_comment_with_extras() { /* This is a description * SAFETY: @@ -209,6 +204,43 @@ fn local_nest() { let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})]; } +fn in_fn_call(x: *const u32) { + fn f(x: u32) {} + + // Safety: reason + f(unsafe { *x }); +} + +fn multi_in_fn_call(x: *const u32) { + fn f(x: u32, y: u32) {} + + // Safety: reason + f(unsafe { *x }, unsafe { *x }); +} + +fn in_multiline_fn_call(x: *const u32) { + fn f(x: u32, y: u32) {} + + f( + // Safety: reason + unsafe { *x }, + 0, + ); +} + +fn in_macro_call(x: *const u32) { + // Safety: reason + println!("{}", unsafe { *x }); +} + +fn in_multiline_macro_call(x: *const u32) { + println!( + "{}", + // Safety: reason + unsafe { *x }, + ); +} + // Invalid comments fn no_comment() { diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index f69d0da54e0d..4f5c1111c7e3 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -1,114 +1,102 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:215:5 + --> $DIR/undocumented_unsafe_blocks.rs:247:5 | LL | unsafe {} | ^^^^^^^^^ | = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + unsafe {} - | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:219:5 + --> $DIR/undocumented_unsafe_blocks.rs:251:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; + | ^^^^^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:223:5 + --> $DIR/undocumented_unsafe_blocks.rs:251:29 + | +LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; + | ^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:251:48 + | +LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; + | ^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:255:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = (42, unsafe {}, "test", unsafe {}); + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:227:5 + --> $DIR/undocumented_unsafe_blocks.rs:255:37 + | +LL | let _ = (42, unsafe {}, "test", unsafe {}); + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:259:14 | LL | let _ = *unsafe { &42 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = *unsafe { &42 }; + | ^^^^^^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:232:5 + --> $DIR/undocumented_unsafe_blocks.rs:264:19 | LL | let _ = match unsafe {} { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = match unsafe {} { + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:238:5 + --> $DIR/undocumented_unsafe_blocks.rs:270:14 | LL | let _ = &unsafe {}; - | ^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = &unsafe {}; + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:242:5 + --> $DIR/undocumented_unsafe_blocks.rs:274:14 | LL | let _ = [unsafe {}; 5]; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = [unsafe {}; 5]; + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:246:5 + --> $DIR/undocumented_unsafe_blocks.rs:278:13 | LL | let _ = unsafe {}; - | ^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = unsafe {}; + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:256:8 + --> $DIR/undocumented_unsafe_blocks.rs:288:8 | LL | t!(unsafe {}); | ^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ t!(// SAFETY: ... -LL ~ unsafe {}); - | + = help: consider adding a safety comment on the preceding line -error: unsafe block in macro expansion missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:262:13 +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:294:13 | LL | unsafe {} | ^^^^^^^^^ @@ -116,56 +104,40 @@ LL | unsafe {} LL | t!(); | ---- in this macro invocation | - = help: consider adding a safety comment in the macro definition + = help: consider adding a safety comment on the preceding line = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:5 + --> $DIR/undocumented_unsafe_blocks.rs:302:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL ~ unsafe {} // SAFETY: - | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:274:5 + --> $DIR/undocumented_unsafe_blocks.rs:306:5 | LL | unsafe { | ^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + unsafe { - | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:284:5 + --> $DIR/undocumented_unsafe_blocks.rs:316:5 | LL | unsafe {}; | ^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL ~ unsafe {}; - | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:288:20 + --> $DIR/undocumented_unsafe_blocks.rs:320:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ println!("{}", // SAFETY: ... -LL ~ unsafe { String::from_utf8_unchecked(vec![]) }); - | + = help: consider adding a safety comment on the preceding line -error: aborting due to 14 previous errors +error: aborting due to 17 previous errors From 30b333645d1fb7cc4f657ad2e162977db6ee4b8c Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 15 Mar 2022 16:52:28 -0400 Subject: [PATCH 05/82] Don't lint `undocumented_unsafe_blocks` on bad proc-macro spans. --- .../src/undocumented_unsafe_blocks.rs | 12 +++++++ tests/ui/auxiliary/proc_macro_unsafe.rs | 18 ++++++++++ tests/ui/undocumented_unsafe_blocks.rs | 8 +++++ tests/ui/undocumented_unsafe_blocks.stderr | 34 +++++++++---------- 4 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 tests/ui/auxiliary/proc_macro_unsafe.rs diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 59c3d42b3538..ac13a309e7b8 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -48,6 +48,7 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) && !in_external_macro(cx.tcx.sess, block.span) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) + && !is_unsafe_from_proc_macro(cx, block) && !block_has_safety_comment(cx, block) { let source_map = cx.tcx.sess.source_map(); @@ -69,6 +70,17 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { } } +fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { + let source_map = cx.sess().source_map(); + let file_pos = source_map.lookup_byte_offset(block.span.lo()); + file_pos + .sf + .src + .as_deref() + .and_then(|src| src.get(file_pos.pos.to_usize()..)) + .map_or(true, |src| !src.starts_with("unsafe")) +} + /// Checks if the lines immediately preceding the block contain a safety comment. fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { // This intentionally ignores text before the start of a function so something like: diff --git a/tests/ui/auxiliary/proc_macro_unsafe.rs b/tests/ui/auxiliary/proc_macro_unsafe.rs new file mode 100644 index 000000000000..3c40f77469b8 --- /dev/null +++ b/tests/ui/auxiliary/proc_macro_unsafe.rs @@ -0,0 +1,18 @@ +// compile-flags: --emit=link +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree}; + +#[proc_macro] +pub fn unsafe_block(input: TokenStream) -> TokenStream { + let span = input.into_iter().next().unwrap().span(); + TokenStream::from_iter([TokenTree::Ident(Ident::new("unsafe", span)), { + let mut group = Group::new(Delimiter::Brace, TokenStream::new()); + group.set_span(span); + TokenTree::Group(group) + }]) +} diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index fcfb49dcdd44..80fb198ab538 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -1,5 +1,9 @@ +// aux-build:proc_macro_unsafe.rs + #![warn(clippy::undocumented_unsafe_blocks)] +extern crate proc_macro_unsafe; + // Valid comments fn nested_local() { @@ -241,6 +245,10 @@ fn in_multiline_macro_call(x: *const u32) { ); } +fn from_proc_macro() { + proc_macro_unsafe::unsafe_block!(token); +} + // Invalid comments fn no_comment() { diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 4f5c1111c7e3..746bbe2f1414 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:247:5 + --> $DIR/undocumented_unsafe_blocks.rs:255:5 | LL | unsafe {} | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:251:14 + --> $DIR/undocumented_unsafe_blocks.rs:259:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:251:29 + --> $DIR/undocumented_unsafe_blocks.rs:259:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:251:48 + --> $DIR/undocumented_unsafe_blocks.rs:259:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:255:18 + --> $DIR/undocumented_unsafe_blocks.rs:263:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:255:37 + --> $DIR/undocumented_unsafe_blocks.rs:263:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:259:14 + --> $DIR/undocumented_unsafe_blocks.rs:267:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:19 + --> $DIR/undocumented_unsafe_blocks.rs:272:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:14 + --> $DIR/undocumented_unsafe_blocks.rs:278:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:274:14 + --> $DIR/undocumented_unsafe_blocks.rs:282:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:278:13 + --> $DIR/undocumented_unsafe_blocks.rs:286:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:288:8 + --> $DIR/undocumented_unsafe_blocks.rs:296:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -96,7 +96,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:294:13 + --> $DIR/undocumented_unsafe_blocks.rs:302:13 | LL | unsafe {} | ^^^^^^^^^ @@ -108,7 +108,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:302:5 + --> $DIR/undocumented_unsafe_blocks.rs:310:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -116,7 +116,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:306:5 + --> $DIR/undocumented_unsafe_blocks.rs:314:5 | LL | unsafe { | ^^^^^^^^ @@ -124,7 +124,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:316:5 + --> $DIR/undocumented_unsafe_blocks.rs:324:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -132,7 +132,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:320:20 + --> $DIR/undocumented_unsafe_blocks.rs:328:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 2909b33a243591cec6a5d6d51dc6e1684809aeac Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Wed, 16 Mar 2022 16:26:56 +0800 Subject: [PATCH 06/82] quick fix of issue#8542 for lint `[needless_match]` remove `ref`/`ref mut` check --- clippy_lints/src/matches/needless_match.rs | 61 ++++----- tests/ui/needless_match.fixed | 116 ++++++++++++---- tests/ui/needless_match.rs | 148 +++++++++++++++------ tests/ui/needless_match.stderr | 83 ++++++------ 4 files changed, 257 insertions(+), 151 deletions(-) diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 76131d307d77..d9544e18b18b 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{eq_expr_value, get_parent_expr, higher, is_else_clause, is_lang_ctor, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind, Path, PathSegment, QPath, UnOp}; +use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -21,7 +21,7 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) if !eq_expr_value(cx, ex, ret_expr) { return; } - } else if !pat_same_as_expr(arm.pat, arm.body) { + } else if !pat_same_as_expr(arm.pat, peel_blocks_with_stmt(arm.body)) { return; } } @@ -92,6 +92,9 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if matches!(if_else.kind, ExprKind::Block(..)) { let else_expr = peel_blocks_with_stmt(if_else); + if matches!(else_expr.kind, ExprKind::Block(..)) { + return false; + } let ret = strip_return(else_expr); let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { @@ -120,40 +123,25 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { let expr = strip_return(expr); match (&pat.kind, &expr.kind) { // Example: `Some(val) => Some(val)` - ( - PatKind::TupleStruct(QPath::Resolved(_, path), [first_pat, ..], _), - ExprKind::Call(call_expr, [first_param, ..]), - ) => { + (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => { if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind { - if has_identical_segments(path.segments, call_path.segments) - && has_same_non_ref_symbol(first_pat, first_param) - { - return true; - } + return has_identical_segments(path.segments, call_path.segments) + && has_same_non_ref_symbols(tuple_params, call_params); } }, - // Example: `val => val`, or `ref val => *val` - (PatKind::Binding(annot, _, pat_ident, _), _) => { - let new_expr = if let ( - BindingAnnotation::Ref | BindingAnnotation::RefMut, - ExprKind::Unary(UnOp::Deref, operand_expr), - ) = (annot, &expr.kind) - { - operand_expr - } else { - expr - }; - - if let ExprKind::Path(QPath::Resolved( + // Example: `val => val` + ( + PatKind::Binding(annot, _, pat_ident, _), + ExprKind::Path(QPath::Resolved( _, Path { segments: [first_seg, ..], .. }, - )) = new_expr.kind - { - return pat_ident.name == first_seg.ident.name; - } + )), + ) => { + return !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut) + && pat_ident.name == first_seg.ident.name; }, // Example: `Custom::TypeA => Custom::TypeB`, or `None => None` (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => { @@ -183,15 +171,16 @@ fn has_identical_segments(left_segs: &[PathSegment<'_>], right_segs: &[PathSegme true } -fn has_same_non_ref_symbol(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { - if_chain! { - if let PatKind::Binding(annot, _, pat_ident, _) = pat.kind; - if !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); - if let ExprKind::Path(QPath::Resolved(_, Path {segments: [first_seg, ..], .. })) = expr.kind; - then { - return pat_ident.name == first_seg.ident.name; +fn has_same_non_ref_symbols(pats: &[Pat<'_>], exprs: &[Expr<'_>]) -> bool { + if pats.len() != exprs.len() { + return false; + } + + for i in 0..pats.len() { + if !pat_same_as_expr(&pats[i], &exprs[i]) { + return false; } } - false + true } diff --git a/tests/ui/needless_match.fixed b/tests/ui/needless_match.fixed index ece18ad737fd..6a518f839253 100644 --- a/tests/ui/needless_match.fixed +++ b/tests/ui/needless_match.fixed @@ -4,38 +4,35 @@ #![allow(dead_code)] #[derive(Clone, Copy)] -enum Choice { +enum Simple { A, B, C, D, } -#[allow(unused_mut)] fn useless_match() { - let mut i = 10; + let i = 10; let _: i32 = i; - let _: i32 = i; - let mut _i_mut = i; - let s = "test"; let _: &str = s; } -fn custom_type_match(se: Choice) { - let _: Choice = se; +fn custom_type_match() { + let se = Simple::A; + let _: Simple = se; // Don't trigger - let _: Choice = match se { - Choice::A => Choice::A, - Choice::B => Choice::B, - _ => Choice::C, + let _: Simple = match se { + Simple::A => Simple::A, + Simple::B => Simple::B, + _ => Simple::C, }; // Mingled, don't trigger - let _: Choice = match se { - Choice::A => Choice::B, - Choice::B => Choice::C, - Choice::C => Choice::D, - Choice::D => Choice::A, + let _: Simple = match se { + Simple::A => Simple::B, + Simple::B => Simple::C, + Simple::C => Simple::D, + Simple::D => Simple::A, }; } @@ -55,29 +52,96 @@ fn func_ret_err(err: T) -> Result { fn result_match() { let _: Result = Ok(1); let _: Result = func_ret_err(0_i32); + // as ref, don't trigger + let res = &func_ret_err(0_i32); + let _: Result<&i32, &i32> = match *res { + Ok(ref x) => Ok(x), + Err(ref x) => Err(x), + }; } fn if_let_option() -> Option { Some(1) } -fn if_let_result(x: Result<(), i32>) { - let _: Result<(), i32> = x; - let _: Result<(), i32> = x; +fn if_let_result() { + let x: Result = Ok(1); + let _: Result = x; + let _: Result = x; // Input type mismatch, don't trigger - let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x }; + let _: Result = if let Err(e) = Ok(1) { Err(e) } else { x }; } -fn if_let_custom_enum(x: Choice) { - let _: Choice = x; +fn if_let_custom_enum(x: Simple) { + let _: Simple = x; + // Don't trigger - let _: Choice = if let Choice::A = x { - Choice::A + let _: Simple = if let Simple::A = x { + Simple::A } else if true { - Choice::B + Simple::B } else { x }; } +mod issue8542 { + #[derive(Clone, Copy)] + enum E { + VariantA(u8, u8), + VariantB(u8, bool), + } + + enum Complex { + A(u8), + B(u8, bool), + C(u8, i32, f64), + D(E, bool), + } + + fn match_test() { + let ce = Complex::B(8, false); + let aa = 0_u8; + let bb = false; + + let _: Complex = ce; + + // Don't trigger + let _: Complex = match ce { + Complex::A(_) => Complex::A(aa), + Complex::B(_, b) => Complex::B(aa, b), + Complex::C(_, b, _) => Complex::C(aa, b, 64_f64), + Complex::D(e, b) => Complex::D(e, b), + }; + + // Don't trigger + let _: Complex = match ce { + Complex::A(a) => Complex::A(a), + Complex::B(a, _) => Complex::B(a, bb), + Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64), + _ => ce, + }; + } + + fn if_let_test() { + fn do_something() {} + + // Don't trigger + let _ = if let Some(a) = Some(1) { + Some(a) + } else { + do_something(); + None + }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { + do_something(); + Some(a) + } else { + None + }; + } +} + fn main() {} diff --git a/tests/ui/needless_match.rs b/tests/ui/needless_match.rs index 36649de35a60..ffc6a291a157 100644 --- a/tests/ui/needless_match.rs +++ b/tests/ui/needless_match.rs @@ -4,33 +4,21 @@ #![allow(dead_code)] #[derive(Clone, Copy)] -enum Choice { +enum Simple { A, B, C, D, } -#[allow(unused_mut)] fn useless_match() { - let mut i = 10; + let i = 10; let _: i32 = match i { 0 => 0, 1 => 1, 2 => 2, _ => i, }; - let _: i32 = match i { - 0 => 0, - 1 => 1, - ref i => *i, - }; - let mut _i_mut = match i { - 0 => 0, - 1 => 1, - ref mut i => *i, - }; - let s = "test"; let _: &str = match s { "a" => "a", @@ -39,25 +27,26 @@ fn useless_match() { }; } -fn custom_type_match(se: Choice) { - let _: Choice = match se { - Choice::A => Choice::A, - Choice::B => Choice::B, - Choice::C => Choice::C, - Choice::D => Choice::D, +fn custom_type_match() { + let se = Simple::A; + let _: Simple = match se { + Simple::A => Simple::A, + Simple::B => Simple::B, + Simple::C => Simple::C, + Simple::D => Simple::D, }; // Don't trigger - let _: Choice = match se { - Choice::A => Choice::A, - Choice::B => Choice::B, - _ => Choice::C, + let _: Simple = match se { + Simple::A => Simple::A, + Simple::B => Simple::B, + _ => Simple::C, }; // Mingled, don't trigger - let _: Choice = match se { - Choice::A => Choice::B, - Choice::B => Choice::C, - Choice::C => Choice::D, - Choice::D => Choice::A, + let _: Simple = match se { + Simple::A => Simple::B, + Simple::B => Simple::C, + Simple::C => Simple::D, + Simple::D => Simple::A, }; } @@ -86,37 +75,110 @@ fn result_match() { Err(err) => Err(err), Ok(a) => Ok(a), }; + // as ref, don't trigger + let res = &func_ret_err(0_i32); + let _: Result<&i32, &i32> = match *res { + Ok(ref x) => Ok(x), + Err(ref x) => Err(x), + }; } fn if_let_option() -> Option { if let Some(a) = Some(1) { Some(a) } else { None } } -fn if_let_result(x: Result<(), i32>) { - let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x }; - let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x }; +fn if_let_result() { + let x: Result = Ok(1); + let _: Result = if let Err(e) = x { Err(e) } else { x }; + let _: Result = if let Ok(val) = x { Ok(val) } else { x }; // Input type mismatch, don't trigger - let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x }; + let _: Result = if let Err(e) = Ok(1) { Err(e) } else { x }; } -fn if_let_custom_enum(x: Choice) { - let _: Choice = if let Choice::A = x { - Choice::A - } else if let Choice::B = x { - Choice::B - } else if let Choice::C = x { - Choice::C +fn if_let_custom_enum(x: Simple) { + let _: Simple = if let Simple::A = x { + Simple::A + } else if let Simple::B = x { + Simple::B + } else if let Simple::C = x { + Simple::C } else { x }; + // Don't trigger - let _: Choice = if let Choice::A = x { - Choice::A + let _: Simple = if let Simple::A = x { + Simple::A } else if true { - Choice::B + Simple::B } else { x }; } +mod issue8542 { + #[derive(Clone, Copy)] + enum E { + VariantA(u8, u8), + VariantB(u8, bool), + } + + enum Complex { + A(u8), + B(u8, bool), + C(u8, i32, f64), + D(E, bool), + } + + fn match_test() { + let ce = Complex::B(8, false); + let aa = 0_u8; + let bb = false; + + let _: Complex = match ce { + Complex::A(a) => Complex::A(a), + Complex::B(a, b) => Complex::B(a, b), + Complex::C(a, b, c) => Complex::C(a, b, c), + Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b), + Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b), + }; + + // Don't trigger + let _: Complex = match ce { + Complex::A(_) => Complex::A(aa), + Complex::B(_, b) => Complex::B(aa, b), + Complex::C(_, b, _) => Complex::C(aa, b, 64_f64), + Complex::D(e, b) => Complex::D(e, b), + }; + + // Don't trigger + let _: Complex = match ce { + Complex::A(a) => Complex::A(a), + Complex::B(a, _) => Complex::B(a, bb), + Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64), + _ => ce, + }; + } + + fn if_let_test() { + fn do_something() {} + + // Don't trigger + let _ = if let Some(a) = Some(1) { + Some(a) + } else { + do_something(); + None + }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { + do_something(); + Some(a) + } else { + None + }; + } +} + fn main() {} diff --git a/tests/ui/needless_match.stderr b/tests/ui/needless_match.stderr index ad1525406ade..67bd84d6bbc7 100644 --- a/tests/ui/needless_match.stderr +++ b/tests/ui/needless_match.stderr @@ -1,5 +1,5 @@ error: this match expression is unnecessary - --> $DIR/needless_match.rs:17:18 + --> $DIR/needless_match.rs:16:18 | LL | let _: i32 = match i { | __________________^ @@ -13,29 +13,7 @@ LL | | }; = note: `-D clippy::needless-match` implied by `-D warnings` error: this match expression is unnecessary - --> $DIR/needless_match.rs:23:18 - | -LL | let _: i32 = match i { - | __________________^ -LL | | 0 => 0, -LL | | 1 => 1, -LL | | ref i => *i, -LL | | }; - | |_____^ help: replace it with: `i` - -error: this match expression is unnecessary - --> $DIR/needless_match.rs:28:22 - | -LL | let mut _i_mut = match i { - | ______________________^ -LL | | 0 => 0, -LL | | 1 => 1, -LL | | ref mut i => *i, -LL | | }; - | |_____^ help: replace it with: `i` - -error: this match expression is unnecessary - --> $DIR/needless_match.rs:35:19 + --> $DIR/needless_match.rs:23:19 | LL | let _: &str = match s { | ___________________^ @@ -46,19 +24,19 @@ LL | | }; | |_____^ help: replace it with: `s` error: this match expression is unnecessary - --> $DIR/needless_match.rs:43:21 + --> $DIR/needless_match.rs:32:21 | -LL | let _: Choice = match se { +LL | let _: Simple = match se { | _____________________^ -LL | | Choice::A => Choice::A, -LL | | Choice::B => Choice::B, -LL | | Choice::C => Choice::C, -LL | | Choice::D => Choice::D, +LL | | Simple::A => Simple::A, +LL | | Simple::B => Simple::B, +LL | | Simple::C => Simple::C, +LL | | Simple::D => Simple::D, LL | | }; | |_____^ help: replace it with: `se` error: this match expression is unnecessary - --> $DIR/needless_match.rs:65:26 + --> $DIR/needless_match.rs:54:26 | LL | let _: Option = match x { | __________________________^ @@ -68,7 +46,7 @@ LL | | }; | |_____^ help: replace it with: `x` error: this match expression is unnecessary - --> $DIR/needless_match.rs:81:31 + --> $DIR/needless_match.rs:70:31 | LL | let _: Result = match Ok(1) { | _______________________________^ @@ -78,7 +56,7 @@ LL | | }; | |_____^ help: replace it with: `Ok(1)` error: this match expression is unnecessary - --> $DIR/needless_match.rs:85:31 + --> $DIR/needless_match.rs:74:31 | LL | let _: Result = match func_ret_err(0_i32) { | _______________________________^ @@ -88,35 +66,48 @@ LL | | }; | |_____^ help: replace it with: `func_ret_err(0_i32)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:92:5 + --> $DIR/needless_match.rs:87:5 | LL | if let Some(a) = Some(1) { Some(a) } else { None } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:96:30 + --> $DIR/needless_match.rs:92:31 | -LL | let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` +LL | let _: Result = if let Err(e) = x { Err(e) } else { x }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:97:30 + --> $DIR/needless_match.rs:93:31 | -LL | let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` +LL | let _: Result = if let Ok(val) = x { Ok(val) } else { x }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:103:21 + --> $DIR/needless_match.rs:99:21 | -LL | let _: Choice = if let Choice::A = x { +LL | let _: Simple = if let Simple::A = x { | _____________________^ -LL | | Choice::A -LL | | } else if let Choice::B = x { -LL | | Choice::B +LL | | Simple::A +LL | | } else if let Simple::B = x { +LL | | Simple::B ... | LL | | x LL | | }; | |_____^ help: replace it with: `x` -error: aborting due to 12 previous errors +error: this match expression is unnecessary + --> $DIR/needless_match.rs:138:26 + | +LL | let _: Complex = match ce { + | __________________________^ +LL | | Complex::A(a) => Complex::A(a), +LL | | Complex::B(a, b) => Complex::B(a, b), +LL | | Complex::C(a, b, c) => Complex::C(a, b, c), +LL | | Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b), +LL | | Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b), +LL | | }; + | |_________^ help: replace it with: `ce` + +error: aborting due to 11 previous errors From 4b128624ed44223b7d0b61438b0d02fc91419e43 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Thu, 17 Mar 2022 23:06:31 +0800 Subject: [PATCH 07/82] fix #8551, add test cases, and some code improvement --- clippy_lints/src/matches/mod.rs | 2 +- clippy_lints/src/matches/needless_match.rs | 108 ++++++++++++++------- tests/ui/needless_match.fixed | 66 ++++++++++--- tests/ui/needless_match.rs | 66 ++++++++++--- tests/ui/needless_match.stderr | 14 +-- 5 files changed, 185 insertions(+), 71 deletions(-) diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index ff85623acf49..e93b494653fc 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -667,7 +667,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { overlapping_arms::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms); match_as_ref::check(cx, ex, arms, expr); - needless_match::check_match(cx, ex, arms); + needless_match::check_match(cx, ex, arms, expr); if self.infallible_destructuring_match_linted { self.infallible_destructuring_match_linted = false; diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index d9544e18b18b..2830ae1f61d5 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -1,37 +1,25 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, get_parent_expr, higher, is_else_clause, is_lang_ctor, peel_blocks_with_stmt}; +use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::{ + eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, + peel_blocks_with_stmt, +}; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind, Path, PathSegment, QPath}; +use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; use rustc_span::sym; +use rustc_typeck::hir_ty_to_ty; -pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { - // This is for avoiding collision with `match_single_binding`. - if arms.len() < 2 { - return; - } - - for arm in arms { - if let PatKind::Wild = arm.pat.kind { - let ret_expr = strip_return(arm.body); - if !eq_expr_value(cx, ex, ret_expr) { - return; - } - } else if !pat_same_as_expr(arm.pat, peel_blocks_with_stmt(arm.body)) { - return; - } - } - - if let Some(match_expr) = get_parent_expr(cx, ex) { +pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { + if arms.len() > 1 && !is_coercion_casting(cx, ex, expr) && check_all_arms(cx, ex, arms) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, NEEDLESS_MATCH, - match_expr.span, + expr.span, "this match expression is unnecessary", "replace it with", snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(), @@ -60,11 +48,8 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) /// } /// ``` pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) { - if_chain! { - if let Some(ref if_let) = higher::IfLet::hir(cx, ex); - if !is_else_clause(cx.tcx, ex); - if check_if_let(cx, if_let); - then { + if let Some(ref if_let) = higher::IfLet::hir(cx, ex) { + if !is_else_clause(cx.tcx, ex) && !is_coercion_casting(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -79,6 +64,19 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) { } } +fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool { + for arm in arms { + let arm_expr = peel_blocks_with_stmt(arm.body); + if let PatKind::Wild = arm.pat.kind { + return eq_expr_value(cx, match_expr, strip_return(arm_expr)); + } else if !pat_same_as_expr(arm.pat, arm_expr) { + return false; + } + } + + true +} + fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if let Some(if_else) = if_let.if_else { if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) { @@ -101,12 +99,12 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if let ExprKind::Path(ref qpath) = ret.kind { return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); } - } else { - return eq_expr_value(cx, if_let.let_expr, ret); + return true; } - return true; + return eq_expr_value(cx, if_let.let_expr, ret); } } + false } @@ -119,14 +117,52 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { } } +/// Manually check for coercion casting by checking if the type of the match operand or let expr +/// differs with the assigned local variable or the funtion return type. +fn is_coercion_casting(cx: &LateContext<'_>, match_expr: &Expr<'_>, expr: &Expr<'_>) -> bool { + if let Some(p_node) = get_parent_node(cx.tcx, expr.hir_id) { + match p_node { + // Compare match_expr ty with local in `let local = match match_expr {..}` + Node::Local(local) => { + let results = cx.typeck_results(); + return !same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(match_expr)); + }, + // compare match_expr ty with RetTy in `fn foo() -> RetTy` + Node::Item(..) => { + if let Some(fn_decl) = p_node.fn_decl() { + if let FnRetTy::Return(ret_ty) = fn_decl.output { + return !same_type_and_consts( + hir_ty_to_ty(cx.tcx, ret_ty), + cx.typeck_results().expr_ty(match_expr), + ); + } + } + }, + // check the parent expr for this whole block `{ match match_expr {..} }` + Node::Block(block) => { + if let Some(block_parent_expr) = get_parent_expr_for_hir(cx, block.hir_id) { + return is_coercion_casting(cx, match_expr, block_parent_expr); + } + }, + // recursively call on `if xxx {..}` etc. + Node::Expr(p_expr) => { + return is_coercion_casting(cx, match_expr, p_expr); + }, + _ => {}, + } + } + + false +} + fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { let expr = strip_return(expr); match (&pat.kind, &expr.kind) { // Example: `Some(val) => Some(val)` (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => { if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind { - return has_identical_segments(path.segments, call_path.segments) - && has_same_non_ref_symbols(tuple_params, call_params); + return same_segments(path.segments, call_path.segments) + && same_non_ref_symbols(tuple_params, call_params); } }, // Example: `val => val` @@ -145,7 +181,7 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { }, // Example: `Custom::TypeA => Custom::TypeB`, or `None => None` (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => { - return has_identical_segments(p_path.segments, e_path.segments); + return same_segments(p_path.segments, e_path.segments); }, // Example: `5 => 5` (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => { @@ -159,19 +195,21 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { false } -fn has_identical_segments(left_segs: &[PathSegment<'_>], right_segs: &[PathSegment<'_>]) -> bool { +fn same_segments(left_segs: &[PathSegment<'_>], right_segs: &[PathSegment<'_>]) -> bool { if left_segs.len() != right_segs.len() { return false; } + for i in 0..left_segs.len() { if left_segs[i].ident.name != right_segs[i].ident.name { return false; } } + true } -fn has_same_non_ref_symbols(pats: &[Pat<'_>], exprs: &[Expr<'_>]) -> bool { +fn same_non_ref_symbols(pats: &[Pat<'_>], exprs: &[Expr<'_>]) -> bool { if pats.len() != exprs.len() { return false; } diff --git a/tests/ui/needless_match.fixed b/tests/ui/needless_match.fixed index 6a518f839253..d55dd589a546 100644 --- a/tests/ui/needless_match.fixed +++ b/tests/ui/needless_match.fixed @@ -60,8 +60,26 @@ fn result_match() { }; } -fn if_let_option() -> Option { - Some(1) +fn if_let_option() { + let _ = Some(1); + + fn do_something() {} + + // Don't trigger + let _ = if let Some(a) = Some(1) { + Some(a) + } else { + do_something(); + None + }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { + do_something(); + Some(a) + } else { + None + }; } fn if_let_result() { @@ -122,25 +140,45 @@ mod issue8542 { _ => ce, }; } +} - fn if_let_test() { - fn do_something() {} +/// Lint triggered when type coercions happen. +/// Do NOT trigger on any of these. +mod issue8551 { + trait Trait {} + struct Struct; + impl Trait for Struct {} - // Don't trigger - let _ = if let Some(a) = Some(1) { - Some(a) - } else { - do_something(); - None + fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> { + match s { + Some(s) => Some(s), + None => None, + } + } + + fn lint_tests() { + let option: Option<&Struct> = None; + let _: Option<&dyn Trait> = match option { + Some(s) => Some(s), + None => None, }; - // Don't trigger - let _ = if let Some(a) = Some(1) { - do_something(); - Some(a) + let _: Option<&dyn Trait> = if true { + match option { + Some(s) => Some(s), + None => None, + } } else { None }; + + let result: Result<&Struct, i32> = Err(0); + let _: Result<&dyn Trait, i32> = match result { + Ok(s) => Ok(s), + Err(e) => Err(e), + }; + + let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None }; } } diff --git a/tests/ui/needless_match.rs b/tests/ui/needless_match.rs index ffc6a291a157..f3c7ee01a47a 100644 --- a/tests/ui/needless_match.rs +++ b/tests/ui/needless_match.rs @@ -83,8 +83,26 @@ fn result_match() { }; } -fn if_let_option() -> Option { - if let Some(a) = Some(1) { Some(a) } else { None } +fn if_let_option() { + let _ = if let Some(a) = Some(1) { Some(a) } else { None }; + + fn do_something() {} + + // Don't trigger + let _ = if let Some(a) = Some(1) { + Some(a) + } else { + do_something(); + None + }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { + do_something(); + Some(a) + } else { + None + }; } fn if_let_result() { @@ -159,25 +177,45 @@ mod issue8542 { _ => ce, }; } +} - fn if_let_test() { - fn do_something() {} +/// Lint triggered when type coercions happen. +/// Do NOT trigger on any of these. +mod issue8551 { + trait Trait {} + struct Struct; + impl Trait for Struct {} - // Don't trigger - let _ = if let Some(a) = Some(1) { - Some(a) - } else { - do_something(); - None + fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> { + match s { + Some(s) => Some(s), + None => None, + } + } + + fn lint_tests() { + let option: Option<&Struct> = None; + let _: Option<&dyn Trait> = match option { + Some(s) => Some(s), + None => None, }; - // Don't trigger - let _ = if let Some(a) = Some(1) { - do_something(); - Some(a) + let _: Option<&dyn Trait> = if true { + match option { + Some(s) => Some(s), + None => None, + } } else { None }; + + let result: Result<&Struct, i32> = Err(0); + let _: Result<&dyn Trait, i32> = match result { + Ok(s) => Ok(s), + Err(e) => Err(e), + }; + + let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None }; } } diff --git a/tests/ui/needless_match.stderr b/tests/ui/needless_match.stderr index 67bd84d6bbc7..34c5226f0605 100644 --- a/tests/ui/needless_match.stderr +++ b/tests/ui/needless_match.stderr @@ -66,25 +66,25 @@ LL | | }; | |_____^ help: replace it with: `func_ret_err(0_i32)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:87:5 + --> $DIR/needless_match.rs:87:13 | -LL | if let Some(a) = Some(1) { Some(a) } else { None } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)` +LL | let _ = if let Some(a) = Some(1) { Some(a) } else { None }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:92:31 + --> $DIR/needless_match.rs:110:31 | LL | let _: Result = if let Err(e) = x { Err(e) } else { x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:93:31 + --> $DIR/needless_match.rs:111:31 | LL | let _: Result = if let Ok(val) = x { Ok(val) } else { x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:99:21 + --> $DIR/needless_match.rs:117:21 | LL | let _: Simple = if let Simple::A = x { | _____________________^ @@ -97,7 +97,7 @@ LL | | }; | |_____^ help: replace it with: `x` error: this match expression is unnecessary - --> $DIR/needless_match.rs:138:26 + --> $DIR/needless_match.rs:156:26 | LL | let _: Complex = match ce { | __________________________^ From 94c727eccf0d8befe27aca4050cbb880a6e0343b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 20 Mar 2022 18:26:09 +0100 Subject: [PATCH 08/82] Take &mut Diagnostic in emit_diagnostic. Taking a Diagnostic by move would break the usual pattern `diag.label(..).emit()`. --- src/driver.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 8f8f1140a3da..855a6a6ef6ad 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -178,8 +178,8 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // a .span_bug or .bug call has already printed what // it wants to print. if !info.payload().is::() { - let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic"); - handler.emit_diagnostic(&d); + let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic"); + handler.emit_diagnostic(&mut d); } let version_info = rustc_tools_util::get_version_info!(); From 65a26692fd361b794f528ead645d880527ce3de0 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Wed, 23 Mar 2022 21:08:52 -0400 Subject: [PATCH 09/82] Add `crate_in_macro_def` lint --- CHANGELOG.md | 1 + clippy_lints/src/crate_in_macro_def.rs | 100 +++++++++++++++++++ clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_correctness.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/utils/conf.rs | 2 +- tests/ui/crate_in_macro_def.fixed | 29 ++++++ tests/ui/crate_in_macro_def.rs | 29 ++++++ tests/ui/crate_in_macro_def.stderr | 14 +++ tests/ui/crate_in_macro_def_allow.rs | 19 ++++ tests/ui/crate_in_macro_def_allow.stderr | 0 12 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/crate_in_macro_def.rs create mode 100644 tests/ui/crate_in_macro_def.fixed create mode 100644 tests/ui/crate_in_macro_def.rs create mode 100644 tests/ui/crate_in_macro_def.stderr create mode 100644 tests/ui/crate_in_macro_def_allow.rs create mode 100644 tests/ui/crate_in_macro_def_allow.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index dc83de665548..9cede8065ec7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3097,6 +3097,7 @@ Released 2018-09-13 [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator +[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def [`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute [`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs new file mode 100644 index 000000000000..9ac6d6d520cc --- /dev/null +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -0,0 +1,100 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::MacroDef; +use rustc_ast::node_id::NodeId; +use rustc_ast::token::{Token, TokenKind}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks for use of `crate` as opposed to `$crate` in a macro definition. + /// + /// ### Why is this bad? + /// `crate` refers to macro call's crate, whereas `$crate` refers to the macro + /// definition's crate. Rarely is the former intended. See: + /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene + /// + /// ### Example + /// ```rust + /// macro_rules! print_message { + /// () => { + /// println!("{}", crate::MESSAGE); + /// }; + /// } + /// pub const MESSAGE: &str = "Hello!"; + /// ``` + /// Use instead: + /// ```rust + /// macro_rules! print_message { + /// () => { + /// println!("{}", $crate::MESSAGE); + /// }; + /// } + /// pub const MESSAGE: &str = "Hello!"; + /// ``` + #[clippy::version = "1.61.0"] + pub CRATE_IN_MACRO_DEF, + correctness, + "using `crate` in a macro definition" +} +declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); + +impl EarlyLintPass for CrateInMacroDef { + fn check_mac_def(&mut self, cx: &EarlyContext<'_>, macro_def: &MacroDef, _: NodeId) { + let tts = macro_def.body.inner_tokens(); + if let Some(span) = contains_unhygienic_crate_reference(&tts) { + span_lint_and_sugg( + cx, + CRATE_IN_MACRO_DEF, + span, + "reference to the macro call's crate, which is rarely intended", + "if reference to the macro definition's crate is intended, use", + String::from("$crate"), + Applicability::MachineApplicable, + ); + } + } +} + +fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { + let mut prev_is_dollar = false; + let mut cursor = tts.trees(); + while let Some(curr) = cursor.next() { + if_chain! { + if !prev_is_dollar; + if let Some(span) = is_crate_keyword(&curr); + if let Some(next) = cursor.look_ahead(0); + if is_token(next, &TokenKind::ModSep); + then { + return Some(span); + } + } + if let TokenTree::Delimited(_, _, tts) = &curr { + let span = contains_unhygienic_crate_reference(tts); + if span.is_some() { + return span; + } + } + prev_is_dollar = is_token(&curr, &TokenKind::Dollar); + } + None +} + +fn is_crate_keyword(tt: &TokenTree) -> Option { + if_chain! { + if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }) = tt; + if symbol.as_str() == "crate"; + then { Some(*span) } else { None } + } +} + +fn is_token(tt: &TokenTree, kind: &TokenKind) -> bool { + if let TokenTree::Token(Token { kind: other, .. }) = tt { + kind == other + } else { + false + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 132a46626762..1fb3ca1fd9b2 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -37,6 +37,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), LintId::of(derivable_impls::DERIVABLE_IMPLS), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index df63f84463db..d5cade8fe998 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(drop_forget_ref::DROP_COPY), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 65ad64f19018..984939a74b8d 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -97,6 +97,7 @@ store.register_lints(&[ copies::IF_SAME_THEN_ELSE, copies::SAME_FUNCTIONS_IN_IF_CONDITION, copy_iterator::COPY_ITERATOR, + crate_in_macro_def::CRATE_IN_MACRO_DEF, create_dir::CREATE_DIR, dbg_macro::DBG_MACRO, default::DEFAULT_TRAIT_ACCESS, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f2a079991444..c8b57956b1b6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -190,6 +190,7 @@ mod collapsible_match; mod comparison_chain; mod copies; mod copy_iterator; +mod crate_in_macro_def; mod create_dir; mod dbg_macro; mod default; @@ -867,6 +868,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ignore_publish: cargo_ignore_publish, }) }); + store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 680b2eb1da72..b1e474f80561 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -123,7 +123,7 @@ macro_rules! define_Conf { #[cfg(feature = "internal")] pub mod metadata { - use crate::utils::internal_lints::metadata_collector::ClippyConfiguration; + use $crate::utils::internal_lints::metadata_collector::ClippyConfiguration; macro_rules! wrap_option { () => (None); diff --git a/tests/ui/crate_in_macro_def.fixed b/tests/ui/crate_in_macro_def.fixed new file mode 100644 index 000000000000..77b43f432601 --- /dev/null +++ b/tests/ui/crate_in_macro_def.fixed @@ -0,0 +1,29 @@ +// run-rustfix +#![warn(clippy::crate_in_macro_def)] + +#[macro_use] +mod hygienic { + macro_rules! print_message_hygienic { + () => { + println!("{}", $crate::hygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +#[macro_use] +mod unhygienic { + macro_rules! print_message_unhygienic { + () => { + println!("{}", $crate::unhygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +fn main() { + print_message_hygienic!(); + print_message_unhygienic!(); +} diff --git a/tests/ui/crate_in_macro_def.rs b/tests/ui/crate_in_macro_def.rs new file mode 100644 index 000000000000..d72d4c4009c3 --- /dev/null +++ b/tests/ui/crate_in_macro_def.rs @@ -0,0 +1,29 @@ +// run-rustfix +#![warn(clippy::crate_in_macro_def)] + +#[macro_use] +mod hygienic { + macro_rules! print_message_hygienic { + () => { + println!("{}", $crate::hygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +#[macro_use] +mod unhygienic { + macro_rules! print_message_unhygienic { + () => { + println!("{}", crate::unhygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +fn main() { + print_message_hygienic!(); + print_message_unhygienic!(); +} diff --git a/tests/ui/crate_in_macro_def.stderr b/tests/ui/crate_in_macro_def.stderr new file mode 100644 index 000000000000..3f5e4870ad1c --- /dev/null +++ b/tests/ui/crate_in_macro_def.stderr @@ -0,0 +1,14 @@ +error: reference to the macro call's crate, which is rarely intended + --> $DIR/crate_in_macro_def.rs:19:28 + | +LL | println!("{}", crate::unhygienic::MESSAGE); + | ^^^^^ + | + = note: `-D clippy::crate-in-macro-def` implied by `-D warnings` +help: if reference to the macro definition's crate is intended, use + | +LL | println!("{}", $crate::unhygienic::MESSAGE); + | ~~~~~~ + +error: aborting due to previous error + diff --git a/tests/ui/crate_in_macro_def_allow.rs b/tests/ui/crate_in_macro_def_allow.rs new file mode 100644 index 000000000000..d6b30fd96152 --- /dev/null +++ b/tests/ui/crate_in_macro_def_allow.rs @@ -0,0 +1,19 @@ +#![warn(clippy::crate_in_macro_def)] + +#[macro_use] +mod intentional { + // For cases where use of `crate` is intentional, applying `allow` to the macro definition + // should suppress the lint. + #[allow(clippy::crate_in_macro_def)] + macro_rules! print_message { + () => { + println!("{}", crate::CALLER_PROVIDED_MESSAGE); + }; + } +} + +fn main() { + print_message!(); +} + +pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!"; diff --git a/tests/ui/crate_in_macro_def_allow.stderr b/tests/ui/crate_in_macro_def_allow.stderr new file mode 100644 index 000000000000..e69de29bb2d1 From 6fc3850847f399628c57bfbedbe4cdd27cc646b5 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 24 Mar 2022 11:27:07 +0000 Subject: [PATCH 10/82] update clippy stderr file --- tests/ui/crashes/ice-5497.stderr | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/ui/crashes/ice-5497.stderr diff --git a/tests/ui/crashes/ice-5497.stderr b/tests/ui/crashes/ice-5497.stderr new file mode 100644 index 000000000000..e75e7dc91367 --- /dev/null +++ b/tests/ui/crashes/ice-5497.stderr @@ -0,0 +1,10 @@ +error: this operation will panic at runtime + --> $DIR/ice-5497.rs:9:22 + | +LL | const OOB: i32 = [1][1] + T::OOB; + | ^^^^^^ index out of bounds: the length is 1 but the index is 1 + | + = note: `#[deny(unconditional_panic)]` on by default + +error: aborting due to previous error + From 86872059ed143e93f04130578d8b2d1561d5f3a7 Mon Sep 17 00:00:00 2001 From: Samuel Moelius <35515885+smoelius@users.noreply.github.com> Date: Thu, 24 Mar 2022 08:57:08 -0400 Subject: [PATCH 11/82] Typo --- clippy_lints/src/crate_in_macro_def.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index 9ac6d6d520cc..de9c7ee5f204 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// Checks for use of `crate` as opposed to `$crate` in a macro definition. /// /// ### Why is this bad? - /// `crate` refers to macro call's crate, whereas `$crate` refers to the macro + /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro /// definition's crate. Rarely is the former intended. See: /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene /// From 1fa3d66e6276755746e0b6e42fb0058ca4de963f Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 24 Mar 2022 14:50:04 +0100 Subject: [PATCH 12/82] Merge commit 'd0cf3481a84e3aa68c2f185c460e282af36ebc42' into clippyup --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- CHANGELOG.md | 3 + .../src/casts/cast_enum_constructor.rs | 21 + clippy_lints/src/casts/mod.rs | 21 + clippy_lints/src/collapsible_if.rs | 4 +- clippy_lints/src/doc.rs | 15 +- clippy_lints/src/lib.register_all.rs | 4 +- clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_lints.rs | 3 + clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 1 + clippy_lints/src/lib.register_perf.rs | 1 - clippy_lints/src/lib.register_restriction.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 - clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 1 + clippy_lints/src/matches/match_same_arms.rs | 392 ++++++++++++++++-- clippy_lints/src/methods/map_flatten.rs | 128 +++--- clippy_lints/src/methods/mod.rs | 81 +++- clippy_lints/src/methods/or_then_unwrap.rs | 68 +++ clippy_lints/src/methods/unnecessary_join.rs | 41 ++ .../src/methods/unnecessary_lazy_eval.rs | 29 +- .../src/methods/unnecessary_to_owned.rs | 46 +- clippy_lints/src/ptr.rs | 2 +- .../src/single_component_path_imports.rs | 18 +- clippy_lints/src/transmute/mod.rs | 3 +- .../src/transmute/transmute_undefined_repr.rs | 98 +++-- clippy_lints/src/try_err.rs | 2 +- .../internal_lints/metadata_collector.rs | 3 +- clippy_lints/src/write.rs | 9 +- clippy_utils/src/diagnostics.rs | 86 +++- clippy_utils/src/sugg.rs | 2 +- rust-toolchain | 2 +- tests/ui/cast_enum_constructor.rs | 17 + tests/ui/cast_enum_constructor.stderr | 16 + tests/ui/map_flatten.rs | 76 ++-- tests/ui/map_flatten.stderr | 129 ++++-- ...latten.fixed => map_flatten_fixable.fixed} | 0 tests/ui/map_flatten_fixable.rs | 31 ++ tests/ui/map_flatten_fixable.stderr | 80 ++++ tests/ui/match_same_arms.stderr | 165 ++++---- tests/ui/match_same_arms2.rs | 53 +++ tests/ui/match_same_arms2.stderr | 248 +++++------ tests/ui/or_then_unwrap.fixed | 52 +++ tests/ui/or_then_unwrap.rs | 52 +++ tests/ui/or_then_unwrap.stderr | 22 + tests/ui/ptr_arg.rs | 7 + .../single_component_path_imports_macro.fixed | 20 - .../ui/single_component_path_imports_macro.rs | 4 +- ...single_component_path_imports_macro.stderr | 10 - tests/ui/transmute_undefined_repr.rs | 55 ++- tests/ui/transmute_undefined_repr.stderr | 34 +- ...transmutes_expressible_as_ptr_casts.stderr | 4 +- tests/ui/unnecessary_join.fixed | 35 ++ tests/ui/unnecessary_join.rs | 37 ++ tests/ui/unnecessary_join.stderr | 20 + tests/ui/unnecessary_lazy_eval.fixed | 8 + tests/ui/unnecessary_lazy_eval.rs | 8 + tests/ui/unnecessary_lazy_eval.stderr | 145 +++++-- .../ui/unnecessary_lazy_eval_unfixable.stderr | 12 +- tests/ui/unnecessary_to_owned.fixed | 48 +++ tests/ui/unnecessary_to_owned.rs | 48 +++ tests/ui/unnecessary_to_owned.stderr | 8 +- 63 files changed, 1973 insertions(+), 562 deletions(-) create mode 100644 clippy_lints/src/casts/cast_enum_constructor.rs create mode 100644 clippy_lints/src/methods/or_then_unwrap.rs create mode 100644 clippy_lints/src/methods/unnecessary_join.rs create mode 100644 tests/ui/cast_enum_constructor.rs create mode 100644 tests/ui/cast_enum_constructor.stderr rename tests/ui/{map_flatten.fixed => map_flatten_fixable.fixed} (100%) create mode 100644 tests/ui/map_flatten_fixable.rs create mode 100644 tests/ui/map_flatten_fixable.stderr create mode 100644 tests/ui/or_then_unwrap.fixed create mode 100644 tests/ui/or_then_unwrap.rs create mode 100644 tests/ui/or_then_unwrap.stderr delete mode 100644 tests/ui/single_component_path_imports_macro.fixed delete mode 100644 tests/ui/single_component_path_imports_macro.stderr create mode 100644 tests/ui/unnecessary_join.fixed create mode 100644 tests/ui/unnecessary_join.rs create mode 100644 tests/ui/unnecessary_join.stderr diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 68877efc9e12..b6f70a7f1830 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -18,7 +18,7 @@ body: id: reproducer attributes: label: Reproducer - description: Please provide the code and steps to repoduce the bug + description: Please provide the code and steps to reproduce the bug value: | I tried this code: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bc393d60425..88f71931d92b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3069,6 +3069,7 @@ Released 2018-09-13 [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons +[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor [`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless [`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation @@ -3366,6 +3367,7 @@ Released 2018-09-13 [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call +[`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic @@ -3506,6 +3508,7 @@ Released 2018-09-13 [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold +[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation diff --git a/clippy_lints/src/casts/cast_enum_constructor.rs b/clippy_lints/src/casts/cast_enum_constructor.rs new file mode 100644 index 000000000000..1973692e105f --- /dev/null +++ b/clippy_lints/src/casts/cast_enum_constructor.rs @@ -0,0 +1,21 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +use super::CAST_ENUM_CONSTRUCTOR; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>) { + if matches!(cast_from.kind(), ty::FnDef(..)) + && let ExprKind::Path(path) = &cast_expr.kind + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = cx.qpath_res(path, cast_expr.hir_id) + { + span_lint( + cx, + CAST_ENUM_CONSTRUCTOR, + expr.span, + "cast of an enum tuple constructor to an integer", + ); + } +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 6a0eabd089d0..be59145afa00 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -1,3 +1,4 @@ +mod cast_enum_constructor; mod cast_lossless; mod cast_possible_truncation; mod cast_possible_wrap; @@ -454,6 +455,24 @@ declare_clippy_lint! { "casting using `as` between raw pointers to slices of types with different sizes" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts from an enum tuple constructor to an integer. + /// + /// ### Why is this bad? + /// The cast is easily confused with casting a c-like enum value to an integer. + /// + /// ### Example + /// ```rust + /// enum E { X(i32) }; + /// let _ = E::X as usize; + /// ``` + #[clippy::version = "1.61.0"] + pub CAST_ENUM_CONSTRUCTOR, + suspicious, + "casts from an enum tuple constructor to an integer" +} + pub struct Casts { msrv: Option, } @@ -481,6 +500,7 @@ impl_lint_pass!(Casts => [ CHAR_LIT_AS_U8, PTR_AS_PTR, CAST_ENUM_TRUNCATION, + CAST_ENUM_CONSTRUCTOR ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -518,6 +538,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); } cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); + cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } } diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index f03f34e5a4b3..eae2723a7dac 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -42,7 +42,7 @@ declare_clippy_lint! { /// /// Should be written: /// - /// ```rust.ignore + /// ```rust,ignore /// if x && y { /// … /// } @@ -76,7 +76,7 @@ declare_clippy_lint! { /// /// Should be written: /// - /// ```rust.ignore + /// ```rust,ignore /// if x { /// … /// } else if y { diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 16173580fd46..703aa458f44e 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -637,12 +637,6 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { loop { match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => match &item.kind { - // Tests with one of these items are ignored - ItemKind::Static(..) - | ItemKind::Const(..) - | ItemKind::ExternCrate(..) - | ItemKind::ForeignMod(..) => return false, - // We found a main function ... ItemKind::Fn(box Fn { sig, body: Some(block), .. }) if item.ident.name == sym::main => { @@ -661,8 +655,13 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { return false; } }, - // Another function was found; this case is ignored too - ItemKind::Fn(..) => return false, + // Tests with one of these items are ignored + ItemKind::Static(..) + | ItemKind::Const(..) + | ItemKind::ExternCrate(..) + | ItemKind::ForeignMod(..) + // Another function was found; this case is ignored + | ItemKind::Fn(..) => return false, _ => {}, }, Ok(None) => break, diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 23bca5a0eabb..132a46626762 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -23,6 +23,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(casts::CAST_REF_TO_MUT), LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), @@ -166,7 +167,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::ITER_NTH_ZERO), LintId::of(methods::ITER_OVEREAGER_CLONED), LintId::of(methods::ITER_SKIP_NEXT), - LintId::of(methods::ITER_WITH_DRAIN), LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FIND_MAP), LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), @@ -182,6 +182,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::OPTION_FILTER_MAP), LintId::of(methods::OPTION_MAP_OR_NONE), LintId::of(methods::OR_FUN_CALL), + LintId::of(methods::OR_THEN_UNWRAP), LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(methods::SEARCH_IS_SOME), LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), @@ -290,7 +291,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmuting_null::TRANSMUTING_NULL), - LintId::of(try_err::TRY_ERR), LintId::of(types::BORROWED_BOX), LintId::of(types::BOX_COLLECTION), LintId::of(types::REDUNDANT_ALLOCATION), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 68d6c6ce5f7d..a2ce69065f94 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -47,6 +47,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), + LintId::of(methods::OR_THEN_UNWRAP), LintId::of(methods::SEARCH_IS_SOME), LintId::of(methods::SKIP_WHILE_NEXT), LintId::of(methods::UNNECESSARY_FILTER_MAP), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 1a45763a8696..21f1ef562b5a 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -70,6 +70,7 @@ store.register_lints(&[ cargo::REDUNDANT_FEATURE_NAMES, cargo::WILDCARD_DEPENDENCIES, case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + casts::CAST_ENUM_CONSTRUCTOR, casts::CAST_ENUM_TRUNCATION, casts::CAST_LOSSLESS, casts::CAST_POSSIBLE_TRUNCATION, @@ -319,6 +320,7 @@ store.register_lints(&[ methods::OPTION_FILTER_MAP, methods::OPTION_MAP_OR_NONE, methods::OR_FUN_CALL, + methods::OR_THEN_UNWRAP, methods::RESULT_MAP_OR_INTO_OPTION, methods::SEARCH_IS_SOME, methods::SHOULD_IMPLEMENT_TRAIT, @@ -332,6 +334,7 @@ store.register_lints(&[ methods::UNNECESSARY_FILTER_MAP, methods::UNNECESSARY_FIND_MAP, methods::UNNECESSARY_FOLD, + methods::UNNECESSARY_JOIN, methods::UNNECESSARY_LAZY_EVALUATIONS, methods::UNNECESSARY_TO_OWNED, methods::UNWRAP_OR_ELSE_DEFAULT, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 8d4dde42bbec..c2fc67afba51 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -13,6 +13,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(future_not_send::FUTURE_NOT_SEND), LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE), LintId::of(let_if_seq::USELESS_LET_IF_SEQ), + LintId::of(methods::ITER_WITH_DRAIN), LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(mutex_atomic::MUTEX_ATOMIC), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 00d305131810..eb6534cb8cae 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -63,6 +63,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(methods::IMPLICIT_CLONE), LintId::of(methods::INEFFICIENT_TO_STRING), LintId::of(methods::MAP_UNWRAP_OR), + LintId::of(methods::UNNECESSARY_JOIN), LintId::of(misc::FLOAT_CMP), LintId::of(misc::USED_UNDERSCORE_BINDING), LintId::of(mut_mut::MUT_MUT), diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index 6e9c0ee33a12..f2f5c988d8f9 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -16,7 +16,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::ITER_NTH), LintId::of(methods::ITER_OVEREAGER_CLONED), - LintId::of(methods::ITER_WITH_DRAIN), LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::OR_FUN_CALL), LintId::of(methods::SINGLE_CHAR_PATTERN), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 4e30fc381975..6ab139b2fb67 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -62,6 +62,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(strings::STRING_SLICE), LintId::of(strings::STRING_TO_STRING), LintId::of(strings::STR_TO_STRING), + LintId::of(try_err::TRY_ERR), LintId::of(types::RC_BUFFER), LintId::of(types::RC_MUTEX), LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 05211476ff23..dcf399cf9562 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -105,7 +105,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(try_err::TRY_ERR), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_unit::UNUSED_UNIT), LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 465baa825817..fa3a88e1368c 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -7,6 +7,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), + LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 504235d0d1ef..f2a079991444 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -23,6 +23,7 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) +extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index d11dda57e6fd..b8591fe0db0a 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,19 +1,66 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash}; -use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdSet, Pat, PatKind}; +use core::cmp::Ordering; +use core::iter; +use core::slice; +use rustc_arena::DroplessArena; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; +use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdSet, Pat, PatKind, RangeEnd}; use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::Symbol; use std::collections::hash_map::Entry; use super::MATCH_SAME_ARMS; -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { +#[allow(clippy::too_many_lines)] +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { let mut h = SpanlessHash::new(cx); h.hash_expr(arm.body); h.finish() }; + let arena = DroplessArena::default(); + let normalized_pats: Vec<_> = arms + .iter() + .map(|a| NormalizedPat::from_pat(cx, &arena, a.pat)) + .collect(); + + // The furthast forwards a pattern can move without semantic changes + let forwards_blocking_idxs: Vec<_> = normalized_pats + .iter() + .enumerate() + .map(|(i, pat)| { + normalized_pats[i + 1..] + .iter() + .enumerate() + .find_map(|(j, other)| pat.has_overlapping_values(other).then(|| i + 1 + j)) + .unwrap_or(normalized_pats.len()) + }) + .collect(); + + // The furthast backwards a pattern can move without semantic changes + let backwards_blocking_idxs: Vec<_> = normalized_pats + .iter() + .enumerate() + .map(|(i, pat)| { + normalized_pats[..i] + .iter() + .enumerate() + .rev() + .zip(forwards_blocking_idxs[..i].iter().copied().rev()) + .skip_while(|&(_, forward_block)| forward_block > i) + .find_map(|((j, other), forward_block)| { + (forward_block == i || pat.has_overlapping_values(other)).then(|| j) + }) + .unwrap_or(0) + }) + .collect(); + let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool { let min_index = usize::min(lindex, rindex); let max_index = usize::max(lindex, rindex); @@ -42,53 +89,316 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { } }; // Arms with a guard are ignored, those can’t always be merged together - // This is also the case for arms in-between each there is an arm with a guard - (min_index..=max_index).all(|index| arms[index].guard.is_none()) - && SpanlessEq::new(cx) - .expr_fallback(eq_fallback) - .eq_expr(lhs.body, rhs.body) - // these checks could be removed to allow unused bindings - && bindings_eq(lhs.pat, local_map.keys().copied().collect()) - && bindings_eq(rhs.pat, local_map.values().copied().collect()) + // If both arms overlap with an arm in between then these can't be merged either. + !(backwards_blocking_idxs[max_index] > min_index && forwards_blocking_idxs[min_index] < max_index) + && lhs.guard.is_none() + && rhs.guard.is_none() + && SpanlessEq::new(cx) + .expr_fallback(eq_fallback) + .eq_expr(lhs.body, rhs.body) + // these checks could be removed to allow unused bindings + && bindings_eq(lhs.pat, local_map.keys().copied().collect()) + && bindings_eq(rhs.pat, local_map.values().copied().collect()) }; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); - for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) { - span_lint_and_then( - cx, - MATCH_SAME_ARMS, - j.body.span, - "this `match` has identical arm bodies", - |diag| { - diag.span_note(i.body.span, "same as this"); + for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) { + if matches!(arm2.pat.kind, PatKind::Wild) { + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + arm1.span, + "this match arm has an identical body to the `_` wildcard arm", + |diag| { + diag.span_suggestion( + arm1.span, + "try removing the arm", + String::new(), + Applicability::MaybeIncorrect, + ) + .help("or try changing either arm body") + .span_note(arm2.span, "`_` wildcard arm here"); + }, + ); + } else { + let back_block = backwards_blocking_idxs[j]; + let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) { + (arm1, arm2) + } else { + (arm2, arm1) + }; - // Note: this does not use `span_suggestion` on purpose: - // there is no clean way - // to remove the other arm. Building a span and suggest to replace it to "" - // makes an even more confusing error message. Also in order not to make up a - // span for the whole pattern, the suggestion is only shown when there is only - // one pattern. The user should know about `|` if they are already using it… + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + keep_arm.span, + "this match arm has an identical body to another arm", + |diag| { + let move_pat_snip = snippet(cx, move_arm.pat.span, ""); + let keep_pat_snip = snippet(cx, keep_arm.pat.span, ""); - let lhs = snippet(cx, i.pat.span, ""); - let rhs = snippet(cx, j.pat.span, ""); + diag.span_suggestion( + keep_arm.pat.span, + "try merging the arm patterns", + format!("{} | {}", keep_pat_snip, move_pat_snip), + Applicability::MaybeIncorrect, + ) + .help("or try changing either arm body") + .span_note(move_arm.span, "other arm here"); + }, + ); + } + } +} - if let PatKind::Wild = j.pat.kind { - // if the last arm is _, then i could be integrated into _ - // note that i.pat cannot be _, because that would mean that we're - // hiding all the subsequent arms, and rust won't compile - diag.span_note( - i.body.span, - &format!( - "`{}` has the same arm body as the `_` wildcard, consider removing it", - lhs - ), - ); - } else { - diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,)) - .help("...or consider changing the match arm bodies"); +#[derive(Clone, Copy)] +enum NormalizedPat<'a> { + Wild, + Struct(Option, &'a [(Symbol, Self)]), + Tuple(Option, &'a [Self]), + Or(&'a [Self]), + Path(Option), + LitStr(Symbol), + LitBytes(&'a [u8]), + LitInt(u128), + LitBool(bool), + Range(PatRange), + /// A slice pattern. If the second value is `None`, then this matches an exact size. Otherwise + /// the first value contains everything before the `..` wildcard pattern, and the second value + /// contains everything afterwards. Note that either side, or both sides, may contain zero + /// patterns. + Slice(&'a [Self], Option<&'a [Self]>), +} + +#[derive(Clone, Copy)] +struct PatRange { + start: u128, + end: u128, + bounds: RangeEnd, +} +impl PatRange { + fn contains(&self, x: u128) -> bool { + x >= self.start + && match self.bounds { + RangeEnd::Included => x <= self.end, + RangeEnd::Excluded => x < self.end, + } + } + + fn overlaps(&self, other: &Self) -> bool { + // Note: Empty ranges are impossible, so this is correct even though it would return true if an + // empty exclusive range were to reside within an inclusive range. + (match self.bounds { + RangeEnd::Included => self.end >= other.start, + RangeEnd::Excluded => self.end > other.start, + } && match other.bounds { + RangeEnd::Included => self.start <= other.end, + RangeEnd::Excluded => self.start < other.end, + }) + } +} + +/// Iterates over the pairs of fields with matching names. +fn iter_matching_struct_fields<'a>( + left: &'a [(Symbol, NormalizedPat<'a>)], + right: &'a [(Symbol, NormalizedPat<'a>)], +) -> impl Iterator, &'a NormalizedPat<'a>)> + 'a { + struct Iter<'a>( + slice::Iter<'a, (Symbol, NormalizedPat<'a>)>, + slice::Iter<'a, (Symbol, NormalizedPat<'a>)>, + ); + impl<'a> Iterator for Iter<'a> { + type Item = (&'a NormalizedPat<'a>, &'a NormalizedPat<'a>); + fn next(&mut self) -> Option { + // Note: all the fields in each slice are sorted by symbol value. + let mut left = self.0.next()?; + let mut right = self.1.next()?; + loop { + match left.0.cmp(&right.0) { + Ordering::Equal => return Some((&left.1, &right.1)), + Ordering::Less => left = self.0.next()?, + Ordering::Greater => right = self.1.next()?, } + } + } + } + Iter(left.iter(), right.iter()) +} + +#[allow(clippy::similar_names)] +impl<'a> NormalizedPat<'a> { + #[allow(clippy::too_many_lines)] + fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { + match pat.kind { + PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, + PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Ref(pat, _) => { + Self::from_pat(cx, arena, pat) }, - ); + PatKind::Struct(ref path, fields, _) => { + let fields = + arena.alloc_from_iter(fields.iter().map(|f| (f.ident.name, Self::from_pat(cx, arena, f.pat)))); + fields.sort_by_key(|&(name, _)| name); + Self::Struct(cx.qpath_res(path, pat.hir_id).opt_def_id(), fields) + }, + PatKind::TupleStruct(ref path, pats, wild_idx) => { + let adt = match cx.typeck_results().pat_ty(pat).ty_adt_def() { + Some(x) => x, + None => return Self::Wild, + }; + let (var_id, variant) = if adt.is_enum() { + match cx.qpath_res(path, pat.hir_id).opt_def_id() { + Some(x) => (Some(x), adt.variant_with_ctor_id(x)), + None => return Self::Wild, + } + } else { + (None, adt.non_enum_variant()) + }; + let (front, back) = match wild_idx { + Some(i) => pats.split_at(i), + None => (pats, [].as_slice()), + }; + let pats = arena.alloc_from_iter( + front + .iter() + .map(|pat| Self::from_pat(cx, arena, pat)) + .chain(iter::repeat_with(|| Self::Wild).take(variant.fields.len() - pats.len())) + .chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))), + ); + Self::Tuple(var_id, pats) + }, + PatKind::Or(pats) => Self::Or(arena.alloc_from_iter(pats.iter().map(|pat| Self::from_pat(cx, arena, pat)))), + PatKind::Path(ref path) => Self::Path(cx.qpath_res(path, pat.hir_id).opt_def_id()), + PatKind::Tuple(pats, wild_idx) => { + let field_count = match cx.typeck_results().pat_ty(pat).kind() { + ty::Tuple(subs) => subs.len(), + _ => return Self::Wild, + }; + let (front, back) = match wild_idx { + Some(i) => pats.split_at(i), + None => (pats, [].as_slice()), + }; + let pats = arena.alloc_from_iter( + front + .iter() + .map(|pat| Self::from_pat(cx, arena, pat)) + .chain(iter::repeat_with(|| Self::Wild).take(field_count - pats.len())) + .chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))), + ); + Self::Tuple(None, pats) + }, + PatKind::Lit(e) => match &e.kind { + // TODO: Handle negative integers. They're currently treated as a wild match. + ExprKind::Lit(lit) => match lit.node { + LitKind::Str(sym, _) => Self::LitStr(sym), + LitKind::ByteStr(ref bytes) => Self::LitBytes(&**bytes), + LitKind::Byte(val) => Self::LitInt(val.into()), + LitKind::Char(val) => Self::LitInt(val.into()), + LitKind::Int(val, _) => Self::LitInt(val), + LitKind::Bool(val) => Self::LitBool(val), + LitKind::Float(..) | LitKind::Err(_) => Self::Wild, + }, + _ => Self::Wild, + }, + PatKind::Range(start, end, bounds) => { + // TODO: Handle negative integers. They're currently treated as a wild match. + let start = match start { + None => 0, + Some(e) => match &e.kind { + ExprKind::Lit(lit) => match lit.node { + LitKind::Int(val, _) => val, + LitKind::Char(val) => val.into(), + LitKind::Byte(val) => val.into(), + _ => return Self::Wild, + }, + _ => return Self::Wild, + }, + }; + let (end, bounds) = match end { + None => (u128::MAX, RangeEnd::Included), + Some(e) => match &e.kind { + ExprKind::Lit(lit) => match lit.node { + LitKind::Int(val, _) => (val, bounds), + LitKind::Char(val) => (val.into(), bounds), + LitKind::Byte(val) => (val.into(), bounds), + _ => return Self::Wild, + }, + _ => return Self::Wild, + }, + }; + Self::Range(PatRange { start, end, bounds }) + }, + PatKind::Slice(front, wild_pat, back) => Self::Slice( + arena.alloc_from_iter(front.iter().map(|pat| Self::from_pat(cx, arena, pat))), + wild_pat.map(|_| &*arena.alloc_from_iter(back.iter().map(|pat| Self::from_pat(cx, arena, pat)))), + ), + } + } + + /// Checks if two patterns overlap in the values they can match assuming they are for the same + /// type. + fn has_overlapping_values(&self, other: &Self) -> bool { + match (*self, *other) { + (Self::Wild, _) | (_, Self::Wild) => true, + (Self::Or(pats), ref other) | (ref other, Self::Or(pats)) => { + pats.iter().any(|pat| pat.has_overlapping_values(other)) + }, + (Self::Struct(lpath, lfields), Self::Struct(rpath, rfields)) => { + if lpath != rpath { + return false; + } + iter_matching_struct_fields(lfields, rfields).all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) + }, + (Self::Tuple(lpath, lpats), Self::Tuple(rpath, rpats)) => { + if lpath != rpath { + return false; + } + lpats + .iter() + .zip(rpats.iter()) + .all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) + }, + (Self::Path(x), Self::Path(y)) => x == y, + (Self::LitStr(x), Self::LitStr(y)) => x == y, + (Self::LitBytes(x), Self::LitBytes(y)) => x == y, + (Self::LitInt(x), Self::LitInt(y)) => x == y, + (Self::LitBool(x), Self::LitBool(y)) => x == y, + (Self::Range(ref x), Self::Range(ref y)) => x.overlaps(y), + (Self::Range(ref range), Self::LitInt(x)) | (Self::LitInt(x), Self::Range(ref range)) => range.contains(x), + (Self::Slice(lpats, None), Self::Slice(rpats, None)) => { + lpats.len() == rpats.len() && lpats.iter().zip(rpats.iter()).all(|(x, y)| x.has_overlapping_values(y)) + }, + (Self::Slice(pats, None), Self::Slice(front, Some(back))) + | (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => { + // Here `pats` is an exact size match. If the combined lengths of `front` and `back` are greater + // then the minium length required will be greater than the length of `pats`. + if pats.len() < front.len() + back.len() { + return false; + } + pats[..front.len()] + .iter() + .zip(front.iter()) + .chain(pats[pats.len() - back.len()..].iter().zip(back.iter())) + .all(|(x, y)| x.has_overlapping_values(y)) + }, + (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => lfront + .iter() + .zip(rfront.iter()) + .chain(lback.iter().rev().zip(rback.iter().rev())) + .all(|(x, y)| x.has_overlapping_values(y)), + + // Enums can mix unit variants with tuple/struct variants. These can never overlap. + (Self::Path(_), Self::Tuple(..) | Self::Struct(..)) + | (Self::Tuple(..) | Self::Struct(..), Self::Path(_)) => false, + + // Tuples can be matched like a struct. + (Self::Tuple(x, _), Self::Struct(y, _)) | (Self::Struct(x, _), Self::Tuple(y, _)) => { + // TODO: check fields here. + x == y + }, + + // TODO: Lit* with Path, Range with Path, LitBytes with Slice + _ => true, + } } } diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index e1212c31cfb0..f447940ea3b5 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,83 +1,73 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg_for_edges; use clippy_utils::is_trait_method; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::sym; +use rustc_span::{symbol::sym, Span}; use super::MAP_FLATTEN; /// lint use of `map().flatten()` for `Iterators` and 'Options' -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - recv: &'tcx hir::Expr<'_>, - map_arg: &'tcx hir::Expr<'_>, -) { - // lint if caller of `.map().flatten()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { - let map_closure_ty = cx.typeck_results().expr_ty(map_arg); - let is_map_to_option = match map_closure_ty.kind() { - ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { - let map_closure_sig = match map_closure_ty.kind() { - ty::Closure(_, substs) => substs.as_closure().sig(), - _ => map_closure_ty.fn_sig(cx.tcx), - }; - let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option) - }, - _ => false, - }; - - let method_to_use = if is_map_to_option { - // `(...).map(...)` has type `impl Iterator> - "filter_map" - } else { - // `(...).map(...)` has type `impl Iterator> - "flat_map" - }; - let func_snippet = snippet(cx, map_arg.span, ".."); - let hint = format!(".{0}({1})", method_to_use, func_snippet); - span_lint_and_sugg( +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) { + if let Some((caller_ty_name, method_to_use)) = try_get_caller_ty_name_and_method_name(cx, expr, recv, map_arg) { + let mut applicability = Applicability::MachineApplicable; + let help_msgs = [ + &format!("try replacing `map` with `{}`", method_to_use), + "and remove the `.flatten()`", + ]; + let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability); + span_lint_and_sugg_for_edges( cx, MAP_FLATTEN, - expr.span.with_lo(recv.span.hi()), - "called `map(..).flatten()` on an `Iterator`", - &format!("try using `{}` instead", method_to_use), - hint, - Applicability::MachineApplicable, + expr.span.with_lo(map_span.lo()), + &format!("called `map(..).flatten()` on `{}`", caller_ty_name), + &help_msgs, + format!("{}({})", method_to_use, closure_snippet), + applicability, ); } - - // lint if caller of `.map().flatten()` is an Option or Result - let caller_type = match cx.typeck_results().expr_ty(recv).kind() { - ty::Adt(adt, _) => { - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) { - "Option" - } else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) { - "Result" - } else { - return; - } - }, - _ => { - return; - }, - }; - - let func_snippet = snippet(cx, map_arg.span, ".."); - let hint = format!(".and_then({})", func_snippet); - let lint_info = format!("called `map(..).flatten()` on an `{}`", caller_type); - span_lint_and_sugg( - cx, - MAP_FLATTEN, - expr.span.with_lo(recv.span.hi()), - &lint_info, - "try using `and_then` instead", - hint, - Applicability::MachineApplicable, - ); +} + +fn try_get_caller_ty_name_and_method_name( + cx: &LateContext<'_>, + expr: &Expr<'_>, + caller_expr: &Expr<'_>, + map_arg: &Expr<'_>, +) -> Option<(&'static str, &'static str)> { + if is_trait_method(cx, expr, sym::Iterator) { + if is_map_to_option(cx, map_arg) { + // `(...).map(...)` has type `impl Iterator> + Some(("Iterator", "filter_map")) + } else { + // `(...).map(...)` has type `impl Iterator> + Some(("Iterator", "flat_map")) + } + } else { + if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(caller_expr).kind() { + if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) { + return Some(("Option", "and_then")); + } else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) { + return Some(("Result", "and_then")); + } + } + None + } +} + +fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool { + let map_closure_ty = cx.typeck_results().expr_ty(map_arg); + match map_closure_ty.kind() { + ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { + let map_closure_sig = match map_closure_ty.kind() { + ty::Closure(_, substs) => substs.as_closure().sig(), + _ => map_closure_ty.fn_sig(cx.tcx), + }; + let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output()); + is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option) + }, + _ => false, + } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5edd22cd14c7..9d4e1fa39940 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -45,6 +45,7 @@ mod option_as_ref_deref; mod option_map_or_none; mod option_map_unwrap_or; mod or_fun_call; +mod or_then_unwrap; mod search_is_some; mod single_char_add_str; mod single_char_insert_string; @@ -59,6 +60,7 @@ mod uninit_assumed_init; mod unnecessary_filter_map; mod unnecessary_fold; mod unnecessary_iter_cloned; +mod unnecessary_join; mod unnecessary_lazy_eval; mod unnecessary_to_owned; mod unwrap_or_else_default; @@ -778,6 +780,42 @@ declare_clippy_lint! { "using any `*or` method with a function call, which suggests `*or_else`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `.or(…).unwrap()` calls to Options and Results. + /// + /// ### Why is this bad? + /// You should use `.unwrap_or(…)` instead for clarity. + /// + /// ### Example + /// ```rust + /// # let fallback = "fallback"; + /// // Result + /// # type Error = &'static str; + /// # let result: Result<&str, Error> = Err("error"); + /// let value = result.or::(Ok(fallback)).unwrap(); + /// + /// // Option + /// # let option: Option<&str> = None; + /// let value = option.or(Some(fallback)).unwrap(); + /// ``` + /// Use instead: + /// ```rust + /// # let fallback = "fallback"; + /// // Result + /// # let result: Result<&str, &str> = Err("error"); + /// let value = result.unwrap_or(fallback); + /// + /// // Option + /// # let option: Option<&str> = None; + /// let value = option.unwrap_or(fallback); + /// ``` + #[clippy::version = "1.61.0"] + pub OR_THEN_UNWRAP, + complexity, + "checks for `.or(…).unwrap()` calls to Options and Results." +} + declare_clippy_lint! { /// ### What it does /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, @@ -1140,7 +1178,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.61.0"] pub ITER_WITH_DRAIN, - perf, + nursery, "replace `.drain(..)` with `.into_iter()`" } @@ -2012,6 +2050,35 @@ declare_clippy_lint! { "unnecessary calls to `to_owned`-like functions" } +declare_clippy_lint! { + /// ### What it does + /// Checks for use of `.collect::>().join("")` on iterators. + /// + /// ### Why is this bad? + /// `.collect::()` is more concise and usually more performant + /// + /// ### Example + /// ```rust + /// let vector = vec!["hello", "world"]; + /// let output = vector.iter().map(|item| item.to_uppercase()).collect::>().join(""); + /// println!("{}", output); + /// ``` + /// The correct use would be: + /// ```rust + /// let vector = vec!["hello", "world"]; + /// let output = vector.iter().map(|item| item.to_uppercase()).collect::(); + /// println!("{}", output); + /// ``` + /// ### Known problems + /// While `.collect::()` is more performant in most cases, there are cases where + /// using `.collect::()` over `.collect::>().join("")` + /// will prevent loop unrolling and will result in a negative performance impact. + #[clippy::version = "1.61.0"] + pub UNNECESSARY_JOIN, + pedantic, + "using `.collect::>().join(\"\")` on an iterator" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2039,6 +2106,7 @@ impl_lint_pass!(Methods => [ OPTION_MAP_OR_NONE, BIND_INSTEAD_OF_MAP, OR_FUN_CALL, + OR_THEN_UNWRAP, EXPECT_FUN_CALL, CHARS_NEXT_CMP, CHARS_LAST_CMP, @@ -2096,6 +2164,7 @@ impl_lint_pass!(Methods => [ MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN, UNNECESSARY_TO_OWNED, + UNNECESSARY_JOIN, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2377,7 +2446,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio flat_map_option::check(cx, expr, arg, span); }, (name @ "flatten", args @ []) => match method_call(recv) { - Some(("map", [recv, map_arg], _)) => map_flatten::check(cx, expr, recv, map_arg), + Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), _ => {}, }, @@ -2391,6 +2460,11 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("is_file", []) => filetype_is_file::check(cx, expr, recv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), + ("join", [join_arg]) => { + if let Some(("collect", _, span)) = method_call(recv) { + unnecessary_join::check(cx, expr, recv, join_arg, span); + } + }, ("last", args @ []) | ("skip", args @ [_]) => { if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { @@ -2474,6 +2548,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio Some(("get_mut", [recv, get_arg], _)) => { get_unwrap::check(cx, expr, recv, get_arg, true); }, + Some(("or", [recv, or_arg], or_span)) => { + or_then_unwrap::check(cx, expr, recv, or_arg, or_span); + }, _ => {}, } unwrap_used::check(cx, expr, recv); diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs new file mode 100644 index 000000000000..be5768c35450 --- /dev/null +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -0,0 +1,68 @@ +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{diagnostics::span_lint_and_sugg, is_lang_ctor}; +use rustc_errors::Applicability; +use rustc_hir::{lang_items::LangItem, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::{sym, Span}; + +use super::OR_THEN_UNWRAP; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + unwrap_expr: &Expr<'_>, + recv: &'tcx Expr<'tcx>, + or_arg: &'tcx Expr<'_>, + or_span: Span, +) { + let ty = cx.typeck_results().expr_ty(recv); // get type of x (we later check if it's Option or Result) + let title; + let or_arg_content: Span; + + if is_type_diagnostic_item(cx, ty, sym::Option) { + title = "found `.or(Some(…)).unwrap()`"; + if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::OptionSome) { + or_arg_content = content; + } else { + return; + } + } else if is_type_diagnostic_item(cx, ty, sym::Result) { + title = "found `.or(Ok(…)).unwrap()`"; + if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::ResultOk) { + or_arg_content = content; + } else { + return; + } + } else { + // Someone has implemented a struct with .or(...).unwrap() chaining, + // but it's not an Option or a Result, so bail + return; + } + + let mut applicability = Applicability::MachineApplicable; + let suggestion = format!( + "unwrap_or({})", + snippet_with_applicability(cx, or_arg_content, "..", &mut applicability) + ); + + span_lint_and_sugg( + cx, + OR_THEN_UNWRAP, + unwrap_expr.span.with_lo(or_span.lo()), + title, + "try this", + suggestion, + applicability, + ); +} + +fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option { + if let ExprKind::Call(some_expr, [arg]) = expr.kind + && let ExprKind::Path(qpath) = &some_expr.kind + && is_lang_ctor(cx, qpath, item) + { + Some(arg.span) + } else { + None + } +} diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs new file mode 100644 index 000000000000..973b8a7e6bf6 --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -0,0 +1,41 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{Ref, Slice}; +use rustc_span::{sym, Span}; + +use super::UNNECESSARY_JOIN; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + join_self_arg: &'tcx Expr<'tcx>, + join_arg: &'tcx Expr<'tcx>, + span: Span, +) { + let applicability = Applicability::MachineApplicable; + let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg); + if_chain! { + // the turbofish for collect is ::> + if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind(); + if let Slice(slice) = ref_type.kind(); + if is_type_diagnostic_item(cx, *slice, sym::String); + // the argument for join is "" + if let ExprKind::Lit(spanned) = &join_arg.kind; + if let LitKind::Str(symbol, _) = spanned.node; + if symbol.is_empty(); + then { + span_lint_and_sugg( + cx, + UNNECESSARY_JOIN, + span.with_hi(expr.span.hi()), + r#"called `.collect>().join("")` on an iterator"#, + "try using", + "collect::()".to_owned(), + applicability, + ); + } + } +} diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 1e2765263c87..2369be708129 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{eager_or_lazy, usage}; @@ -48,20 +48,19 @@ pub(super) fn check<'tcx>( Applicability::MaybeIncorrect }; - span_lint_and_sugg( - cx, - UNNECESSARY_LAZY_EVALUATIONS, - expr.span, - msg, - &format!("use `{}` instead", simplify_using), - format!( - "{0}.{1}({2})", - snippet(cx, recv.span, ".."), - simplify_using, - snippet(cx, body_expr.span, ".."), - ), - applicability, - ); + // This is a duplicate of what's happening in clippy_lints::methods::method_call, + // which isn't ideal, We want to get the method call span, + // but prefer to avoid changing the signature of the function itself. + if let hir::ExprKind::MethodCall(_, _, span) = expr.kind { + span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { + diag.span_suggestion( + span, + &format!("use `{}(..)` instead", simplify_using), + format!("{}({})", simplify_using, snippet(cx, body_expr.span, "..")), + applicability, + ); + }); + } } } } diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 7916fb8e3b45..1555758fc4ad 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -2,7 +2,9 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; +use clippy_utils::ty::{ + contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs, +}; use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind}; @@ -114,7 +116,12 @@ fn check_addr_of_expr( parent.span, &format!("unnecessary use of `{}`", method_name), "use", - format!("{:&>width$}{}", "", receiver_snippet, width = n_target_refs - n_receiver_refs), + format!( + "{:&>width$}{}", + "", + receiver_snippet, + width = n_target_refs - n_receiver_refs + ), Applicability::MachineApplicable, ); return true; @@ -182,20 +189,10 @@ fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { - if unnecessary_iter_cloned::check_for_loop_iter( - cx, - parent, - method_name, - receiver, - true, - ) { + if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } - let cloned_or_copied = if is_copy(cx, item_ty) { - "copied" - } else { - "cloned" - }; + let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" }; // The next suggestion may be incorrect because the removal of the `to_owned`-like // function could cause the iterator to hold a reference to a resource that is used // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. @@ -243,10 +240,11 @@ fn check_other_call_arg<'tcx>( if if trait_predicate.def_id() == deref_trait_id { if let [projection_predicate] = projection_predicates[..] { let normalized_ty = - cx.tcx.subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term); + cx.tcx + .subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term); implements_trait(cx, receiver_ty, deref_trait_id, &[]) - && get_associated_type(cx, receiver_ty, deref_trait_id, - "Target").map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty) + && get_associated_type(cx, receiver_ty, deref_trait_id, "Target") + .map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty) } else { false } @@ -254,7 +252,7 @@ fn check_other_call_arg<'tcx>( let composed_substs = compose_substs( cx, &trait_predicate.trait_ref.substs.iter().skip(1).collect::>()[..], - call_substs + call_substs, ); implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs) } else { @@ -264,6 +262,12 @@ fn check_other_call_arg<'tcx>( // `Target = T`. if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id; let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 }); + // If the trait is `AsRef` and the input type variable `T` occurs in the output type, then + // `T` must not be instantiated with a reference + // (https://github.com/rust-lang/rust-clippy/issues/8507). + if (n_refs == 0 && !receiver_ty.is_ref()) + || trait_predicate.def_id() != as_ref_trait_id + || !contains_ty(fn_sig.output(), input); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { span_lint_and_sugg( @@ -339,11 +343,7 @@ fn get_input_traits_and_projections<'tcx>( if let Some(arg) = substs.iter().next(); if let GenericArgKind::Type(arg_ty) = arg.unpack(); if arg_ty == input; - then { - true - } else { - false - } + then { true } else { false } } }; match predicate.kind().skip_binder() { diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 9c776437d7fe..ba1997e70e13 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -436,7 +436,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( DerefTy::Path, None, ), - Some(sym::Cow) => { + Some(sym::Cow) if mutability == Mutability::Not => { let ty_name = name.args .and_then(|args| { args.args.iter().find_map(|a| match a { diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 961cdb317e76..66b79513032f 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind}; +use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -76,14 +76,13 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { ); } - for single_use in &single_use_usages { - if !imports_reused_with_self.contains(&single_use.0) { - let can_suggest = single_use.2; + for (name, span, can_suggest) in single_use_usages { + if !imports_reused_with_self.contains(&name) { if can_suggest { span_lint_and_sugg( cx, SINGLE_COMPONENT_PATH_IMPORTS, - single_use.1, + span, "this import is redundant", "remove it entirely", String::new(), @@ -93,7 +92,7 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { span_lint_and_help( cx, SINGLE_COMPONENT_PATH_IMPORTS, - single_use.1, + span, "this import is redundant", None, "remove this import", @@ -124,14 +123,11 @@ fn track_uses( ItemKind::Use(use_tree) => { let segments = &use_tree.prefix.segments; - let should_report = - |name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited); - // keep track of `use some_module;` usages if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = use_tree.kind { let name = segments[0].ident.name; - if should_report(&name) { + if !macros.contains(&name) { single_use_usages.push((name, item.span, true)); } } @@ -146,7 +142,7 @@ fn track_uses( if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = tree.0.kind { let name = segments[0].ident.name; - if should_report(&name) { + if !macros.contains(&name) { single_use_usages.push((name, tree.0.span, false)); } } diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 23cb9d40dfdc..02569bd3a476 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -415,7 +415,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers. let const_context = in_constant(cx, e.hir_id); - let from_ty = cx.typeck_results().expr_ty(arg); + let from_ty = cx.typeck_results().expr_ty_adjusted(arg); + // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them. let to_ty = cx.typeck_results().expr_ty(e); // If useless_transmute is triggered, the other lints can be skipped. diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 6edff2240920..f5e21267a897 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -3,8 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_c_void; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Ty, TypeAndMut}; +use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy}; use rustc_span::Span; #[allow(clippy::too_many_lines)] @@ -23,7 +23,8 @@ pub(super) fn check<'tcx>( unsized_ty, to_ty: to_sub_ty, } => match reduce_ty(cx, to_sub_ty) { - ReducedTy::IntArray | ReducedTy::TypeErasure => break, + ReducedTy::TypeErasure => break, + ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break, ReducedTy::Ref(to_sub_ty) => { from_ty = unsized_ty; to_ty = to_sub_ty; @@ -48,7 +49,8 @@ pub(super) fn check<'tcx>( unsized_ty, from_ty: from_sub_ty, } => match reduce_ty(cx, from_sub_ty) { - ReducedTy::IntArray | ReducedTy::TypeErasure => break, + ReducedTy::TypeErasure => break, + ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break, ReducedTy::Ref(from_sub_ty) => { from_ty = from_sub_ty; to_ty = unsized_ty; @@ -123,9 +125,19 @@ pub(super) fn check<'tcx>( from_ty: from_sub_ty, to_ty: to_sub_ty, } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) { - (ReducedTy::IntArray | ReducedTy::TypeErasure, _) - | (_, ReducedTy::IntArray | ReducedTy::TypeErasure) => return false, + (ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false, (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => { + let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs)) + = (from_ty.kind(), to_ty.kind()) + && from_def == to_def + { + if same_except_params(from_subs, to_subs) { + return false; + } + Some(from_def.did()) + } else { + None + }; span_lint_and_then( cx, TRANSMUTE_UNDEFINED_REPR, @@ -135,21 +147,17 @@ pub(super) fn check<'tcx>( from_ty_orig, to_ty_orig ), |diag| { - if_chain! { - if let (Some(from_def), Some(to_def)) = (from_ty.ty_adt_def(), to_ty.ty_adt_def()); - if from_def == to_def; - then { - diag.note(&format!( - "two instances of the same generic type (`{}`) may have different layouts", - cx.tcx.item_name(from_def.did()) - )); - } else { - if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); - } - if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); - } + if let Some(same_adt_did) = same_adt_did { + diag.note(&format!( + "two instances of the same generic type (`{}`) may have different layouts", + cx.tcx.item_name(same_adt_did) + )); + } else { + if from_ty_orig.peel_refs() != from_ty { + diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + } + if to_ty_orig.peel_refs() != to_ty { + diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); } } }, @@ -196,10 +204,13 @@ pub(super) fn check<'tcx>( continue; }, ( - ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_), - ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_), + ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param, + ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param, ) - | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => break, + | ( + ReducedTy::UnorderedFields(_) | ReducedTy::Param, + ReducedTy::UnorderedFields(_) | ReducedTy::Param, + ) => break, }, } } @@ -263,9 +274,8 @@ enum ReducedTy<'tcx> { UnorderedFields(Ty<'tcx>), /// The type is a reference to the contained type. Ref(Ty<'tcx>), - /// The type is an array of a primitive integer type. These can be used as storage for a value - /// of another type. - IntArray, + /// The type is a generic parameter. + Param, /// Any other type. Other(Ty<'tcx>), } @@ -275,17 +285,18 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> loop { ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty); return match *ty.kind() { - ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::IntArray, + ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::TypeErasure, ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { ty = sub_ty; continue; }, ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure, ty::Tuple(args) => { - let Some(sized_ty) = args.iter().find(|&ty| !is_zero_sized_ty(cx, ty)) else { + let mut iter = args.iter(); + let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else { return ReducedTy::OrderedFields(ty); }; - if args.iter().all(|ty| is_zero_sized_ty(cx, ty)) { + if iter.all(|ty| is_zero_sized_ty(cx, ty)) { ty = sized_ty; continue; } @@ -313,9 +324,12 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => { ReducedTy::TypeErasure }, + // TODO: Check if the conversion to or from at least one of a union's fields is valid. + ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure, ty::Foreign(_) => ReducedTy::TypeErasure, ty::Ref(_, ty, _) => ReducedTy::Ref(ty), ty::RawPtr(ty) => ReducedTy::Ref(ty.ty), + ty::Param(_) => ReducedTy::Param, _ => ReducedTy::Other(ty), }; } @@ -332,3 +346,27 @@ fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } } + +fn is_size_pair(ty: Ty<'_>) -> bool { + if let ty::Tuple(tys) = *ty.kind() + && let [ty1, ty2] = &**tys + { + matches!(ty1.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) + && matches!(ty2.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) + } else { + false + } +} + +fn same_except_params(subs1: SubstsRef<'_>, subs2: SubstsRef<'_>) -> bool { + // TODO: check const parameters as well. Currently this will consider `Array<5>` the same as + // `Array<6>` + for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) { + match (ty1.kind(), ty2.kind()) { + (ty::Param(_), _) | (_, ty::Param(_)) => (), + (ty::Adt(adt1, subs1), ty::Adt(adt2, subs2)) if adt1 == adt2 && same_except_params(subs1, subs2) => (), + _ => return false, + } + } + true +} diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 80d6f3c63367..e108f7be12e6 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.38.0"] pub TRY_ERR, - style, + restriction, "return errors explicitly rather than hiding them behind a `?`" } diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index a617422bbeb0..b3fad6ce7b65 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -85,7 +85,7 @@ macro_rules! CONFIGURATION_VALUE_TEMPLATE { }; } -const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ +const LINT_EMISSION_FUNCTIONS: [&[&str]; 8] = [ &["clippy_utils", "diagnostics", "span_lint"], &["clippy_utils", "diagnostics", "span_lint_and_help"], &["clippy_utils", "diagnostics", "span_lint_and_note"], @@ -93,6 +93,7 @@ const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ &["clippy_utils", "diagnostics", "span_lint_and_sugg"], &["clippy_utils", "diagnostics", "span_lint_and_then"], &["clippy_utils", "diagnostics", "span_lint_hir_and_then"], + &["clippy_utils", "diagnostics", "span_lint_and_sugg_for_edges"], ]; const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [ ("span_suggestion", false), diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 532bd810a2e3..f3d818cc3485 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -581,14 +581,19 @@ impl Write { }; let replacement: String = match lit.token.kind { - LitKind::Integer | LitKind::Float | LitKind::Err => continue, LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => { lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}") }, LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => { lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}") }, - LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue, + LitKind::StrRaw(_) + | LitKind::Str + | LitKind::ByteStrRaw(_) + | LitKind::ByteStr + | LitKind::Integer + | LitKind::Float + | LitKind::Err => continue, LitKind::Byte | LitKind::Char => match lit.token.symbol.as_str() { "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"", "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue, diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index a927788e6a44..625a53899df9 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -8,7 +8,7 @@ //! Thank you! //! ~The `INTERNAL_METADATA_COLLECTOR` lint -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{emitter::MAX_SUGGESTION_HIGHLIGHT_LINES, Applicability, Diagnostic}; use rustc_hir::HirId; use rustc_lint::{LateContext, Lint, LintContext}; use rustc_span::source_map::{MultiSpan, Span}; @@ -213,6 +213,90 @@ pub fn span_lint_and_sugg<'a, T: LintContext>( }); } +/// Like [`span_lint_and_sugg`] with a focus on the edges. The output will either +/// emit single span or multispan suggestion depending on the number of its lines. +/// +/// If the given suggestion string has more lines than the maximum display length defined by +/// [`MAX_SUGGESTION_HIGHLIGHT_LINES`][`rustc_errors::emitter::MAX_SUGGESTION_HIGHLIGHT_LINES`], +/// this function will split the suggestion and span to showcase the change for the top and +/// bottom edge of the code. For normal suggestions, in one display window, the help message +/// will be combined with a colon. +/// +/// Multipart suggestions like the one being created here currently cannot be +/// applied by rustfix (See [rustfix#141](https://github.com/rust-lang/rustfix/issues/141)). +/// Testing rustfix with this lint emission function might require a file with +/// suggestions that can be fixed and those that can't. See +/// [clippy#8520](https://github.com/rust-lang/rust-clippy/pull/8520/files) for +/// an example and of this. +/// +/// # Example for a long suggestion +/// +/// ```text +/// error: called `map(..).flatten()` on `Option` +/// --> $DIR/map_flatten.rs:8:10 +/// | +/// LL | .map(|x| { +/// | __________^ +/// LL | | if x <= 5 { +/// LL | | Some(x) +/// LL | | } else { +/// ... | +/// LL | | }) +/// LL | | .flatten(); +/// | |__________________^ +/// | +/// = note: `-D clippy::map-flatten` implied by `-D warnings` +/// help: try replacing `map` with `and_then` +/// | +/// LL ~ .and_then(|x| { +/// LL + if x <= 5 { +/// LL + Some(x) +/// | +/// help: and remove the `.flatten()` +/// | +/// LL + None +/// LL + } +/// LL ~ }); +/// | +/// ``` +pub fn span_lint_and_sugg_for_edges( + cx: &LateContext<'_>, + lint: &'static Lint, + sp: Span, + msg: &str, + helps: &[&str; 2], + sugg: String, + applicability: Applicability, +) { + span_lint_and_then(cx, lint, sp, msg, |diag| { + let sugg_lines_count = sugg.lines().count(); + if sugg_lines_count > MAX_SUGGESTION_HIGHLIGHT_LINES { + let sm = cx.sess().source_map(); + if let (Ok(line_upper), Ok(line_bottom)) = (sm.lookup_line(sp.lo()), sm.lookup_line(sp.hi())) { + let split_idx = MAX_SUGGESTION_HIGHLIGHT_LINES / 2; + let span_upper = sm.span_until_char(sp.with_hi(line_upper.sf.lines[line_upper.line + split_idx]), '\n'); + let span_bottom = sp.with_lo(line_bottom.sf.lines[line_bottom.line - split_idx]); + + let sugg_lines_vec = sugg.lines().collect::>(); + let sugg_upper = sugg_lines_vec[..split_idx].join("\n"); + let sugg_bottom = sugg_lines_vec[sugg_lines_count - split_idx..].join("\n"); + + diag.span_suggestion(span_upper, helps[0], sugg_upper, applicability); + diag.span_suggestion(span_bottom, helps[1], sugg_bottom, applicability); + + return; + } + } + diag.span_suggestion_with_style( + sp, + &helps.join(", "), + sugg, + applicability, + rustc_errors::SuggestionStyle::ShowAlways, + ); + }); +} + /// Create a suggestion made from several `span → replacement`. /// /// Note: in the JSON format (used by `compiletest_rs`), the help message will diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 63c442e70085..1fc9979f3dd7 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -808,7 +808,7 @@ pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<' closure_arg_is_type_annotated_double_ref, next_pos: closure.span.lo(), suggestion_start: String::new(), - applicability: Applicability::MaybeIncorrect, + applicability: Applicability::MachineApplicable, }; let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id); diff --git a/rust-toolchain b/rust-toolchain index 9d5da4ed68f8..5befb856a023 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-03-14" +channel = "nightly-2022-03-24" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/ui/cast_enum_constructor.rs b/tests/ui/cast_enum_constructor.rs new file mode 100644 index 000000000000..0193454ad144 --- /dev/null +++ b/tests/ui/cast_enum_constructor.rs @@ -0,0 +1,17 @@ +#![warn(clippy::cast_enum_constructor)] +#![allow(clippy::fn_to_numeric_cast)] + +fn main() { + enum Foo { + Y(u32), + } + + enum Bar { + X, + } + + let _ = Foo::Y as usize; + let _ = Foo::Y as isize; + let _ = Foo::Y as fn(u32) -> Foo; + let _ = Bar::X as usize; +} diff --git a/tests/ui/cast_enum_constructor.stderr b/tests/ui/cast_enum_constructor.stderr new file mode 100644 index 000000000000..710909dd26fa --- /dev/null +++ b/tests/ui/cast_enum_constructor.stderr @@ -0,0 +1,16 @@ +error: cast of an enum tuple constructor to an integer + --> $DIR/cast_enum_constructor.rs:13:13 + | +LL | let _ = Foo::Y as usize; + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::cast-enum-constructor` implied by `-D warnings` + +error: cast of an enum tuple constructor to an integer + --> $DIR/cast_enum_constructor.rs:14:13 + | +LL | let _ = Foo::Y as isize; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index aa1f76e335af..7d47ee09dc1a 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -1,31 +1,55 @@ -// run-rustfix - -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::let_underscore_drop)] -#![allow(clippy::missing_docs_in_private_items)] -#![allow(clippy::map_identity)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::unnecessary_wraps)] +#![warn(clippy::map_flatten)] #![feature(result_flattening)] -fn main() { - // mapping to Option on Iterator - fn option_id(x: i8) -> Option { - Some(x) - } - let option_id_ref: fn(i8) -> Option = option_id; - let option_id_closure = |x| Some(x); - let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); - let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); - let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); - let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); +// issue #8506, multi-line +#[rustfmt::skip] +fn long_span() { + let _: Option = Some(1) + .map(|x| { + if x <= 5 { + Some(x) + } else { + None + } + }) + .flatten(); - // mapping to Iterator on Iterator - let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + let _: Result = Ok(1) + .map(|x| { + if x == 1 { + Ok(x) + } else { + Err(0) + } + }) + .flatten(); - // mapping to Option on Option - let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); - - // mapping to Result on Result - let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); + let result: Result = Ok(2); + fn do_something() { } + let _: Result = result + .map(|res| { + if res > 0 { + do_something(); + Ok(res) + } else { + Err(0) + } + }) + .flatten(); + + let _: Vec<_> = vec![5_i8; 6] + .into_iter() + .map(|some_value| { + if some_value > 3 { + Some(some_value) + } else { + None + } + }) + .flatten() + .collect(); +} + +fn main() { + long_span(); } diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index bcd2047e6faa..c9c60df838f6 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,46 +1,107 @@ -error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:18:46 +error: called `map(..).flatten()` on `Option` + --> $DIR/map_flatten.rs:8:10 | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` +LL | .map(|x| { + | __________^ +LL | | if x <= 5 { +LL | | Some(x) +LL | | } else { +... | +LL | | }) +LL | | .flatten(); + | |__________________^ | = note: `-D clippy::map-flatten` implied by `-D warnings` - -error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:19:46 +help: try replacing `map` with `and_then` | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` - -error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:20:46 +LL ~ .and_then(|x| { +LL + if x <= 5 { +LL + Some(x) | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` - -error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:21:46 +help: and remove the `.flatten()` | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` - -error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:24:46 +LL + None +LL + } +LL ~ }); | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` -error: called `map(..).flatten()` on an `Option` - --> $DIR/map_flatten.rs:27:39 +error: called `map(..).flatten()` on `Result` + --> $DIR/map_flatten.rs:18:10 | -LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` - -error: called `map(..).flatten()` on an `Result` - --> $DIR/map_flatten.rs:30:41 +LL | .map(|x| { + | __________^ +LL | | if x == 1 { +LL | | Ok(x) +LL | | } else { +... | +LL | | }) +LL | | .flatten(); + | |__________________^ + | +help: try replacing `map` with `and_then` + | +LL ~ .and_then(|x| { +LL + if x == 1 { +LL + Ok(x) + | +help: and remove the `.flatten()` + | +LL + Err(0) +LL + } +LL ~ }); | -LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: aborting due to 7 previous errors +error: called `map(..).flatten()` on `Result` + --> $DIR/map_flatten.rs:30:10 + | +LL | .map(|res| { + | __________^ +LL | | if res > 0 { +LL | | do_something(); +LL | | Ok(res) +... | +LL | | }) +LL | | .flatten(); + | |__________________^ + | +help: try replacing `map` with `and_then` + | +LL ~ .and_then(|res| { +LL + if res > 0 { +LL + do_something(); + | +help: and remove the `.flatten()` + | +LL + Err(0) +LL + } +LL ~ }); + | + +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten.rs:42:10 + | +LL | .map(|some_value| { + | __________^ +LL | | if some_value > 3 { +LL | | Some(some_value) +LL | | } else { +... | +LL | | }) +LL | | .flatten() + | |__________________^ + | +help: try replacing `map` with `filter_map` + | +LL ~ .filter_map(|some_value| { +LL + if some_value > 3 { +LL + Some(some_value) + | +help: and remove the `.flatten()` + | +LL + None +LL + } +LL + }) + | + +error: aborting due to 4 previous errors diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten_fixable.fixed similarity index 100% rename from tests/ui/map_flatten.fixed rename to tests/ui/map_flatten_fixable.fixed diff --git a/tests/ui/map_flatten_fixable.rs b/tests/ui/map_flatten_fixable.rs new file mode 100644 index 000000000000..aa1f76e335af --- /dev/null +++ b/tests/ui/map_flatten_fixable.rs @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::let_underscore_drop)] +#![allow(clippy::missing_docs_in_private_items)] +#![allow(clippy::map_identity)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::unnecessary_wraps)] +#![feature(result_flattening)] + +fn main() { + // mapping to Option on Iterator + fn option_id(x: i8) -> Option { + Some(x) + } + let option_id_ref: fn(i8) -> Option = option_id; + let option_id_closure = |x| Some(x); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + + // mapping to Iterator on Iterator + let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + + // mapping to Option on Option + let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); + + // mapping to Result on Result + let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); +} diff --git a/tests/ui/map_flatten_fixable.stderr b/tests/ui/map_flatten_fixable.stderr new file mode 100644 index 000000000000..c91c73846b69 --- /dev/null +++ b/tests/ui/map_flatten_fixable.stderr @@ -0,0 +1,80 @@ +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten_fixable.rs:18:47 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::map-flatten` implied by `-D warnings` +help: try replacing `map` with `filter_map`, and remove the `.flatten()` + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect(); + | ~~~~~~~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten_fixable.rs:19:47 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `filter_map`, and remove the `.flatten()` + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten_fixable.rs:20:47 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `filter_map`, and remove the `.flatten()` + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten_fixable.rs:21:47 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `filter_map`, and remove the `.flatten()` + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Iterator` + --> $DIR/map_flatten_fixable.rs:24:47 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `flat_map`, and remove the `.flatten()` + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); + | ~~~~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Option` + --> $DIR/map_flatten_fixable.rs:27:40 + | +LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `and_then`, and remove the `.flatten()` + | +LL | let _: Option<_> = (Some(Some(1))).and_then(|x| x); + | ~~~~~~~~~~~~~~~ + +error: called `map(..).flatten()` on `Result` + --> $DIR/map_flatten_fixable.rs:30:42 + | +LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try replacing `map` with `and_then`, and remove the `.flatten()` + | +LL | let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x); + | ~~~~~~~~~~~~~~~ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/match_same_arms.stderr b/tests/ui/match_same_arms.stderr index 7752a8a6ff2b..b6d04263b37a 100644 --- a/tests/ui/match_same_arms.stderr +++ b/tests/ui/match_same_arms.stderr @@ -1,128 +1,121 @@ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:13:14 +error: this match arm has an identical body to the `_` wildcard arm + --> $DIR/match_same_arms.rs:11:9 | -LL | _ => 0, //~ ERROR match arms have same body - | ^ +LL | Abc::A => 0, + | ^^^^^^^^^^^ help: try removing the arm | = note: `-D clippy::match-same-arms` implied by `-D warnings` -note: same as this - --> $DIR/match_same_arms.rs:11:19 + = help: or try changing either arm body +note: `_` wildcard arm here + --> $DIR/match_same_arms.rs:13:9 | -LL | Abc::A => 0, - | ^ -note: `Abc::A` has the same arm body as the `_` wildcard, consider removing it - --> $DIR/match_same_arms.rs:11:19 - | -LL | Abc::A => 0, - | ^ +LL | _ => 0, //~ ERROR match arms have same body + | ^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:18:20 - | -LL | (.., 3) => 42, //~ ERROR match arms have same body - | ^^ - | -note: same as this - --> $DIR/match_same_arms.rs:17:23 - | -LL | (1, .., 3) => 42, - | ^^ -help: consider refactoring into `(1, .., 3) | (.., 3)` +error: this match arm has an identical body to another arm --> $DIR/match_same_arms.rs:17:9 | LL | (1, .., 3) => 42, - | ^^^^^^^^^^ - = help: ...or consider changing the match arm bodies + | ----------^^^^^^ + | | + | help: try merging the arm patterns: `(1, .., 3) | (.., 3)` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms.rs:18:9 + | +LL | (.., 3) => 42, //~ ERROR match arms have same body + | ^^^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:24:15 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms.rs:24:9 | LL | 51 => 1, //~ ERROR match arms have same body - | ^ + | --^^^^^ + | | + | help: try merging the arm patterns: `51 | 42` | -note: same as this - --> $DIR/match_same_arms.rs:23:15 - | -LL | 42 => 1, - | ^ -help: consider refactoring into `42 | 51` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms.rs:23:9 | LL | 42 => 1, - | ^^ - = help: ...or consider changing the match arm bodies + | ^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:26:15 - | -LL | 52 => 2, //~ ERROR match arms have same body - | ^ - | -note: same as this - --> $DIR/match_same_arms.rs:25:15 - | -LL | 41 => 2, - | ^ -help: consider refactoring into `41 | 52` +error: this match arm has an identical body to another arm --> $DIR/match_same_arms.rs:25:9 | LL | 41 => 2, - | ^^ - = help: ...or consider changing the match arm bodies + | --^^^^^ + | | + | help: try merging the arm patterns: `41 | 52` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms.rs:26:9 + | +LL | 52 => 2, //~ ERROR match arms have same body + | ^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:32:14 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms.rs:32:9 | LL | 2 => 2, //~ ERROR 2nd matched arms have same body - | ^ + | -^^^^^ + | | + | help: try merging the arm patterns: `2 | 1` | -note: same as this - --> $DIR/match_same_arms.rs:31:14 - | -LL | 1 => 2, - | ^ -help: consider refactoring into `1 | 2` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms.rs:31:9 | LL | 1 => 2, - | ^ - = help: ...or consider changing the match arm bodies + | ^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:33:14 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms.rs:33:9 | LL | 3 => 2, //~ ERROR 3rd matched arms have same body - | ^ + | -^^^^^ + | | + | help: try merging the arm patterns: `3 | 1` | -note: same as this - --> $DIR/match_same_arms.rs:31:14 - | -LL | 1 => 2, - | ^ -help: consider refactoring into `1 | 3` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms.rs:31:9 | LL | 1 => 2, - | ^ - = help: ...or consider changing the match arm bodies + | ^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms.rs:50:55 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms.rs:32:9 + | +LL | 2 => 2, //~ ERROR 2nd matched arms have same body + | -^^^^^ + | | + | help: try merging the arm patterns: `2 | 3` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms.rs:33:9 + | +LL | 3 => 2, //~ ERROR 3rd matched arms have same body + | ^^^^^^ + +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms.rs:50:17 | LL | CommandInfo::External { name, .. } => name.to_string(), - | ^^^^^^^^^^^^^^^^ + | ----------------------------------^^^^^^^^^^^^^^^^^^^^ + | | + | help: try merging the arm patterns: `CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. }` | -note: same as this - --> $DIR/match_same_arms.rs:49:54 - | -LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), - | ^^^^^^^^^^^^^^^^ -help: consider refactoring into `CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. }` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms.rs:49:17 | LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: ...or consider changing the match arm bodies + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index 67e1d518483c..dbfeb4379d51 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -174,4 +174,57 @@ fn main() { Some(2) => 2, _ => 1, }; + + enum Foo { + X(u32), + Y(u32), + Z(u32), + } + + // Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between. + let _ = match Foo::X(0) { + Foo::X(0) => 1, + Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) => 1, + _ => 0, + }; + + // Suggest moving `Foo::Z(_)` up. + let _ = match Foo::X(0) { + Foo::X(0) => 1, + Foo::X(_) | Foo::Y(_) => 2, + Foo::Z(_) => 1, + _ => 0, + }; + + // Suggest moving `Foo::X(0)` down. + let _ = match Foo::X(0) { + Foo::X(0) => 1, + Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) => 1, + _ => 0, + }; + + // Don't lint. + let _ = match 0 { + -2 => 1, + -5..=50 => 2, + -150..=88 => 1, + _ => 3, + }; + + struct Bar { + x: u32, + y: u32, + z: u32, + } + + // Lint. + let _ = match None { + Some(Bar { x: 0, y: 5, .. }) => 1, + Some(Bar { y: 10, z: 0, .. }) => 2, + None => 50, + Some(Bar { y: 0, x: 5, .. }) => 1, + _ => 200, + }; } diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index e1ed12f93708..14a672ba2fec 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -1,175 +1,138 @@ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:20:14 +error: this match arm has an identical body to the `_` wildcard arm + --> $DIR/match_same_arms2.rs:11:9 | -LL | _ => { - | ______________^ +LL | / 42 => { +LL | | foo(); +LL | | let mut a = 42 + [23].len() as i32; +LL | | if true { +... | +LL | | a +LL | | }, + | |_________^ help: try removing the arm + | + = note: `-D clippy::match-same-arms` implied by `-D warnings` + = help: or try changing either arm body +note: `_` wildcard arm here + --> $DIR/match_same_arms2.rs:20:9 + | +LL | / _ => { LL | | //~ ERROR match arms have same body LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; ... | LL | | a -LL | | }, - | |_________^ - | - = note: `-D clippy::match-same-arms` implied by `-D warnings` -note: same as this - --> $DIR/match_same_arms2.rs:11:15 - | -LL | 42 => { - | _______________^ -LL | | foo(); -LL | | let mut a = 42 + [23].len() as i32; -LL | | if true { -... | -LL | | a -LL | | }, - | |_________^ -note: `42` has the same arm body as the `_` wildcard, consider removing it - --> $DIR/match_same_arms2.rs:11:15 - | -LL | 42 => { - | _______________^ -LL | | foo(); -LL | | let mut a = 42 + [23].len() as i32; -LL | | if true { -... | -LL | | a LL | | }, | |_________^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:34:15 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:34:9 | LL | 51 => foo(), //~ ERROR match arms have same body - | ^^^^^ + | --^^^^^^^^^ + | | + | help: try merging the arm patterns: `51 | 42` | -note: same as this - --> $DIR/match_same_arms2.rs:33:15 - | -LL | 42 => foo(), - | ^^^^^ -help: consider refactoring into `42 | 51` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms2.rs:33:9 | LL | 42 => foo(), - | ^^ - = help: ...or consider changing the match arm bodies + | ^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:40:17 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:40:9 | LL | None => 24, //~ ERROR match arms have same body - | ^^ + | ----^^^^^^ + | | + | help: try merging the arm patterns: `None | Some(_)` | -note: same as this - --> $DIR/match_same_arms2.rs:39:20 - | -LL | Some(_) => 24, - | ^^ -help: consider refactoring into `Some(_) | None` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms2.rs:39:9 | LL | Some(_) => 24, - | ^^^^^^^ - = help: ...or consider changing the match arm bodies + | ^^^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:62:28 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:62:9 | LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body - | ^^^^^^ + | ---------------^^^^^^^^^^ + | | + | help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)` | -note: same as this - --> $DIR/match_same_arms2.rs:61:28 - | -LL | (Some(a), None) => bar(a), - | ^^^^^^ -help: consider refactoring into `(Some(a), None) | (None, Some(a))` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms2.rs:61:9 | LL | (Some(a), None) => bar(a), - | ^^^^^^^^^^^^^^^ - = help: ...or consider changing the match arm bodies + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:68:26 - | -LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body - | ^^^^^^ - | -note: same as this - --> $DIR/match_same_arms2.rs:67:26 - | -LL | (Some(a), ..) => bar(a), - | ^^^^^^ -help: consider refactoring into `(Some(a), ..) | (.., Some(a))` +error: this match arm has an identical body to another arm --> $DIR/match_same_arms2.rs:67:9 | LL | (Some(a), ..) => bar(a), - | ^^^^^^^^^^^^^ - = help: ...or consider changing the match arm bodies + | -------------^^^^^^^^^^ + | | + | help: try merging the arm patterns: `(Some(a), ..) | (.., Some(a))` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:68:9 + | +LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body + | ^^^^^^^^^^^^^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:102:29 - | -LL | (Ok(_), Some(x)) => println!("ok {}", x), - | ^^^^^^^^^^^^^^^^^^^^ - | -note: same as this - --> $DIR/match_same_arms2.rs:101:29 - | -LL | (Ok(x), Some(_)) => println!("ok {}", x), - | ^^^^^^^^^^^^^^^^^^^^ -help: consider refactoring into `(Ok(x), Some(_)) | (Ok(_), Some(x))` +error: this match arm has an identical body to another arm --> $DIR/match_same_arms2.rs:101:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), - | ^^^^^^^^^^^^^^^^ - = help: ...or consider changing the match arm bodies - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + | ----------------^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: try merging the arm patterns: `(Ok(x), Some(_)) | (Ok(_), Some(x))` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:102:9 + | +LL | (Ok(_), Some(x)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:117:18 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:117:9 | LL | Ok(_) => println!("ok"), - | ^^^^^^^^^^^^^^ + | -----^^^^^^^^^^^^^^^^^^ + | | + | help: try merging the arm patterns: `Ok(_) | Ok(3)` | -note: same as this - --> $DIR/match_same_arms2.rs:116:18 - | -LL | Ok(3) => println!("ok"), - | ^^^^^^^^^^^^^^ -help: consider refactoring into `Ok(3) | Ok(_)` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms2.rs:116:9 | LL | Ok(3) => println!("ok"), - | ^^^^^ - = help: ...or consider changing the match arm bodies - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^^^^^^^^^^^^^^^^^^^^ -error: this `match` has identical arm bodies - --> $DIR/match_same_arms2.rs:144:14 +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:144:9 | LL | 1 => { - | ______________^ + | ^ help: try merging the arm patterns: `1 | 0` + | _________| + | | LL | | empty!(0); LL | | }, | |_________^ | -note: same as this - --> $DIR/match_same_arms2.rs:141:14 - | -LL | 0 => { - | ______________^ -LL | | empty!(0); -LL | | }, - | |_________^ -help: consider refactoring into `0 | 1` + = help: or try changing either arm body +note: other arm here --> $DIR/match_same_arms2.rs:141:9 | -LL | 0 => { - | ^ - = help: ...or consider changing the match arm bodies +LL | / 0 => { +LL | | empty!(0); +LL | | }, + | |_________^ error: match expression looks like `matches!` macro --> $DIR/match_same_arms2.rs:162:16 @@ -184,5 +147,50 @@ LL | | }; | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` -error: aborting due to 9 previous errors +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:194:9 + | +LL | Foo::X(0) => 1, + | ---------^^^^^ + | | + | help: try merging the arm patterns: `Foo::X(0) | Foo::Z(_)` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:196:9 + | +LL | Foo::Z(_) => 1, + | ^^^^^^^^^^^^^^ + +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:204:9 + | +LL | Foo::Z(_) => 1, + | ---------^^^^^ + | | + | help: try merging the arm patterns: `Foo::Z(_) | Foo::X(0)` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:202:9 + | +LL | Foo::X(0) => 1, + | ^^^^^^^^^^^^^^ + +error: this match arm has an identical body to another arm + --> $DIR/match_same_arms2.rs:227:9 + | +LL | Some(Bar { y: 0, x: 5, .. }) => 1, + | ----------------------------^^^^^ + | | + | help: try merging the arm patterns: `Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. })` + | + = help: or try changing either arm body +note: other arm here + --> $DIR/match_same_arms2.rs:224:9 + | +LL | Some(Bar { x: 0, y: 5, .. }) => 1, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors diff --git a/tests/ui/or_then_unwrap.fixed b/tests/ui/or_then_unwrap.fixed new file mode 100644 index 000000000000..27d4b795a5ee --- /dev/null +++ b/tests/ui/or_then_unwrap.fixed @@ -0,0 +1,52 @@ +// run-rustfix + +#![warn(clippy::or_then_unwrap)] +#![allow(clippy::map_identity)] + +struct SomeStruct {} +impl SomeStruct { + fn or(self, _: Option) -> Self { + self + } + fn unwrap(&self) {} +} + +struct SomeOtherStruct {} +impl SomeOtherStruct { + fn or(self) -> Self { + self + } + fn unwrap(&self) {} +} + +fn main() { + let option: Option<&str> = None; + let _ = option.unwrap_or("fallback"); // should trigger lint + + let result: Result<&str, &str> = Err("Error"); + let _ = result.unwrap_or("fallback"); // should trigger lint + + // as part of a method chain + let option: Option<&str> = None; + let _ = option.map(|v| v).unwrap_or("fallback").to_string().chars(); // should trigger lint + + // Not Option/Result + let instance = SomeStruct {}; + let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint + + // or takes no argument + let instance = SomeOtherStruct {}; + let _ = instance.or().unwrap(); // should not trigger lint and should not panic + + // None in or + let option: Option<&str> = None; + let _ = option.or(None).unwrap(); // should not trigger lint + + // Not Err in or + let result: Result<&str, &str> = Err("Error"); + let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint + + // other function between + let option: Option<&str> = None; + let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint +} diff --git a/tests/ui/or_then_unwrap.rs b/tests/ui/or_then_unwrap.rs new file mode 100644 index 000000000000..0dab5ae2f1c0 --- /dev/null +++ b/tests/ui/or_then_unwrap.rs @@ -0,0 +1,52 @@ +// run-rustfix + +#![warn(clippy::or_then_unwrap)] +#![allow(clippy::map_identity)] + +struct SomeStruct {} +impl SomeStruct { + fn or(self, _: Option) -> Self { + self + } + fn unwrap(&self) {} +} + +struct SomeOtherStruct {} +impl SomeOtherStruct { + fn or(self) -> Self { + self + } + fn unwrap(&self) {} +} + +fn main() { + let option: Option<&str> = None; + let _ = option.or(Some("fallback")).unwrap(); // should trigger lint + + let result: Result<&str, &str> = Err("Error"); + let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint + + // as part of a method chain + let option: Option<&str> = None; + let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint + + // Not Option/Result + let instance = SomeStruct {}; + let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint + + // or takes no argument + let instance = SomeOtherStruct {}; + let _ = instance.or().unwrap(); // should not trigger lint and should not panic + + // None in or + let option: Option<&str> = None; + let _ = option.or(None).unwrap(); // should not trigger lint + + // Not Err in or + let result: Result<&str, &str> = Err("Error"); + let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint + + // other function between + let option: Option<&str> = None; + let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint +} diff --git a/tests/ui/or_then_unwrap.stderr b/tests/ui/or_then_unwrap.stderr new file mode 100644 index 000000000000..da88154c59f7 --- /dev/null +++ b/tests/ui/or_then_unwrap.stderr @@ -0,0 +1,22 @@ +error: found `.or(Some(…)).unwrap()` + --> $DIR/or_then_unwrap.rs:24:20 + | +LL | let _ = option.or(Some("fallback")).unwrap(); // should trigger lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")` + | + = note: `-D clippy::or-then-unwrap` implied by `-D warnings` + +error: found `.or(Ok(…)).unwrap()` + --> $DIR/or_then_unwrap.rs:27:20 + | +LL | let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")` + +error: found `.or(Some(…)).unwrap()` + --> $DIR/or_then_unwrap.rs:31:31 + | +LL | let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index 97990fedd51f..03dd938a2339 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -194,3 +194,10 @@ fn two_vecs(a: &mut Vec, b: &mut Vec) { a.push(0); b.push(1); } + +// Issue #8495 +fn cow_conditional_to_mut(a: &mut Cow) { + if a.is_empty() { + a.to_mut().push_str("foo"); + } +} diff --git a/tests/ui/single_component_path_imports_macro.fixed b/tests/ui/single_component_path_imports_macro.fixed deleted file mode 100644 index e43f5d381aaa..000000000000 --- a/tests/ui/single_component_path_imports_macro.fixed +++ /dev/null @@ -1,20 +0,0 @@ -// run-rustfix -#![warn(clippy::single_component_path_imports)] -#![allow(unused_imports)] - -// #7106: use statements exporting a macro within a crate should not trigger lint - -macro_rules! m1 { - () => {}; -} -pub(crate) use m1; // ok - -macro_rules! m2 { - () => {}; -} - // fail - -fn main() { - m1!(); - m2!(); -} diff --git a/tests/ui/single_component_path_imports_macro.rs b/tests/ui/single_component_path_imports_macro.rs index 3c65ca3054c6..fda294a61546 100644 --- a/tests/ui/single_component_path_imports_macro.rs +++ b/tests/ui/single_component_path_imports_macro.rs @@ -1,8 +1,8 @@ -// run-rustfix #![warn(clippy::single_component_path_imports)] #![allow(unused_imports)] // #7106: use statements exporting a macro within a crate should not trigger lint +// #7923: normal `use` statements of macros should also not trigger the lint macro_rules! m1 { () => {}; @@ -12,7 +12,7 @@ pub(crate) use m1; // ok macro_rules! m2 { () => {}; } -use m2; // fail +use m2; // ok fn main() { m1!(); diff --git a/tests/ui/single_component_path_imports_macro.stderr b/tests/ui/single_component_path_imports_macro.stderr deleted file mode 100644 index 37d5176129ff..000000000000 --- a/tests/ui/single_component_path_imports_macro.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: this import is redundant - --> $DIR/single_component_path_imports_macro.rs:15:1 - | -LL | use m2; // fail - | ^^^^^^^ help: remove it entirely - | - = note: `-D clippy::single-component-path-imports` implied by `-D warnings` - -error: aborting due to previous error - diff --git a/tests/ui/transmute_undefined_repr.rs b/tests/ui/transmute_undefined_repr.rs index b163d6056343..b06ed4a91737 100644 --- a/tests/ui/transmute_undefined_repr.rs +++ b/tests/ui/transmute_undefined_repr.rs @@ -1,8 +1,9 @@ #![warn(clippy::transmute_undefined_repr)] #![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)] +use core::any::TypeId; use core::ffi::c_void; -use core::mem::{size_of, transmute}; +use core::mem::{size_of, transmute, MaybeUninit}; fn value() -> T { unimplemented!() @@ -87,5 +88,57 @@ fn main() { let _: *const [u8] = transmute(value::>()); // Ok let _: Box<[u8]> = transmute(value::<*mut [u8]>()); // Ok + + let _: Ty2 = transmute(value::<(Ty2,)>()); // Ok + let _: (Ty2,) = transmute(value::>()); // Ok + + let _: Ty2 = transmute(value::<(Ty2, ())>()); // Ok + let _: (Ty2, ()) = transmute(value::>()); // Ok + + let _: Ty2 = transmute(value::<((), Ty2)>()); // Ok + let _: ((), Ty2) = transmute(value::>()); // Ok + + let _: (usize, usize) = transmute(value::<&[u8]>()); // Ok + let _: &[u8] = transmute(value::<(usize, usize)>()); // Ok + + trait Trait {} + let _: (isize, isize) = transmute(value::<&dyn Trait>()); // Ok + let _: &dyn Trait = transmute(value::<(isize, isize)>()); // Ok + + let _: MaybeUninit> = transmute(value::>()); // Ok + let _: Ty2 = transmute(value::>>()); // Ok + + let _: Ty<&[u32]> = transmute::<&[u32], _>(value::<&Vec>()); // Ok + } +} + +fn _with_generics() { + if TypeId::of::() != TypeId::of::() || TypeId::of::() != TypeId::of::() { + return; + } + unsafe { + let _: &u32 = transmute(value::<&T>()); // Ok + let _: &T = transmute(value::<&u32>()); // Ok + + let _: Vec = transmute(value::>()); // Ok + let _: Vec = transmute(value::>()); // Ok + + let _: Ty<&u32> = transmute(value::<&T>()); // Ok + let _: Ty<&T> = transmute(value::<&u32>()); // Ok + + let _: Vec = transmute(value::>()); // Ok + let _: Vec = transmute(value::>()); // Ok + + let _: &Ty2 = transmute(value::<&Ty2>()); // Ok + let _: &Ty2 = transmute(value::<&Ty2>()); // Ok + + let _: Vec> = transmute(value::>>()); // Ok + let _: Vec> = transmute(value::>>()); // Ok + + let _: Vec> = transmute(value::>>()); // Err + let _: Vec> = transmute(value::>>()); // Err + + let _: *const u32 = transmute(value::>()); // Ok + let _: Box = transmute(value::<*const u32>()); // Ok } } diff --git a/tests/ui/transmute_undefined_repr.stderr b/tests/ui/transmute_undefined_repr.stderr index 42d544fc954c..28bfba6c7571 100644 --- a/tests/ui/transmute_undefined_repr.stderr +++ b/tests/ui/transmute_undefined_repr.stderr @@ -1,5 +1,5 @@ error: transmute from `Ty2` which has an undefined layout - --> $DIR/transmute_undefined_repr.rs:26:33 + --> $DIR/transmute_undefined_repr.rs:27:33 | LL | let _: Ty2C = transmute(value::>()); // Lint, Ty2 is unordered | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | let _: Ty2C = transmute(value::>()); // Lin = note: `-D clippy::transmute-undefined-repr` implied by `-D warnings` error: transmute into `Ty2` which has an undefined layout - --> $DIR/transmute_undefined_repr.rs:27:32 + --> $DIR/transmute_undefined_repr.rs:28:32 | LL | let _: Ty2 = transmute(value::>()); // Lint, Ty2 is unordered | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `Ty>` to `Ty2`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:32:32 + --> $DIR/transmute_undefined_repr.rs:33:32 | LL | let _: Ty2 = transmute(value::>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | let _: Ty2 = transmute(value::>>()); // = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `Ty2` to `Ty>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:33:36 + --> $DIR/transmute_undefined_repr.rs:34:36 | LL | let _: Ty> = transmute(value::>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | let _: Ty> = transmute(value::>()); // = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `Ty<&Ty2>` to `&Ty2`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:38:33 + --> $DIR/transmute_undefined_repr.rs:39:33 | LL | let _: &Ty2 = transmute(value::>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | let _: &Ty2 = transmute(value::>>()); / = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `&Ty2` to `Ty<&Ty2>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:39:37 + --> $DIR/transmute_undefined_repr.rs:40:37 | LL | let _: Ty<&Ty2> = transmute(value::<&Ty2>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | let _: Ty<&Ty2> = transmute(value::<&Ty2>()); / = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `std::boxed::Box>` to `&mut Ty2`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:56:45 + --> $DIR/transmute_undefined_repr.rs:57:45 | LL | let _: &'static mut Ty2 = transmute(value::>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,12 +53,28 @@ LL | let _: &'static mut Ty2 = transmute(value::` to `std::boxed::Box>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:57:37 + --> $DIR/transmute_undefined_repr.rs:58:37 | LL | let _: Box> = transmute(value::<&'static mut Ty2>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: two instances of the same generic type (`Ty2`) may have different layouts -error: aborting due to 8 previous errors +error: transmute from `std::vec::Vec>` to `std::vec::Vec>`, both of which have an undefined layout + --> $DIR/transmute_undefined_repr.rs:138:35 + | +LL | let _: Vec> = transmute(value::>>()); // Err + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: two instances of the same generic type (`Vec`) may have different layouts + +error: transmute from `std::vec::Vec>` to `std::vec::Vec>`, both of which have an undefined layout + --> $DIR/transmute_undefined_repr.rs:139:35 + | +LL | let _: Vec> = transmute(value::>>()); // Err + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: two instances of the same generic type (`Vec`) may have different layouts + +error: aborting due to 10 previous errors diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/tests/ui/transmutes_expressible_as_ptr_casts.stderr index d9b64a0ed7b0..de9418c8d1ad 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -34,13 +34,13 @@ error: transmute from a reference to a pointer LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` -error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead +error: transmute from `fn(usize) -> u8` to `*const usize` which could be expressed as a pointer cast instead --> $DIR/transmutes_expressible_as_ptr_casts.rs:48:41 | LL | let _usize_ptr_transmute = unsafe { transmute:: u8, *const usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` -error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead +error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a pointer cast instead --> $DIR/transmutes_expressible_as_ptr_casts.rs:52:49 | LL | let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; diff --git a/tests/ui/unnecessary_join.fixed b/tests/ui/unnecessary_join.fixed new file mode 100644 index 000000000000..7e12c6ae4be9 --- /dev/null +++ b/tests/ui/unnecessary_join.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::unnecessary_join)] + +fn main() { + // should be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::(); + println!("{}", output); + + // should be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::(); + println!("{}", output); + + // should not be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::>() + .join("\n"); + println!("{}", output); + + // should not be linted + let vector = vec!["hello", "world"]; + let output = vector.iter().map(|item| item.to_uppercase()).collect::(); + println!("{}", output); +} diff --git a/tests/ui/unnecessary_join.rs b/tests/ui/unnecessary_join.rs new file mode 100644 index 000000000000..0a21656a7558 --- /dev/null +++ b/tests/ui/unnecessary_join.rs @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::unnecessary_join)] + +fn main() { + // should be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::>() + .join(""); + println!("{}", output); + + // should be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::>() + .join(""); + println!("{}", output); + + // should not be linted + let vector = vec!["hello", "world"]; + let output = vector + .iter() + .map(|item| item.to_uppercase()) + .collect::>() + .join("\n"); + println!("{}", output); + + // should not be linted + let vector = vec!["hello", "world"]; + let output = vector.iter().map(|item| item.to_uppercase()).collect::(); + println!("{}", output); +} diff --git a/tests/ui/unnecessary_join.stderr b/tests/ui/unnecessary_join.stderr new file mode 100644 index 000000000000..0b14b143affd --- /dev/null +++ b/tests/ui/unnecessary_join.stderr @@ -0,0 +1,20 @@ +error: called `.collect>().join("")` on an iterator + --> $DIR/unnecessary_join.rs:11:10 + | +LL | .collect::>() + | __________^ +LL | | .join(""); + | |_________________^ help: try using: `collect::()` + | + = note: `-D clippy::unnecessary-join` implied by `-D warnings` + +error: called `.collect>().join("")` on an iterator + --> $DIR/unnecessary_join.rs:20:10 + | +LL | .collect::>() + | __________^ +LL | | .join(""); + | |_________________^ help: try using: `collect::()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index 4ba2a0a5dbcc..65fcdc43061b 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -115,6 +115,14 @@ fn main() { let _: Result = res.or(Ok(2)); let _: Result = res.or(Ok(astronomers_pi)); let _: Result = res.or(Ok(ext_str.some_field)); + let _: Result = res. + // some lines + // some lines + // some lines + // some lines + // some lines + // some lines + or(Ok(ext_str.some_field)); // neither bind_instead_of_map nor unnecessary_lazy_eval applies here let _: Result = res.and_then(|x| Err(x)); diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 466915217e42..206080ed69ad 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -115,6 +115,14 @@ fn main() { let _: Result = res.or_else(|_| Ok(2)); let _: Result = res.or_else(|_| Ok(astronomers_pi)); let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + let _: Result = res. + // some lines + // some lines + // some lines + // some lines + // some lines + // some lines + or_else(|_| Ok(ext_str.some_field)); // neither bind_instead_of_map nor unnecessary_lazy_eval applies here let _: Result = res.and_then(|x| Err(x)); diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index cc94bd5cd9e1..7e4dd7730e71 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -2,7 +2,9 @@ error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(2)` + | ^^^^-------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` | = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` @@ -10,187 +12,264 @@ error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` + | ^^^^--------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` + | ^^^^------------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `opt.and(ext_opt)` + | ^^^^--------------------- + | | + | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:40:13 | LL | let _ = opt.or_else(|| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(ext_opt)` + | ^^^^------------------- + | | + | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:41:13 | LL | let _ = opt.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(None)` + | ^^^^---------------- + | | + | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:42:13 | LL | let _ = opt.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `opt.get_or_insert(2)` + | ^^^^------------------------ + | | + | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:43:13 | LL | let _ = opt.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `opt.ok_or(2)` + | ^^^^---------------- + | | + | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:44:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))` + | ^^^^^^^^^^^^^^^^^------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:47:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Some(10).unwrap_or(2)` + | ^^^^^^^^^-------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:48:13 | LL | let _ = Some(10).and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `Some(10).and(ext_opt)` + | ^^^^^^^^^--------------------- + | | + | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:49:28 | LL | let _: Option = None.or_else(|| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(ext_opt)` + | ^^^^^------------------- + | | + | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:50:13 | LL | let _ = None.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `None.get_or_insert(2)` + | ^^^^^------------------------ + | | + | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:51:35 | LL | let _: Result = None.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `None.ok_or(2)` + | ^^^^^---------------- + | | + | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:52:28 | LL | let _: Option = None.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(None)` + | ^^^^^---------------- + | | + | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `deep.0.unwrap_or(2)` + | ^^^^^^^-------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = deep.0.and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `deep.0.and(ext_opt)` + | ^^^^^^^--------------------- + | | + | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = deep.0.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(None)` + | ^^^^^^^---------------- + | | + | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `deep.0.get_or_insert(2)` + | ^^^^^^^------------------------ + | | + | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:59:13 | LL | let _ = deep.0.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `deep.0.ok_or(2)` + | ^^^^^^^---------------- + | | + | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:79:28 | LL | let _: Option = None.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(Some(3))` + | ^^^^^------------------- + | | + | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:80:13 | LL | let _ = deep.0.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(Some(3))` + | ^^^^^^^------------------- + | | + | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:81:13 | LL | let _ = opt.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(Some(3))` + | ^^^^------------------- + | | + | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:87:13 | LL | let _ = res2.unwrap_or_else(|_| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(2)` + | ^^^^^--------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:88:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` + | ^^^^^---------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:89:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` + | ^^^^^-------------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:111:35 | LL | let _: Result = res.and_then(|_| Err(2)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(2))` + | ^^^^-------------------- + | | + | help: use `and(..)` instead: `and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:112:35 | LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(astronomers_pi))` + | ^^^^--------------------------------- + | | + | help: use `and(..)` instead: `and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:113:35 | LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(ext_str.some_field))` + | ^^^^------------------------------------- + | | + | help: use `and(..)` instead: `and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:115:35 | LL | let _: Result = res.or_else(|_| Ok(2)); - | ^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(2))` + | ^^^^------------------ + | | + | help: use `or(..)` instead: `or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:116:35 | LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(astronomers_pi))` + | ^^^^------------------------------- + | | + | help: use `or(..)` instead: `or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:117:35 | LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(ext_str.some_field))` + | ^^^^----------------------------------- + | | + | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` -error: aborting due to 32 previous errors +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:118:35 + | +LL | let _: Result = res. + | ___________________________________^ +LL | | // some lines +LL | | // some lines +LL | | // some lines +... | +LL | | // some lines +LL | | or_else(|_| Ok(ext_str.some_field)); + | |_________----------------------------------^ + | | + | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` + +error: aborting due to 33 previous errors diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/tests/ui/unnecessary_lazy_eval_unfixable.stderr index 75674b0a9d20..20acab6e844f 100644 --- a/tests/ui/unnecessary_lazy_eval_unfixable.stderr +++ b/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -2,7 +2,9 @@ error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:12:13 | LL | let _ = Ok(1).unwrap_or_else(|()| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^---------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` | = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` @@ -10,13 +12,17 @@ error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:16:13 | LL | let _ = Ok(1).unwrap_or_else(|e::E| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^------------------------ + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13 | LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^------------------------------------- + | | + | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: aborting due to 3 previous errors diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 720138db1377..38ba41ac54ec 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -212,3 +212,51 @@ fn get_file_path(_file_type: &FileType) -> Result(P); + + pub trait Abstracted {} + + impl

Abstracted for Opaque

{} + + fn build

(p: P) -> Opaque

+ where + P: AsRef, + { + Opaque(p) + } + + // Should not lint. + fn test_str(s: &str) -> Box { + Box::new(build(s.to_string())) + } + + // Should not lint. + fn test_x(x: super::X) -> Box { + Box::new(build(x)) + } + + #[derive(Clone, Copy)] + struct Y(&'static str); + + impl AsRef for Y { + fn as_ref(&self) -> &str { + self.0 + } + } + + impl ToString for Y { + fn to_string(&self) -> String { + self.0.to_string() + } + } + + // Should lint because Y is copy. + fn test_y(y: Y) -> Box { + Box::new(build(y)) + } +} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 60b2e718f5d4..15fb7ee83e3d 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -212,3 +212,51 @@ fn get_file_path(_file_type: &FileType) -> Result(P); + + pub trait Abstracted {} + + impl

Abstracted for Opaque

{} + + fn build

(p: P) -> Opaque

+ where + P: AsRef, + { + Opaque(p) + } + + // Should not lint. + fn test_str(s: &str) -> Box { + Box::new(build(s.to_string())) + } + + // Should not lint. + fn test_x(x: super::X) -> Box { + Box::new(build(x)) + } + + #[derive(Clone, Copy)] + struct Y(&'static str); + + impl AsRef for Y { + fn as_ref(&self) -> &str { + self.0 + } + } + + impl ToString for Y { + fn to_string(&self) -> String { + self.0.to_string() + } + } + + // Should lint because Y is copy. + fn test_y(y: Y) -> Box { + Box::new(build(y.to_string())) + } +} diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index 1dfc65e22e2b..c53ce32be775 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -491,5 +491,11 @@ LL - let path = match get_file_path(&t) { LL + let path = match get_file_path(t) { | -error: aborting due to 76 previous errors +error: unnecessary use of `to_string` + --> $DIR/unnecessary_to_owned.rs:260:24 + | +LL | Box::new(build(y.to_string())) + | ^^^^^^^^^^^^^ help: use: `y` + +error: aborting due to 77 previous errors From 854c3de1ff129ee331d3fad05a447f77d6c9d16b Mon Sep 17 00:00:00 2001 From: Grisha Vartanyan Date: Fri, 25 Mar 2022 15:05:27 +0100 Subject: [PATCH 13/82] Update clippy helper function types --- clippy_lints/src/regex.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index b6d04334de9e..a92097e1d24c 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for Regex { #[allow(clippy::cast_possible_truncation)] // truncation very unlikely here #[must_use] -fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u16) -> Span { +fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u8) -> Span { let offset = u32::from(offset); let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset); let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset); From 55feb4c4aa1332a155bd56cba4956fcecd19d407 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 25 Mar 2022 00:27:25 +0100 Subject: [PATCH 14/82] Changelog for Rust 1.60.0 :crab: --- CHANGELOG.md | 138 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88f71931d92b..fd288285532e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,143 @@ document. ## Unreleased / In Rust Nightly -[0eff589...master](https://github.com/rust-lang/rust-clippy/compare/0eff589...master) +[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master) ## Rust 1.59 (beta) -Current beta, release 2022-02-24 +Current beta, release 2022-04-07 + +[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b) + +### New Lints + +* [`single_char_lifetime_names`] + [#8236](https://github.com/rust-lang/rust-clippy/pull/8236) +* [`iter_overeager_cloned`] + [#8203](https://github.com/rust-lang/rust-clippy/pull/8203) +* [`transmute_undefined_repr`] + [#8398](https://github.com/rust-lang/rust-clippy/pull/8398) +* [`default_union_representation`] + [#8289](https://github.com/rust-lang/rust-clippy/pull/8289) +* [`manual_bits`] + [#8213](https://github.com/rust-lang/rust-clippy/pull/8213) +* [`borrow_as_ptr`] + [#8210](https://github.com/rust-lang/rust-clippy/pull/8210) + +### Moves and Deprecations + +* Moved [`disallowed_methods`] and [`disallowed_types`] to `style` (now warn-by-default) + [#8261](https://github.com/rust-lang/rust-clippy/pull/8261) +* Rename `ref_in_deref` to [`needless_borrow`] + [#8217](https://github.com/rust-lang/rust-clippy/pull/8217) +* Moved [`mutex_atomic`] to `nursery` (now allow-by-default) + [#8260](https://github.com/rust-lang/rust-clippy/pull/8260) + +### Enhancements + +* [`ptr_arg`]: Now takes the argument usage into account and lints for mutable references + [#8271](https://github.com/rust-lang/rust-clippy/pull/8271) +* [`unused_io_amount`]: Now supports async read and write traits + [#8179](https://github.com/rust-lang/rust-clippy/pull/8179) +* [`while_let_on_iterator`]: Improved detection to catch more cases + [#8221](https://github.com/rust-lang/rust-clippy/pull/8221) +* [`trait_duplication_in_bounds`]: Now covers trait functions with `Self` bounds + [#8252](https://github.com/rust-lang/rust-clippy/pull/8252) +* [`unwrap_used`]: Now works for `.get(i).unwrap()` and `.get_mut(i).unwrap()` + [#8372](https://github.com/rust-lang/rust-clippy/pull/8372) +* [`map_clone`]: The suggestion takes `msrv` into account + [#8280](https://github.com/rust-lang/rust-clippy/pull/8280) +* [`manual_bits`] and [`borrow_as_ptr`]: Now track the `clippy::msrv` attribute + [#8280](https://github.com/rust-lang/rust-clippy/pull/8280) +* [`disallowed_methods`]: Now works for methods on primitive types + [#8112](https://github.com/rust-lang/rust-clippy/pull/8112) +* [`not_unsafe_ptr_arg_deref`]: Now works for type aliases + [#8273](https://github.com/rust-lang/rust-clippy/pull/8273) +* [`needless_question_mark`]: Now works for async functions + [#8311](https://github.com/rust-lang/rust-clippy/pull/8311) +* [`iter_not_returning_iterator`]: Now handles type projections + [#8228](https://github.com/rust-lang/rust-clippy/pull/8228) +* [`wrong_self_convention`]: Now detects wrong `self` references in more cases + [#8208](https://github.com/rust-lang/rust-clippy/pull/8208) +* [`single_match`]: Now works for `match` statements with tuples + [#8322](https://github.com/rust-lang/rust-clippy/pull/8322) + +### False Positive Fixes + +* [`erasing_op`]: No longer triggers if the output type changes + [#8204](https://github.com/rust-lang/rust-clippy/pull/8204) +* [`if_same_then_else`]: No longer triggers for `if let` statements + [#8297](https://github.com/rust-lang/rust-clippy/pull/8297) +* [`manual_memcpy`]: No longer lints on `VecDeque` + [#8226](https://github.com/rust-lang/rust-clippy/pull/8226) +* [`trait_duplication_in_bounds`]: Now takes path segments into account + [#8315](https://github.com/rust-lang/rust-clippy/pull/8315) +* [`deref_addrof`]: No longer lints when the dereference or borrow occurs in different a context + [#8268](https://github.com/rust-lang/rust-clippy/pull/8268) +* [`type_repetition_in_bounds`]: Now checks for full equality to prevent false positives + [#8224](https://github.com/rust-lang/rust-clippy/pull/8224) +* [`ptr_arg`]: No longer lint for mutable references in traits + [#8369](https://github.com/rust-lang/rust-clippy/pull/8369) +* [`implicit_clone`]: No longer lints for double references + [#8231](https://github.com/rust-lang/rust-clippy/pull/8231) +* [`needless_lifetimes`]: No longer lints lifetimes for explicit `self` types + [#8278](https://github.com/rust-lang/rust-clippy/pull/8278) +* [`op_ref`]: No longer lints in `BinOp` impl if that can cause recursion + [#8298](https://github.com/rust-lang/rust-clippy/pull/8298) +* [`enum_variant_names`]: No longer triggers for empty variant names + [#8329](https://github.com/rust-lang/rust-clippy/pull/8329) +* [`redundant_closure`]: No longer lints for `Arc` or `Rc` + [#8193](https://github.com/rust-lang/rust-clippy/pull/8193) +* [`iter_not_returning_iterator`]: No longer lints on trait implementations but therefore on trait definitions + [#8228](https://github.com/rust-lang/rust-clippy/pull/8228) +* [`single_match`]: No longer lints on exhaustive enum patterns without a wildcard + [#8322](https://github.com/rust-lang/rust-clippy/pull/8322) +* [`manual_swap`]: No longer lints on cases that involve automatic dereferences + [#8220](https://github.com/rust-lang/rust-clippy/pull/8220) +* [`useless_format`]: Now works for implicit named arguments + [#8295](https://github.com/rust-lang/rust-clippy/pull/8295) + +### Suggestion Fixes/Improvements + +* [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls + [#8217](https://github.com/rust-lang/rust-clippy/pull/8217) +* [`chars_next_cmp`]: Correctly excapes the suggestion + [#8376](https://github.com/rust-lang/rust-clippy/pull/8376) +* [`explicit_write`]: Add suggestions for `write!`s with format arguments + [#8365](https://github.com/rust-lang/rust-clippy/pull/8365) +* [`manual_memcpy`]: Suggests `copy_from_slice` when applicable + [#8226](https://github.com/rust-lang/rust-clippy/pull/8226) +* [`or_fun_call`]: Improved suggestion display for long arguments + [#8292](https://github.com/rust-lang/rust-clippy/pull/8292) +* [`unnecessary_cast`]: Now correctly includes the sign + [#8350](https://github.com/rust-lang/rust-clippy/pull/8350) +* [`cmp_owned`]: No longer flips the comparison order + [#8299](https://github.com/rust-lang/rust-clippy/pull/8299) +* [`explicit_counter_loop`]: Now correctly suggests `iter()` on references + [#8382](https://github.com/rust-lang/rust-clippy/pull/8382) + +### ICE Fixes + +* [`manual_split_once`] + [#8250](https://github.com/rust-lang/rust-clippy/pull/8250) + +### Documentation Improvements + +* [`map_flatten`]: Add documentation for the `Option` type + [#8354](https://github.com/rust-lang/rust-clippy/pull/8354) +* Document that Clippy's driver might use a different code generation than rustc + [#8037](https://github.com/rust-lang/rust-clippy/pull/8037) +* Clippy's lint list will now automatically focus the search box + [#8343](https://github.com/rust-lang/rust-clippy/pull/8343) + +### Others + +* Clippy now warns if we find multiple Clippy config files exist + [#8326](https://github.com/rust-lang/rust-clippy/pull/8326) + +## Rust 1.59 + +Current stable, release 2022-02-24 [e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589) @@ -174,7 +306,7 @@ Current beta, release 2022-02-24 ## Rust 1.58 -Current stable, released 2022-01-13 +Released 2022-01-13 [00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011) From 610db04222445e2e25c911dda9a0ae7e30df3cd9 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 25 Mar 2022 20:39:03 +0000 Subject: [PATCH 15/82] Provide suggestion context in map_unit_fn --- clippy_lints/src/map_unit_fn.rs | 23 ++++++++++------------ tests/ui/option_map_unit_fn_fixable.fixed | 5 ++++- tests/ui/option_map_unit_fn_fixable.rs | 5 ++++- tests/ui/option_map_unit_fn_fixable.stderr | 12 +++++++++-- tests/ui/result_map_unit_fn_fixable.fixed | 2 ++ tests/ui/result_map_unit_fn_fixable.rs | 2 ++ tests/ui/result_map_unit_fn_fixable.stderr | 10 +++++++++- 7 files changed, 41 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 0f6ac4784324..f552d5c1afab 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{iter_input_pats, method_chain_args}; use if_chain::if_chain; @@ -217,36 +217,33 @@ fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr let fn_arg = &map_args[1]; if is_unit_function(cx, fn_arg) { + let mut applicability = Applicability::MachineApplicable; let msg = suggestion_msg("function", map_type); let suggestion = format!( "if let {0}({binding}) = {1} {{ {2}({binding}) }}", variant, - snippet(cx, var_arg.span, "_"), - snippet(cx, fn_arg.span, "_"), + snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), + snippet_with_applicability(cx, fn_arg.span, "_", &mut applicability), binding = let_binding_name(cx, var_arg) ); span_lint_and_then(cx, lint, expr.span, &msg, |diag| { - diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::MachineApplicable); + diag.span_suggestion(stmt.span, "try this", suggestion, applicability); }); } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) { let msg = suggestion_msg("closure", map_type); span_lint_and_then(cx, lint, expr.span, &msg, |diag| { if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) { + let mut applicability = Applicability::MachineApplicable; let suggestion = format!( "if let {0}({1}) = {2} {{ {3} }}", variant, - snippet(cx, binding.pat.span, "_"), - snippet(cx, var_arg.span, "_"), - snippet(cx, reduced_expr_span, "_") - ); - diag.span_suggestion( - stmt.span, - "try this", - suggestion, - Applicability::MachineApplicable, // snippet + snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability), + snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), + snippet_with_context(cx, reduced_expr_span, var_arg.span.ctxt(), "_", &mut applicability).0, ); + diag.span_suggestion(stmt.span, "try this", suggestion, applicability); } else { let suggestion = format!( "if let {0}({1}) = {2} {{ ... }}", diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 7d29445e66c8..1290bd8efebd 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -80,6 +80,9 @@ fn option_map_unit_fn() { if let Some(ref value) = x.field { do_nothing(value + captured) } - if let Some(a) = option() { do_nothing(a) }} + if let Some(a) = option() { do_nothing(a) } + + if let Some(value) = option() { println!("{:?}", value) } +} fn main() {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index b6f834f686f9..f3e5b62c65b7 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -80,6 +80,9 @@ fn option_map_unit_fn() { x.field.map(|ref value| { do_nothing(value + captured) }); - option().map(do_nothing);} + option().map(do_nothing); + + option().map(|value| println!("{:?}", value)); +} fn main() {} diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index 8abdbcafb6e9..ab2a294a060f 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -139,10 +139,18 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:83:5 | -LL | option().map(do_nothing);} +LL | option().map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^^- | | | help: try this: `if let Some(a) = option() { do_nothing(a) }` -error: aborting due to 18 previous errors +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> $DIR/option_map_unit_fn_fixable.rs:85:5 + | +LL | option().map(|value| println!("{:?}", value)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = option() { println!("{:?}", value) }` + +error: aborting due to 19 previous errors diff --git a/tests/ui/result_map_unit_fn_fixable.fixed b/tests/ui/result_map_unit_fn_fixable.fixed index 631042c616bc..14c331f67e73 100644 --- a/tests/ui/result_map_unit_fn_fixable.fixed +++ b/tests/ui/result_map_unit_fn_fixable.fixed @@ -75,6 +75,8 @@ fn result_map_unit_fn() { if let Ok(ref value) = x.field { do_nothing(value + captured) } + + if let Ok(value) = x.field { println!("{:?}", value) } } fn main() {} diff --git a/tests/ui/result_map_unit_fn_fixable.rs b/tests/ui/result_map_unit_fn_fixable.rs index 679eb2326268..8b0fca9ece1a 100644 --- a/tests/ui/result_map_unit_fn_fixable.rs +++ b/tests/ui/result_map_unit_fn_fixable.rs @@ -75,6 +75,8 @@ fn result_map_unit_fn() { x.field.map(|ref value| { do_nothing(value + captured) }); + + x.field.map(|value| println!("{:?}", value)); } fn main() {} diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr index 4f3a8c6b7923..782febd52644 100644 --- a/tests/ui/result_map_unit_fn_fixable.stderr +++ b/tests/ui/result_map_unit_fn_fixable.stderr @@ -136,5 +136,13 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | | | help: try this: `if let Ok(ref value) = x.field { do_nothing(value + captured) }` -error: aborting due to 17 previous errors +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` + --> $DIR/result_map_unit_fn_fixable.rs:79:5 + | +LL | x.field.map(|value| println!("{:?}", value)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { println!("{:?}", value) }` + +error: aborting due to 18 previous errors From bca047dceabce1c5a3c481ea01a9840bcad0e5ce Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Fri, 25 Mar 2022 16:39:15 -0600 Subject: [PATCH 16/82] specify serde version compatible with codebase --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 123af23881b6..b9ba9fe73500 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ derive-new = "0.5" if_chain = "1.0" itertools = "0.10.1" quote = "1.0" -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0.125", features = ["derive"] } syn = { version = "1.0", features = ["full"] } futures = "0.3" parking_lot = "0.11.2" From 21eae8ceb6ff58b592b00cab58cda62a96aad51a Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Fri, 25 Mar 2022 23:05:38 -0600 Subject: [PATCH 17/82] fix `indexing_slicing` with const - should not fire if indexing with a constant block - should not fire if indexing within a constant context (const statement or const block) --- clippy_lints/src/indexing_slicing.rs | 8 ++++++++ tests/ui/indexing_slicing_index.rs | 24 +++++++++++++++++++---- tests/ui/indexing_slicing_index.stderr | 27 +++++++++++++++++++------- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 9ead4bb27a58..4ba7477add82 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -96,6 +96,10 @@ declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING] impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + return; + } + if let ExprKind::Index(array, index) = &expr.kind { let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::Range::hir(index) { @@ -151,6 +155,10 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { } else { // Catchall non-range index, i.e., [n] or [n << m] if let ty::Array(..) = ty.kind() { + // Index is a const block. + if let ExprKind::ConstBlock(..) = index.kind { + return; + } // Index is a constant uint. if let Some(..) = constant(cx, cx.typeck_results(), index) { // Let rustc's `const_err` lint handle constant `usize` indexing on arrays. diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index ca8ca53c80c3..45a430edcb58 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -1,18 +1,34 @@ +#![feature(inline_const)] #![warn(clippy::indexing_slicing)] // We also check the out_of_bounds_indexing lint here, because it lints similar things and // we want to avoid false positives. #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(const_err, clippy::no_effect, clippy::unnecessary_operation)] + +const ARR: [i32; 2] = [1, 2]; +const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr. +const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + +const fn idx() -> usize { + 1 +} +const fn idx4() -> usize { + 4 +} fn main() { let x = [1, 2, 3, 4]; let index: usize = 1; x[index]; - x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. - x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + x[4]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + x[1 << 3]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. x[0]; // Ok, should not produce stderr. x[3]; // Ok, should not produce stderr. + x[const { idx() }]; // Ok, should not produce stderr. + x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + const { &ARR[idx()] }; // Ok, should not produce stderr. + const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. let y = &x; y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 @@ -25,7 +41,7 @@ fn main() { const N: usize = 15; // Out of bounds const M: usize = 3; // In bounds - x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + x[N]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. x[M]; // Ok, should not produce stderr. v[N]; v[M]; diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 76ecec334840..83a36f407d5d 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -1,5 +1,17 @@ +error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed + --> $DIR/indexing_slicing_index.rs:31:14 + | +LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +error[E0080]: erroneous constant used + --> $DIR/indexing_slicing_index.rs:31:5 + | +LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + error: indexing may panic - --> $DIR/indexing_slicing_index.rs:10:5 + --> $DIR/indexing_slicing_index.rs:22:5 | LL | x[index]; | ^^^^^^^^ @@ -8,7 +20,7 @@ LL | x[index]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:22:5 + --> $DIR/indexing_slicing_index.rs:38:5 | LL | v[0]; | ^^^^ @@ -16,7 +28,7 @@ LL | v[0]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:23:5 + --> $DIR/indexing_slicing_index.rs:39:5 | LL | v[10]; | ^^^^^ @@ -24,7 +36,7 @@ LL | v[10]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:24:5 + --> $DIR/indexing_slicing_index.rs:40:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -32,7 +44,7 @@ LL | v[1 << 3]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:30:5 + --> $DIR/indexing_slicing_index.rs:46:5 | LL | v[N]; | ^^^^ @@ -40,12 +52,13 @@ LL | v[N]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:31:5 + --> $DIR/indexing_slicing_index.rs:47:5 | LL | v[M]; | ^^^^ | = help: consider using `.get(n)` or `.get_mut(n)` instead -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors +For more information about this error, try `rustc --explain E0080`. From 96f4e1c6306362fe917623110a47d06cec8aaaea Mon Sep 17 00:00:00 2001 From: Jendrik Date: Sat, 26 Mar 2022 15:37:48 +0100 Subject: [PATCH 18/82] add #[must_use] to functions of slice and its iterators. --- tests/ui/bytes_nth.fixed | 4 ++-- tests/ui/bytes_nth.rs | 4 ++-- tests/ui/iter_next_slice.fixed | 8 ++++---- tests/ui/iter_next_slice.rs | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/ui/bytes_nth.fixed b/tests/ui/bytes_nth.fixed index 46b7833f4280..b1fb2e16bd58 100644 --- a/tests/ui/bytes_nth.fixed +++ b/tests/ui/bytes_nth.fixed @@ -5,7 +5,7 @@ fn main() { let s = String::from("String"); - s.as_bytes().get(3); + let _ = s.as_bytes().get(3); let _ = &s.as_bytes().get(3); - s[..].as_bytes().get(3); + let _ = s[..].as_bytes().get(3); } diff --git a/tests/ui/bytes_nth.rs b/tests/ui/bytes_nth.rs index c5e983d4d4e0..034c54e6a420 100644 --- a/tests/ui/bytes_nth.rs +++ b/tests/ui/bytes_nth.rs @@ -5,7 +5,7 @@ fn main() { let s = String::from("String"); - s.bytes().nth(3); + let _ = s.bytes().nth(3); let _ = &s.bytes().nth(3); - s[..].bytes().nth(3); + let _ = s[..].bytes().nth(3); } diff --git a/tests/ui/iter_next_slice.fixed b/tests/ui/iter_next_slice.fixed index 79c1db87ac3c..11ffc8edb149 100644 --- a/tests/ui/iter_next_slice.fixed +++ b/tests/ui/iter_next_slice.fixed @@ -6,16 +6,16 @@ fn main() { let s = [1, 2, 3]; let v = vec![1, 2, 3]; - s.get(0); + let _ = s.get(0); // Should be replaced by s.get(0) - s.get(2); + let _ = s.get(2); // Should be replaced by s.get(2) - v.get(5); + let _ = v.get(5); // Should be replaced by v.get(5) - v.get(0); + let _ = v.get(0); // Should be replaced by v.get(0) let o = Some(5); diff --git a/tests/ui/iter_next_slice.rs b/tests/ui/iter_next_slice.rs index ef9a55f3d997..e0d3aabd54ac 100644 --- a/tests/ui/iter_next_slice.rs +++ b/tests/ui/iter_next_slice.rs @@ -6,16 +6,16 @@ fn main() { let s = [1, 2, 3]; let v = vec![1, 2, 3]; - s.iter().next(); + let _ = s.iter().next(); // Should be replaced by s.get(0) - s[2..].iter().next(); + let _ = s[2..].iter().next(); // Should be replaced by s.get(2) - v[5..].iter().next(); + let _ = v[5..].iter().next(); // Should be replaced by v.get(5) - v.iter().next(); + let _ = v.iter().next(); // Should be replaced by v.get(0) let o = Some(5); From 41f1413085ecf03e3a8ee33c6174d4239f62ddc0 Mon Sep 17 00:00:00 2001 From: Jendrik Date: Sat, 26 Mar 2022 16:19:47 +0100 Subject: [PATCH 19/82] add #[must_use] to functions of slice and its iterators. --- tests/ui/bytes_nth.stderr | 12 ++++++------ tests/ui/iter_next_slice.stderr | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/ui/bytes_nth.stderr b/tests/ui/bytes_nth.stderr index 536decf5e7fc..8a7afa934502 100644 --- a/tests/ui/bytes_nth.stderr +++ b/tests/ui/bytes_nth.stderr @@ -1,8 +1,8 @@ error: called `.byte().nth()` on a `String` - --> $DIR/bytes_nth.rs:8:5 + --> $DIR/bytes_nth.rs:8:13 | -LL | s.bytes().nth(3); - | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)` +LL | let _ = s.bytes().nth(3); + | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)` | = note: `-D clippy::bytes-nth` implied by `-D warnings` @@ -13,10 +13,10 @@ LL | let _ = &s.bytes().nth(3); | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)` error: called `.byte().nth()` on a `str` - --> $DIR/bytes_nth.rs:10:5 + --> $DIR/bytes_nth.rs:10:13 | -LL | s[..].bytes().nth(3); - | ^^^^^^^^^^^^^^^^^^^^ help: try: `s[..].as_bytes().get(3)` +LL | let _ = s[..].bytes().nth(3); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `s[..].as_bytes().get(3)` error: aborting due to 3 previous errors diff --git a/tests/ui/iter_next_slice.stderr b/tests/ui/iter_next_slice.stderr index 8c10a252ee01..a78d2c2d5e83 100644 --- a/tests/ui/iter_next_slice.stderr +++ b/tests/ui/iter_next_slice.stderr @@ -1,28 +1,28 @@ error: using `.iter().next()` on an array - --> $DIR/iter_next_slice.rs:9:5 + --> $DIR/iter_next_slice.rs:9:13 | -LL | s.iter().next(); - | ^^^^^^^^^^^^^^^ help: try calling: `s.get(0)` +LL | let _ = s.iter().next(); + | ^^^^^^^^^^^^^^^ help: try calling: `s.get(0)` | = note: `-D clippy::iter-next-slice` implied by `-D warnings` error: using `.iter().next()` on a Slice without end index - --> $DIR/iter_next_slice.rs:12:5 + --> $DIR/iter_next_slice.rs:12:13 | -LL | s[2..].iter().next(); - | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)` +LL | let _ = s[2..].iter().next(); + | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)` error: using `.iter().next()` on a Slice without end index - --> $DIR/iter_next_slice.rs:15:5 + --> $DIR/iter_next_slice.rs:15:13 | -LL | v[5..].iter().next(); - | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)` +LL | let _ = v[5..].iter().next(); + | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)` error: using `.iter().next()` on an array - --> $DIR/iter_next_slice.rs:18:5 + --> $DIR/iter_next_slice.rs:18:13 | -LL | v.iter().next(); - | ^^^^^^^^^^^^^^^ help: try calling: `v.get(0)` +LL | let _ = v.iter().next(); + | ^^^^^^^^^^^^^^^ help: try calling: `v.get(0)` error: aborting due to 4 previous errors From c687f6575f106b7cd7546b63b7692bad4dee4c5b Mon Sep 17 00:00:00 2001 From: Caio Date: Sat, 26 Mar 2022 15:39:21 -0300 Subject: [PATCH 20/82] Do not fire `panic` in a constant environment --- clippy_lints/src/panic_unimplemented.rs | 4 ++++ tests/ui/panicking_macros.rs | 17 ++++++++++++- tests/ui/panicking_macros.stderr | 32 ++++++++++++------------- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 6ef6b9a20aa4..2f3007658ea6 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -78,6 +78,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; if is_panic(cx, macro_call.def_id) { + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + return; + } + span_lint( cx, PANIC, diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index 733806464624..841fc289b4c0 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -1,8 +1,23 @@ +#![allow(clippy::assertions_on_constants, clippy::eq_op,)] +#![feature(inline_const)] #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] -#![allow(clippy::assertions_on_constants, clippy::eq_op)] extern crate core; +const _: () = { + if 1 == 0 { + panic!("A balanced diet means a cupcake in each hand"); + } +}; + +fn inline_const() { + let _ = const { + if 1 == 0 { + panic!("When nothing goes right, go left") + } + }; +} + fn panic() { let a = 2; panic!(); diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index bfd1c7a38014..4ceb6d1440f6 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -1,5 +1,5 @@ error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:8:5 + --> $DIR/panicking_macros.rs:23:5 | LL | panic!(); | ^^^^^^^^ @@ -7,19 +7,19 @@ LL | panic!(); = note: `-D clippy::panic` implied by `-D warnings` error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:9:5 + --> $DIR/panicking_macros.rs:24:5 | LL | panic!("message"); | ^^^^^^^^^^^^^^^^^ error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:10:5 + --> $DIR/panicking_macros.rs:25:5 | LL | panic!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:16:5 + --> $DIR/panicking_macros.rs:31:5 | LL | todo!(); | ^^^^^^^ @@ -27,19 +27,19 @@ LL | todo!(); = note: `-D clippy::todo` implied by `-D warnings` error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:17:5 + --> $DIR/panicking_macros.rs:32:5 | LL | todo!("message"); | ^^^^^^^^^^^^^^^^ error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:18:5 + --> $DIR/panicking_macros.rs:33:5 | LL | todo!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:24:5 + --> $DIR/panicking_macros.rs:39:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ @@ -47,19 +47,19 @@ LL | unimplemented!(); = note: `-D clippy::unimplemented` implied by `-D warnings` error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:25:5 + --> $DIR/panicking_macros.rs:40:5 | LL | unimplemented!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:26:5 + --> $DIR/panicking_macros.rs:41:5 | LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:32:5 + --> $DIR/panicking_macros.rs:47:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ @@ -67,37 +67,37 @@ LL | unreachable!(); = note: `-D clippy::unreachable` implied by `-D warnings` error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:33:5 + --> $DIR/panicking_macros.rs:48:5 | LL | unreachable!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:34:5 + --> $DIR/panicking_macros.rs:49:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:40:5 + --> $DIR/panicking_macros.rs:55:5 | LL | panic!(); | ^^^^^^^^ error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:41:5 + --> $DIR/panicking_macros.rs:56:5 | LL | todo!(); | ^^^^^^^ error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:42:5 + --> $DIR/panicking_macros.rs:57:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:43:5 + --> $DIR/panicking_macros.rs:58:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ From af8ed04e094e78316de9f37b926c9c4750bbddab Mon Sep 17 00:00:00 2001 From: Caio Date: Sat, 26 Mar 2022 15:48:17 -0300 Subject: [PATCH 21/82] Rustfmt --- tests/ui/panicking_macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index 841fc289b4c0..12a0c776ae2f 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -1,4 +1,4 @@ -#![allow(clippy::assertions_on_constants, clippy::eq_op,)] +#![allow(clippy::assertions_on_constants, clippy::eq_op)] #![feature(inline_const)] #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] From 52b563b28361d49eb455725f319500f7afa362d9 Mon Sep 17 00:00:00 2001 From: Tianyi Song <42670338+tysg@users.noreply.github.com> Date: Sun, 27 Mar 2022 21:48:15 +0800 Subject: [PATCH 22/82] Emit lint when rhs is negative --- clippy_lints/src/identity_op.rs | 6 +++--- tests/ui/identity_op.rs | 3 +++ tests/ui/identity_op.stderr | 16 ++++++++++++++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 0b1ab4dfa5a0..4d6bef89bea7 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { check(cx, left, -1, e.span, right.span); check(cx, right, -1, e.span, left.span); }, - BinOpKind::Rem => check_modulo(cx, left, right, e.span, left.span), + BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span), _ => (), } } @@ -71,11 +71,11 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_ && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))) } -fn check_modulo(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { +fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { let lhs_const = constant_full_int(cx, cx.typeck_results(), left); let rhs_const = constant_full_int(cx, cx.typeck_results(), right); if match (lhs_const, rhs_const) { - (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv, + (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(), (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, } { diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 5e120947646a..edc3fe1aec13 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -69,7 +69,10 @@ fn main() { 2 % 3; -2 % 3; + 2 % -3 + x; + -2 % -3 + x; x + 1 % 3; (x + 1) % 3; // no error 4 % 3; // no error + 4 % -3; // no error } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index cb9757d1158d..706f01a3dd6c 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -90,11 +90,23 @@ error: the operation is ineffective. Consider reducing it to `-2` LL | -2 % 3; | ^^^^^^ +error: the operation is ineffective. Consider reducing it to `2` + --> $DIR/identity_op.rs:72:5 + | +LL | 2 % -3 + x; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `-2` + --> $DIR/identity_op.rs:73:5 + | +LL | -2 % -3 + x; + | ^^^^^^^ + error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:72:9 + --> $DIR/identity_op.rs:74:9 | LL | x + 1 % 3; | ^^^^^ -error: aborting due to 16 previous errors +error: aborting due to 18 previous errors From ec851b870b59848bdbf531928a7cf38225d3a12e Mon Sep 17 00:00:00 2001 From: Jaic1 <506933131@qq.com> Date: Mon, 28 Mar 2022 12:09:01 +0800 Subject: [PATCH 23/82] First submit --- clippy_lints/src/casts/unnecessary_cast.rs | 14 +++++++++++++- tests/ui/unnecessary_cast.rs | 6 ++++++ tests/ui/unnecessary_cast_fixable.fixed | 5 +++++ tests/ui/unnecessary_cast_fixable.rs | 5 +++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 470c8c7ea26a..af56ec11ef8a 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -4,7 +4,8 @@ use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Lit, UnOp}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, Lit, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; @@ -18,6 +19,17 @@ pub(super) fn check( cast_from: Ty<'_>, cast_to: Ty<'_>, ) -> bool { + // skip non-primitive type cast + if_chain! { + if let ExprKind::Cast(_, cast_to) = expr.kind; + if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind; + if let Res::PrimTy(_) = path.res; + then {} + else { + return false + } + } + if let Some(lit) = get_numeric_literal(cast_expr) { let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index b77c19f2ba59..62c3e9636866 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -30,4 +30,10 @@ fn main() { // do not lint cast to cfg-dependant type 1 as std::os::raw::c_char; + + // do not lint cast to alias type + 1 as I32Alias; + &1 as &I32Alias; } + +type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 3332f49c80c9..36800c5340db 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -42,4 +42,9 @@ fn main() { let _ = -1_i32; let _ = -1.0_f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; } + +type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index ec01e9387792..d4b6bb952ab3 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -42,4 +42,9 @@ fn main() { let _ = -1 as i32; let _ = -1.0 as f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; } + +type I32Alias = i32; From cb307bbfcd123d3bae8f210f2b51eebe68525c27 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Mon, 28 Mar 2022 04:32:31 -0400 Subject: [PATCH 24/82] Address review comments --- clippy_lints/src/crate_in_macro_def.rs | 60 +++++++++++++++++------- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/crate_in_macro_def.fixed | 31 +++++++++++- tests/ui/crate_in_macro_def.rs | 31 +++++++++++- tests/ui/crate_in_macro_def_allow.rs | 19 -------- tests/ui/crate_in_macro_def_allow.stderr | 0 6 files changed, 102 insertions(+), 41 deletions(-) delete mode 100644 tests/ui/crate_in_macro_def_allow.rs delete mode 100644 tests/ui/crate_in_macro_def_allow.stderr diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index de9c7ee5f204..21c65f9d6f04 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -1,24 +1,24 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::MacroDef; -use rustc_ast::node_id::NodeId; +use rustc_ast::ast::{AttrKind, Attribute, Item, ItemKind}; use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{symbol::sym, Span}; declare_clippy_lint! { /// ### What it does /// Checks for use of `crate` as opposed to `$crate` in a macro definition. /// /// ### Why is this bad? - /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro - /// definition's crate. Rarely is the former intended. See: + /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's + /// crate. Rarely is the former intended. See: /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene /// /// ### Example /// ```rust + /// #[macro_export] /// macro_rules! print_message { /// () => { /// println!("{}", crate::MESSAGE); @@ -28,6 +28,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust + /// #[macro_export] /// macro_rules! print_message { /// () => { /// println!("{}", $crate::MESSAGE); @@ -35,6 +36,13 @@ declare_clippy_lint! { /// } /// pub const MESSAGE: &str = "Hello!"; /// ``` + /// + /// Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the + /// macro definition, e.g.: + /// ```rust,ignore + /// #[allow(clippy::crate_in_macro_def)] + /// macro_rules! ok { ... crate::foo ... } + /// ``` #[clippy::version = "1.61.0"] pub CRATE_IN_MACRO_DEF, correctness, @@ -43,18 +51,36 @@ declare_clippy_lint! { declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); impl EarlyLintPass for CrateInMacroDef { - fn check_mac_def(&mut self, cx: &EarlyContext<'_>, macro_def: &MacroDef, _: NodeId) { - let tts = macro_def.body.inner_tokens(); - if let Some(span) = contains_unhygienic_crate_reference(&tts) { - span_lint_and_sugg( - cx, - CRATE_IN_MACRO_DEF, - span, - "reference to the macro call's crate, which is rarely intended", - "if reference to the macro definition's crate is intended, use", - String::from("$crate"), - Applicability::MachineApplicable, - ); + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if_chain! { + if item.attrs.iter().any(is_macro_export); + if let ItemKind::MacroDef(macro_def) = &item.kind; + let tts = macro_def.body.inner_tokens(); + if let Some(span) = contains_unhygienic_crate_reference(&tts); + then { + span_lint_and_sugg( + cx, + CRATE_IN_MACRO_DEF, + span, + "reference to the macro call's crate, which is rarely intended", + "if reference to the macro definition's crate is intended, use", + String::from("$crate"), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn is_macro_export(attr: &Attribute) -> bool { + if_chain! { + if let AttrKind::Normal(attr_item, _) = &attr.kind; + if let [segment] = attr_item.path.segments.as_slice(); + if segment.ident.name == sym::macro_export; + then { + true + } else { + false } } } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index b1e474f80561..680b2eb1da72 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -123,7 +123,7 @@ macro_rules! define_Conf { #[cfg(feature = "internal")] pub mod metadata { - use $crate::utils::internal_lints::metadata_collector::ClippyConfiguration; + use crate::utils::internal_lints::metadata_collector::ClippyConfiguration; macro_rules! wrap_option { () => (None); diff --git a/tests/ui/crate_in_macro_def.fixed b/tests/ui/crate_in_macro_def.fixed index 77b43f432601..9fc594be311e 100644 --- a/tests/ui/crate_in_macro_def.fixed +++ b/tests/ui/crate_in_macro_def.fixed @@ -1,8 +1,8 @@ // run-rustfix #![warn(clippy::crate_in_macro_def)] -#[macro_use] mod hygienic { + #[macro_export] macro_rules! print_message_hygienic { () => { println!("{}", $crate::hygienic::MESSAGE); @@ -12,8 +12,8 @@ mod hygienic { pub const MESSAGE: &str = "Hello!"; } -#[macro_use] mod unhygienic { + #[macro_export] macro_rules! print_message_unhygienic { () => { println!("{}", $crate::unhygienic::MESSAGE); @@ -23,7 +23,34 @@ mod unhygienic { pub const MESSAGE: &str = "Hello!"; } +mod unhygienic_intentionally { + // For cases where the use of `crate` is intentional, applying `allow` to the macro definition + // should suppress the lint. + #[allow(clippy::crate_in_macro_def)] + #[macro_export] + macro_rules! print_message_unhygienic_intentionally { + () => { + println!("{}", crate::CALLER_PROVIDED_MESSAGE); + }; + } +} + +#[macro_use] +mod not_exported { + macro_rules! print_message_not_exported { + () => { + println!("{}", crate::not_exported::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + fn main() { print_message_hygienic!(); print_message_unhygienic!(); + print_message_unhygienic_intentionally!(); + print_message_not_exported!(); } + +pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!"; diff --git a/tests/ui/crate_in_macro_def.rs b/tests/ui/crate_in_macro_def.rs index d72d4c4009c3..ac456108e4ab 100644 --- a/tests/ui/crate_in_macro_def.rs +++ b/tests/ui/crate_in_macro_def.rs @@ -1,8 +1,8 @@ // run-rustfix #![warn(clippy::crate_in_macro_def)] -#[macro_use] mod hygienic { + #[macro_export] macro_rules! print_message_hygienic { () => { println!("{}", $crate::hygienic::MESSAGE); @@ -12,8 +12,8 @@ mod hygienic { pub const MESSAGE: &str = "Hello!"; } -#[macro_use] mod unhygienic { + #[macro_export] macro_rules! print_message_unhygienic { () => { println!("{}", crate::unhygienic::MESSAGE); @@ -23,7 +23,34 @@ mod unhygienic { pub const MESSAGE: &str = "Hello!"; } +mod unhygienic_intentionally { + // For cases where the use of `crate` is intentional, applying `allow` to the macro definition + // should suppress the lint. + #[allow(clippy::crate_in_macro_def)] + #[macro_export] + macro_rules! print_message_unhygienic_intentionally { + () => { + println!("{}", crate::CALLER_PROVIDED_MESSAGE); + }; + } +} + +#[macro_use] +mod not_exported { + macro_rules! print_message_not_exported { + () => { + println!("{}", crate::not_exported::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + fn main() { print_message_hygienic!(); print_message_unhygienic!(); + print_message_unhygienic_intentionally!(); + print_message_not_exported!(); } + +pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!"; diff --git a/tests/ui/crate_in_macro_def_allow.rs b/tests/ui/crate_in_macro_def_allow.rs deleted file mode 100644 index d6b30fd96152..000000000000 --- a/tests/ui/crate_in_macro_def_allow.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![warn(clippy::crate_in_macro_def)] - -#[macro_use] -mod intentional { - // For cases where use of `crate` is intentional, applying `allow` to the macro definition - // should suppress the lint. - #[allow(clippy::crate_in_macro_def)] - macro_rules! print_message { - () => { - println!("{}", crate::CALLER_PROVIDED_MESSAGE); - }; - } -} - -fn main() { - print_message!(); -} - -pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!"; diff --git a/tests/ui/crate_in_macro_def_allow.stderr b/tests/ui/crate_in_macro_def_allow.stderr deleted file mode 100644 index e69de29bb2d1..000000000000 From e5fa22e3505319aff3a39bb5dae10b171a791889 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 28 Mar 2022 21:48:34 +0900 Subject: [PATCH 25/82] Fix version in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd288285532e..edc29825360c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ document. [57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master) -## Rust 1.59 (beta) +## Rust 1.60 (beta) Current beta, release 2022-04-07 From 46e832e1ee65b22122da30201a1e742fd5a27543 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 14 Feb 2022 16:10:22 +0000 Subject: [PATCH 26/82] Revert "Auto merge of #93893 - oli-obk:sad_revert, r=oli-obk" This reverts commit 6499c5e7fc173a3f55b7a3bd1e6a50e9edef782d, reversing changes made to 78450d2d602b06d9b94349aaf8cece1a4acaf3a8. --- clippy_utils/src/qualify_min_const_fn.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 891531951c1a..f099d2c07d18 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -33,6 +33,7 @@ pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Trait(..) + | ty::PredicateKind::OpaqueType(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), From b3535a1549691cd9d9ba5c4733077cf0266ba358 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 17 Feb 2022 13:28:06 +0000 Subject: [PATCH 27/82] Remove opaque type obligation and just register opaque types as they are encountered. This also registers obligations for the hidden type immediately. --- clippy_utils/src/qualify_min_const_fn.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index f099d2c07d18..891531951c1a 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -33,7 +33,6 @@ pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Trait(..) - | ty::PredicateKind::OpaqueType(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), From 448a26d6960d9a4902db59a57b61ed0586933dfd Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Tue, 29 Mar 2022 15:23:19 +0800 Subject: [PATCH 28/82] improve parent expr check --- clippy_lints/src/matches/needless_match.rs | 20 ++++++++------------ tests/ui/needless_match.fixed | 12 ++++++++++++ tests/ui/needless_match.rs | 12 ++++++++++++ 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 2830ae1f61d5..3703488632b8 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -14,7 +14,7 @@ use rustc_span::sym; use rustc_typeck::hir_ty_to_ty; pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { - if arms.len() > 1 && !is_coercion_casting(cx, ex, expr) && check_all_arms(cx, ex, arms) { + if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -49,7 +49,7 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], /// ``` pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) { if let Some(ref if_let) = higher::IfLet::hir(cx, ex) { - if !is_else_clause(cx.tcx, ex) && !is_coercion_casting(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) { + if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -119,39 +119,35 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { /// Manually check for coercion casting by checking if the type of the match operand or let expr /// differs with the assigned local variable or the funtion return type. -fn is_coercion_casting(cx: &LateContext<'_>, match_expr: &Expr<'_>, expr: &Expr<'_>) -> bool { - if let Some(p_node) = get_parent_node(cx.tcx, expr.hir_id) { +fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { + if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) { match p_node { // Compare match_expr ty with local in `let local = match match_expr {..}` Node::Local(local) => { let results = cx.typeck_results(); - return !same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(match_expr)); + return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` Node::Item(..) => { if let Some(fn_decl) = p_node.fn_decl() { if let FnRetTy::Return(ret_ty) = fn_decl.output { - return !same_type_and_consts( - hir_ty_to_ty(cx.tcx, ret_ty), - cx.typeck_results().expr_ty(match_expr), - ); + return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr)); } } }, // check the parent expr for this whole block `{ match match_expr {..} }` Node::Block(block) => { if let Some(block_parent_expr) = get_parent_expr_for_hir(cx, block.hir_id) { - return is_coercion_casting(cx, match_expr, block_parent_expr); + return expr_ty_matches_p_ty(cx, expr, block_parent_expr); } }, // recursively call on `if xxx {..}` etc. Node::Expr(p_expr) => { - return is_coercion_casting(cx, match_expr, p_expr); + return expr_ty_matches_p_ty(cx, expr, p_expr); }, _ => {}, } } - false } diff --git a/tests/ui/needless_match.fixed b/tests/ui/needless_match.fixed index d55dd589a546..9ccccaa1725a 100644 --- a/tests/ui/needless_match.fixed +++ b/tests/ui/needless_match.fixed @@ -182,4 +182,16 @@ mod issue8551 { } } +trait Tr { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32>; +} +impl Tr for Result { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32> { + match self { + Ok(x) => Ok(x), + Err(e) => Err(e), + } + } +} + fn main() {} diff --git a/tests/ui/needless_match.rs b/tests/ui/needless_match.rs index f3c7ee01a47a..d210ecff7f16 100644 --- a/tests/ui/needless_match.rs +++ b/tests/ui/needless_match.rs @@ -219,4 +219,16 @@ mod issue8551 { } } +trait Tr { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32>; +} +impl Tr for Result { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32> { + match self { + Ok(x) => Ok(x), + Err(e) => Err(e), + } + } +} + fn main() {} From c22b7b8814e459d985c8ab337d933a4ae4feecea Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 29 Mar 2022 21:51:37 +0900 Subject: [PATCH 29/82] Fix ICE for `iter_overeager_cloned` --- .../src/methods/iter_overeager_cloned.rs | 16 ++++++++++++---- tests/ui/iter_overeager_cloned.fixed | 6 ++++++ tests/ui/iter_overeager_cloned.rs | 6 ++++++ tests/ui/iter_overeager_cloned.stderr | 14 +++++++------- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index b93f1399eaee..54c9ca435a44 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::{get_iterator_item_ty, is_copy}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy}; use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; +use rustc_span::sym; use std::ops::Not; use super::ITER_OVEREAGER_CLONED; @@ -20,9 +21,16 @@ pub(super) fn check<'tcx>( map_arg: &[hir::Expr<'_>], ) { // Check if it's iterator and get type associated with `Item`. - let inner_ty = match get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)) { - Some(ty) => ty, - _ => return, + let inner_ty = if_chain! { + if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + let recv_ty = cx.typeck_results().expr_ty(recv); + if implements_trait(cx, recv_ty, iterator_trait_id, &[]); + if let Some(inner_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)); + then { + inner_ty + } else { + return; + } }; match inner_ty.kind() { diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index a9041671101b..56761ebbcb80 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![allow(dead_code)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; @@ -43,3 +44,8 @@ fn main() { // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); } + +// #8527 +fn cloned_flatten(x: Option<&Option>) -> Option { + x.cloned().flatten() +} diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index dd04e33a4b3a..98321d889b58 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![allow(dead_code)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; @@ -45,3 +46,8 @@ fn main() { // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); } + +// #8527 +fn cloned_flatten(x: Option<&Option>) -> Option { + x.cloned().flatten() +} diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index e36b0e36fbdf..0582700fd16a 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -1,5 +1,5 @@ error: called `cloned().last()` on an `Iterator`. It may be more efficient to call `last().cloned()` instead - --> $DIR/iter_overeager_cloned.rs:7:29 + --> $DIR/iter_overeager_cloned.rs:8:29 | LL | let _: Option = vec.iter().cloned().last(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().last().cloned()` @@ -7,13 +7,13 @@ LL | let _: Option = vec.iter().cloned().last(); = note: `-D clippy::iter-overeager-cloned` implied by `-D warnings` error: called `cloned().next()` on an `Iterator`. It may be more efficient to call `next().cloned()` instead - --> $DIR/iter_overeager_cloned.rs:9:29 + --> $DIR/iter_overeager_cloned.rs:10:29 | LL | let _: Option = vec.iter().chain(vec.iter()).cloned().next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().chain(vec.iter()).next().cloned()` error: called `cloned().count()` on an `Iterator`. It may be more efficient to call `count()` instead - --> $DIR/iter_overeager_cloned.rs:11:20 + --> $DIR/iter_overeager_cloned.rs:12:20 | LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").count()` @@ -21,25 +21,25 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); = note: `-D clippy::redundant-clone` implied by `-D warnings` error: called `cloned().take(...)` on an `Iterator`. It may be more efficient to call `take(...).cloned()` instead - --> $DIR/iter_overeager_cloned.rs:13:21 + --> $DIR/iter_overeager_cloned.rs:14:21 | LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().take(2).cloned()` error: called `cloned().skip(...)` on an `Iterator`. It may be more efficient to call `skip(...).cloned()` instead - --> $DIR/iter_overeager_cloned.rs:15:21 + --> $DIR/iter_overeager_cloned.rs:16:21 | LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().skip(2).cloned()` error: called `cloned().nth(...)` on an `Iterator`. It may be more efficient to call `nth(...).cloned()` instead - --> $DIR/iter_overeager_cloned.rs:17:13 + --> $DIR/iter_overeager_cloned.rs:18:13 | LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").nth(2).cloned()` error: called `cloned().flatten()` on an `Iterator`. It may be more efficient to call `flatten().cloned()` instead - --> $DIR/iter_overeager_cloned.rs:19:13 + --> $DIR/iter_overeager_cloned.rs:20:13 | LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] | _____________^ From e01897edcbcf89952b1823125c199a4ce8ec628d Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 29 Mar 2022 17:11:12 +0200 Subject: [PATCH 30/82] Remember mutability in `DefKind::Static`. This allows to compute the `BodyOwnerKind` from `DefKind` only, and removes a direct dependency of some MIR queries onto HIR. As a side effect, it also simplifies metadata, since we don't need 4 flavours of `EntryKind::*Static` any more. --- clippy_lints/src/arithmetic.rs | 4 ++-- clippy_lints/src/loops/needless_range_loop.rs | 2 +- clippy_lints/src/loops/while_immutable_condition.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 2 +- clippy_lints/src/shadow.rs | 10 ++++++++-- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs index e0c1d6ab6e12..c5948707c812 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/arithmetic.rs @@ -139,11 +139,11 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic { } fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { - let body_owner = cx.tcx.hir().body_owner(body.id()); + let body_owner = cx.tcx.hir().body_owner_def_id(body.id()); match cx.tcx.hir().body_owner_kind(body_owner) { hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => { - let body_span = cx.tcx.hir().span(body_owner); + let body_span = cx.tcx.def_span(body_owner); if let Some(span) = self.const_span { if span.contains(body_span) { diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 9d335073e4fb..72e86804ed2c 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -273,7 +273,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { } return false; // no need to walk further *on the variable* } - Res::Def(DefKind::Static | DefKind::Const, ..) => { + Res::Def(DefKind::Static (_)| DefKind::Const, ..) => { if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index 5dcfed65c78a..a63422d2a36a 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -104,7 +104,7 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { Res::Local(hir_id) => { self.ids.insert(hir_id); }, - Res::Def(DefKind::Static, def_id) => { + Res::Def(DefKind::Static(_), def_id) => { let mutable = self.cx.tcx.is_mutable_static(def_id); self.def_ids.insert(def_id, mutable); }, diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index c3cb02329a11..6f2307d8f18f 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -93,7 +93,7 @@ pub(super) fn check<'tcx>( }, hir::ExprKind::Path(ref p) => matches!( cx.qpath_res(p, arg.hir_id), - hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) + hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static(_), _) ), _ => false, } diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index ce05c5a6164f..118825850446 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -139,14 +139,20 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { let hir = cx.tcx.hir(); - if !matches!(hir.body_owner_kind(hir.body_owner(body.id())), BodyOwnerKind::Closure) { + if !matches!( + hir.body_owner_kind(hir.body_owner_def_id(body.id())), + BodyOwnerKind::Closure + ) { self.bindings.push(FxHashMap::default()); } } fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { let hir = cx.tcx.hir(); - if !matches!(hir.body_owner_kind(hir.body_owner(body.id())), BodyOwnerKind::Closure) { + if !matches!( + hir.body_owner_kind(hir.body_owner_def_id(body.id())), + BodyOwnerKind::Closure + ) { self.bindings.pop(); } } From 9a02386fd8f95303c4319bb3a18f8777e7908bcb Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 29 Mar 2022 18:36:38 +0100 Subject: [PATCH 31/82] Allow running `cargo dev lint` on a package directory --- clippy_dev/Cargo.toml | 1 + clippy_dev/src/bless.rs | 13 ++------- clippy_dev/src/lib.rs | 13 +++++++++ clippy_dev/src/lint.rs | 65 +++++++++++++++++++++++++++++++---------- clippy_dev/src/main.rs | 20 +++++++++---- 5 files changed, 81 insertions(+), 31 deletions(-) diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index d133e8cddabc..449ab1e9c7a0 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -11,6 +11,7 @@ itertools = "0.10.1" opener = "0.5" regex = "1.5" shell-escape = "0.1" +tempfile = "3.3" walkdir = "2.3" cargo_metadata = "0.14" diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs index b0fb39e81699..8e5c739afe05 100644 --- a/clippy_dev/src/bless.rs +++ b/clippy_dev/src/bless.rs @@ -1,22 +1,15 @@ //! `bless` updates the reference files in the repo with changed output files //! from the last test run. +use crate::cargo_clippy_path; use std::ffi::OsStr; use std::fs; use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; -#[cfg(not(windows))] -static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; -#[cfg(windows)] -static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; - -static CLIPPY_BUILD_TIME: SyncLazy> = SyncLazy::new(|| { - let mut path = std::env::current_exe().unwrap(); - path.set_file_name(CARGO_CLIPPY_EXE); - fs::metadata(path).ok()?.modified().ok() -}); +static CLIPPY_BUILD_TIME: SyncLazy> = + SyncLazy::new(|| cargo_clippy_path().metadata().ok()?.modified().ok()); /// # Panics /// diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 59fde4475471..72d01db27383 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -13,6 +13,19 @@ pub mod serve; pub mod setup; pub mod update_lints; +#[cfg(not(windows))] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; +#[cfg(windows)] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; + +/// Returns the path to the `cargo-clippy` binary +#[must_use] +pub fn cargo_clippy_path() -> PathBuf { + let mut path = std::env::current_exe().expect("failed to get current executable name"); + path.set_file_name(CARGO_CLIPPY_EXE); + path +} + /// Returns the path to the Clippy project directory /// /// # Panics diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index b8287980a4ba..1bc1a39542db 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -1,19 +1,52 @@ -use std::process::{self, Command}; +use crate::cargo_clippy_path; +use std::process::{self, Command, ExitStatus}; +use std::{fs, io}; -pub fn run(filename: &str) { - let code = Command::new("cargo") - .args(["run", "--bin", "clippy-driver", "--"]) - .args(["-L", "./target/debug"]) - .args(["-Z", "no-codegen"]) - .args(["--edition", "2021"]) - .arg(filename) - .status() - .expect("failed to run cargo") - .code(); - - if code.is_none() { - eprintln!("Killed by signal"); +fn exit_if_err(status: io::Result) { + match status.expect("failed to run command").code() { + Some(0) => {}, + Some(n) => process::exit(n), + None => { + eprintln!("Killed by signal"); + process::exit(1); + }, + } +} + +pub fn run(path: &str) { + let is_file = match fs::metadata(path) { + Ok(metadata) => metadata.is_file(), + Err(e) => { + eprintln!("Failed to read {path}: {e:?}"); + process::exit(1); + }, + }; + + if is_file { + exit_if_err( + Command::new("cargo") + .args(["run", "--bin", "clippy-driver", "--"]) + .args(["-L", "./target/debug"]) + .args(["-Z", "no-codegen"]) + .args(["--edition", "2021"]) + .arg(path) + .status(), + ); + } else { + exit_if_err(Command::new("cargo").arg("build").status()); + + // Run in a tempdir as changes to clippy do not retrigger linting + let target = tempfile::Builder::new() + .prefix("clippy") + .tempdir() + .expect("failed to create tempdir"); + + let status = Command::new(cargo_clippy_path()) + .current_dir(path) + .env("CARGO_TARGET_DIR", target.as_ref()) + .status(); + + target.close().expect("failed to remove tempdir"); + exit_if_err(status); } - - process::exit(code.unwrap_or(1)); } diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 30a241c8ba15..b1fe35a0243f 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -4,6 +4,7 @@ use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints}; +use indoc::indoc; fn main() { let matches = get_clap_config(); @@ -56,8 +57,8 @@ fn main() { serve::run(port, lint); }, ("lint", Some(matches)) => { - let filename = matches.value_of("filename").unwrap(); - lint::run(filename); + let path = matches.value_of("path").unwrap(); + lint::run(path); }, _ => {}, } @@ -225,11 +226,20 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { ) .subcommand( SubCommand::with_name("lint") - .about("Manually run clippy on a file") + .about("Manually run clippy on a file or package") + .after_help(indoc! {" + EXAMPLES + Lint a single file: + cargo dev lint tests/ui/attrs.rs + + Lint a package directory: + cargo dev lint tests/ui-cargo/wildcard_dependencies/fail + cargo dev lint ~/my-project + "}) .arg( - Arg::with_name("filename") + Arg::with_name("path") .required(true) - .help("The path to a file to lint"), + .help("The path to a file or package directory to lint"), ), ) .get_matches() From 148b5939542514910236e8c55ea06212afff29f2 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 18 Mar 2022 13:33:40 +0100 Subject: [PATCH 32/82] get clippy to compile again --- clippy_lints/src/implicit_saturating_sub.rs | 18 +++++------------- clippy_lints/src/methods/implicit_clone.rs | 9 +++++---- clippy_lints/src/methods/suspicious_splitn.rs | 8 ++++---- clippy_lints/src/ptr.rs | 18 +++--------------- clippy_utils/src/lib.rs | 14 ++++++-------- .../conf_disallowed_methods.rs | 4 ++-- .../conf_disallowed_methods.stderr | 14 +------------- 7 files changed, 26 insertions(+), 59 deletions(-) diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 6515975fbffd..ae4158662d46 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -3,7 +3,7 @@ use clippy_utils::{higher, peel_blocks_with_stmt, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -82,14 +82,6 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { // Get the variable name let var_name = ares_path.segments[0].ident.name.as_str(); - const INT_TYPES: [LangItem; 5] = [ - LangItem::I8, - LangItem::I16, - LangItem::I32, - LangItem::I64, - LangItem::Isize - ]; - match cond_num_val.kind { ExprKind::Lit(ref cond_lit) => { // Check if the constant is zero @@ -105,8 +97,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if name.ident.as_str() == "MIN"; if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(const_id); - let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok()); - if int_ids.any(|int_id| int_id == impl_id); + if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl + if cx.tcx.type_of(impl_id).is_integral(); then { print_lint_and_sugg(cx, var_name, expr) } @@ -118,8 +110,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if name.ident.as_str() == "min_value"; if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(func_id); - let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok()); - if int_ids.any(|int_id| int_id == impl_id); + if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl + if cx.tcx.type_of(impl_id).is_integral(); then { print_lint_and_sugg(cx, var_name, expr) } diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 6e64e7f62220..c98cdfbca434 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -49,10 +49,11 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned), "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path), "to_vec" => { - cx.tcx - .impl_of_method(method_def_id) - .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl()) - == Some(true) + cx.tcx.impl_of_method(method_def_id) + .filter(|&impl_did| { + cx.tcx.type_of(impl_did).is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none() + }) + .is_some() }, _ => false, } diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index 1c546a15bf62..55567d8625e5 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -12,13 +12,13 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se if count <= 1; if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(call_id); - let lang_items = cx.tcx.lang_items(); - if lang_items.slice_impl() == Some(impl_id) || lang_items.str_impl() == Some(impl_id); + if cx.tcx.impl_trait_ref(impl_id).is_none(); + let self_ty = cx.tcx.type_of(impl_id); + if self_ty.is_slice() || self_ty.is_str(); then { // Ignore empty slice and string literals when used with a literal count. if matches!(self_arg.kind, ExprKind::Array([])) || matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()) - { return; } @@ -28,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se "the resulting iterator will always return `None`") } else { (format!("`{}` called with `1` split", method_name), - if lang_items.slice_impl() == Some(impl_id) { + if self_ty.is_slice() { "the resulting iterator will always return the entire slice followed by `None`" } else { "the resulting iterator will always return the entire string followed by `None`" diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index ba1997e70e13..9d4313827f7c 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -16,7 +16,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, AssocItems, AssocKind, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; @@ -308,7 +308,6 @@ struct PtrArg<'tcx> { method_renames: &'static [(&'static str, &'static str)], ref_prefix: RefPrefix, deref_ty: DerefTy<'tcx>, - deref_assoc_items: Option<(DefId, &'tcx AssocItems<'tcx>)>, } impl PtrArg<'_> { fn build_msg(&self) -> String { @@ -411,7 +410,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( if params.get(i).map_or(true, |p| !is_lint_allowed(cx, PTR_ARG, p.hir_id)); then { - let (method_renames, deref_ty, deref_impl_id) = match cx.tcx.get_diagnostic_name(adt.did()) { + let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) { Some(sym::Vec) => ( [("clone", ".to_owned()")].as_slice(), DerefTy::Slice( @@ -424,17 +423,14 @@ fn check_fn_args<'cx, 'tcx: 'cx>( }), substs.type_at(0), ), - cx.tcx.lang_items().slice_impl() ), Some(sym::String) => ( [("clone", ".to_owned()"), ("as_str", "")].as_slice(), DerefTy::Str, - cx.tcx.lang_items().str_impl() ), Some(sym::PathBuf) => ( [("clone", ".to_path_buf()"), ("as_path", "")].as_slice(), DerefTy::Path, - None, ), Some(sym::Cow) if mutability == Mutability::Not => { let ty_name = name.args @@ -470,7 +466,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>( mutability, }, deref_ty, - deref_assoc_items: deref_impl_id.map(|id| (id, cx.tcx.associated_items(id))), }); } } @@ -607,14 +602,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: // If the types match check for methods which exist on both types. e.g. `Vec::len` and // `slice::len` ty::Adt(def, _) - if def.did() == args.ty_did - && (i != 0 - || self.cx.tcx.trait_of_item(id).is_some() - || !args.deref_assoc_items.map_or(false, |(id, items)| { - items - .find_by_name_and_kind(self.cx.tcx, name.ident, AssocKind::Fn, id) - .is_some() - })) => + if def.did() == args.ty_did => { set_skip_flag(); }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index cd20abd94ed2..b55075943b29 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -77,9 +77,9 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, lang_items, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, + def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl, ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, - MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Target, + MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -479,12 +479,10 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { _ => None, } } - fn find_primitive(tcx: TyCtxt<'_>, name: &str) -> Option { - if let Some(&(index, Target::Impl)) = lang_items::ITEM_REFS.get(&Symbol::intern(name)) { - tcx.lang_items().items()[index] - } else { - None - } + fn find_primitive(_tcx: TyCtxt<'_>, _name: &str) -> Option { + // FIXME: Deal with this without relying on lang items or by only + // looking at a single impl. + None } fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option { tcx.crates(()) diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index 338b3b5b28f4..f8086bd2e4d0 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -10,8 +10,8 @@ fn main() { let mut a = vec![1, 2, 3, 4]; a.iter().sum::(); - a.sort_unstable(); + a.sort_unstable(); // FIXME: Warn here - let _ = 2.0f32.clamp(3.0f32, 4.0f32); + let _ = 2.0f32.clamp(3.0f32, 4.0f32); // FIXME: Warn here let _ = 2.0f64.clamp(3.0f64, 4.0f64); } diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 5533676aea28..999ead10d518 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -20,17 +20,5 @@ error: use of a disallowed method `std::iter::Iterator::sum` LL | a.iter().sum::(); | ^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed method `slice::sort_unstable` - --> $DIR/conf_disallowed_methods.rs:13:5 - | -LL | a.sort_unstable(); - | ^^^^^^^^^^^^^^^^^ - -error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:15:13 - | -LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors From 104ba478f202c895545b3023919b346ba84d55a1 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 30 Mar 2022 11:57:53 +0200 Subject: [PATCH 33/82] clippy: nameres for primitive type impls --- clippy_utils/src/lib.rs | 95 ++++++++++++------- .../conf_disallowed_methods.rs | 4 +- .../conf_disallowed_methods.stderr | 14 ++- 3 files changed, 75 insertions(+), 38 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b55075943b29..62e144398012 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -87,6 +87,8 @@ use rustc_middle::hir::place::PlaceBase; use rustc_middle::ty as rustc_ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; +use rustc_middle::ty::{IntTy, UintTy, FloatTy}; +use rustc_middle::ty::fast_reject::SimplifiedTypeGen::*; use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture}; use rustc_semver::RustcVersion; use rustc_session::Session; @@ -455,14 +457,6 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> /// Resolves a def path like `std::vec::Vec`. /// This function is expensive and should be used sparingly. pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { - macro_rules! try_res { - ($e:expr) => { - match $e { - Some(e) => e, - None => return Res::Err, - } - }; - } fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option { match tcx.def_kind(def_id) { DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx @@ -479,10 +473,36 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { _ => None, } } - fn find_primitive(_tcx: TyCtxt<'_>, _name: &str) -> Option { - // FIXME: Deal with this without relying on lang items or by only - // looking at a single impl. - None + fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { + let single = |ty| tcx.incoherent_impls(ty).iter().copied(); + let empty = || [].iter().copied(); + match name { + "bool" => single(BoolSimplifiedType), + "char" => single(CharSimplifiedType), + "str" => single(StrSimplifiedType), + "array" => single(ArraySimplifiedType), + "slice" => single(SliceSimplifiedType), + // FIXME: rustdoc documents these two using just `pointer`. + // + // Maybe this is something we should do here too. + "const_ptr" => single(PtrSimplifiedType(Mutability::Not)), + "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)), + "isize" => single(IntSimplifiedType(IntTy::Isize)), + "i8" => single(IntSimplifiedType(IntTy::I8)), + "i16" => single(IntSimplifiedType(IntTy::I16)), + "i32" => single(IntSimplifiedType(IntTy::I32)), + "i64" => single(IntSimplifiedType(IntTy::I64)), + "i128" => single(IntSimplifiedType(IntTy::I128)), + "usize" => single(UintSimplifiedType(UintTy::Usize)), + "u8" => single(UintSimplifiedType(UintTy::U8)), + "u16" => single(UintSimplifiedType(UintTy::U16)), + "u32" => single(UintSimplifiedType(UintTy::U32)), + "u64" => single(UintSimplifiedType(UintTy::U64)), + "u128" => single(UintSimplifiedType(UintTy::U128)), + "f32" => single(FloatSimplifiedType(FloatTy::F32)), + "f64" => single(FloatSimplifiedType(FloatTy::F64)), + _ => empty(), + } } fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option { tcx.crates(()) @@ -500,30 +520,35 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { _ => return Res::Err, }; let tcx = cx.tcx; - let first = try_res!( - find_primitive(tcx, base) - .or_else(|| find_crate(tcx, base)) - .and_then(|id| item_child_by_name(tcx, id, first)) - ); + let starts = find_primitive(tcx, base) + .chain(find_crate(tcx, base)) + .flat_map(|id| item_child_by_name(tcx, id, first)); - let last = path - .iter() - .copied() - // for each segment, find the child item - .try_fold(first, |res, segment| { - let def_id = res.def_id(); - if let Some(item) = item_child_by_name(tcx, def_id, segment) { - Some(item) - } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { - // it is not a child item so check inherent impl items - tcx.inherent_impls(def_id) - .iter() - .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment)) - } else { - None - } - }); - try_res!(last).expect_non_local() + for first in starts { + let last = path + .iter() + .copied() + // for each segment, find the child item + .try_fold(first, |res, segment| { + let def_id = res.def_id(); + if let Some(item) = item_child_by_name(tcx, def_id, segment) { + Some(item) + } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { + // it is not a child item so check inherent impl items + tcx.inherent_impls(def_id) + .iter() + .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment)) + } else { + None + } + }); + + if let Some(last) = last { + return last; + } + } + + Res::Err } /// Convenience function to get the `DefId` of a trait by path. diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index f8086bd2e4d0..338b3b5b28f4 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -10,8 +10,8 @@ fn main() { let mut a = vec![1, 2, 3, 4]; a.iter().sum::(); - a.sort_unstable(); // FIXME: Warn here + a.sort_unstable(); - let _ = 2.0f32.clamp(3.0f32, 4.0f32); // FIXME: Warn here + let _ = 2.0f32.clamp(3.0f32, 4.0f32); let _ = 2.0f64.clamp(3.0f64, 4.0f64); } diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 999ead10d518..5533676aea28 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -20,5 +20,17 @@ error: use of a disallowed method `std::iter::Iterator::sum` LL | a.iter().sum::(); | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: use of a disallowed method `slice::sort_unstable` + --> $DIR/conf_disallowed_methods.rs:13:5 + | +LL | a.sort_unstable(); + | ^^^^^^^^^^^^^^^^^ + +error: use of a disallowed method `f32::clamp` + --> $DIR/conf_disallowed_methods.rs:15:13 + | +LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors From 9b304533abac7479f168c0aaa1b6282194f4c128 Mon Sep 17 00:00:00 2001 From: Samuel Moelius <35515885+smoelius@users.noreply.github.com> Date: Wed, 30 Mar 2022 12:40:16 -0400 Subject: [PATCH 34/82] Update clippy_lints/src/crate_in_macro_def.rs Co-authored-by: llogiq --- clippy_lints/src/crate_in_macro_def.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index 21c65f9d6f04..844887dcbe53 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -62,7 +62,7 @@ impl EarlyLintPass for CrateInMacroDef { cx, CRATE_IN_MACRO_DEF, span, - "reference to the macro call's crate, which is rarely intended", + "`crate` references the macro call's crate", "if reference to the macro definition's crate is intended, use", String::from("$crate"), Applicability::MachineApplicable, From abc221e7f66b495d2ea86ccdb200ee9f29cecaab Mon Sep 17 00:00:00 2001 From: Samuel Moelius <35515885+smoelius@users.noreply.github.com> Date: Wed, 30 Mar 2022 12:40:25 -0400 Subject: [PATCH 35/82] Update clippy_lints/src/crate_in_macro_def.rs Co-authored-by: llogiq --- clippy_lints/src/crate_in_macro_def.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index 844887dcbe53..ef2f3df105a7 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -63,7 +63,7 @@ impl EarlyLintPass for CrateInMacroDef { CRATE_IN_MACRO_DEF, span, "`crate` references the macro call's crate", - "if reference to the macro definition's crate is intended, use", + "to reference the macro definition's crate, use", String::from("$crate"), Applicability::MachineApplicable, ); From 75dc406e84122363c94e1faaae37472d23d20c29 Mon Sep 17 00:00:00 2001 From: Samuel Moelius <35515885+smoelius@users.noreply.github.com> Date: Wed, 30 Mar 2022 12:40:44 -0400 Subject: [PATCH 36/82] Update clippy_lints/src/crate_in_macro_def.rs Co-authored-by: llogiq --- clippy_lints/src/crate_in_macro_def.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index ef2f3df105a7..e64c7f127eb5 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -76,9 +76,8 @@ fn is_macro_export(attr: &Attribute) -> bool { if_chain! { if let AttrKind::Normal(attr_item, _) = &attr.kind; if let [segment] = attr_item.path.segments.as_slice(); - if segment.ident.name == sym::macro_export; then { - true + segment.ident.name == sym::macro_export } else { false } From d6eb82c0a8d94d21fee110620ee4d0515ac198d3 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Wed, 30 Mar 2022 12:32:07 -0400 Subject: [PATCH 37/82] Move `crate_in_macro_def` to suspicious --- clippy_lints/src/crate_in_macro_def.rs | 2 +- clippy_lints/src/lib.register_correctness.rs | 1 - clippy_lints/src/lib.register_suspicious.rs | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index e64c7f127eb5..fc141b4a6e3a 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.61.0"] pub CRATE_IN_MACRO_DEF, - correctness, + suspicious, "using `crate` in a macro definition" } declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index d5cade8fe998..df63f84463db 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -16,7 +16,6 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), - LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(drop_forget_ref::DROP_COPY), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index fa3a88e1368c..0707e4f8f3d1 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -9,6 +9,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), + LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), From aaf04dc0431aaf4be9f3125dd9cfcb80f2ae7e25 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Wed, 30 Mar 2022 12:52:31 -0400 Subject: [PATCH 38/82] Fix error message in crate_in_macro_def.stderr --- tests/ui/crate_in_macro_def.stderr | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/ui/crate_in_macro_def.stderr b/tests/ui/crate_in_macro_def.stderr index 3f5e4870ad1c..9ac5937dcc06 100644 --- a/tests/ui/crate_in_macro_def.stderr +++ b/tests/ui/crate_in_macro_def.stderr @@ -1,14 +1,10 @@ -error: reference to the macro call's crate, which is rarely intended +error: `crate` references the macro call's crate --> $DIR/crate_in_macro_def.rs:19:28 | LL | println!("{}", crate::unhygienic::MESSAGE); - | ^^^^^ + | ^^^^^ help: to reference the macro definition's crate, use: `$crate` | = note: `-D clippy::crate-in-macro-def` implied by `-D warnings` -help: if reference to the macro definition's crate is intended, use - | -LL | println!("{}", $crate::unhygienic::MESSAGE); - | ~~~~~~ error: aborting due to previous error From 10a6d872d4e42884c4be43a51806ccdd6f8b89b5 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 30 Mar 2022 18:44:04 +0100 Subject: [PATCH 39/82] Handle relative paths in module_files lints --- clippy_lints/src/module_style.rs | 62 +++++++------------ .../module_style/fail_mod/src/main.stderr | 8 +-- .../module_style/fail_no_mod/src/main.stderr | 4 +- 3 files changed, 30 insertions(+), 44 deletions(-) diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index b8dfe9968806..39c44ad6e2cb 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -1,13 +1,10 @@ -use std::{ - ffi::OsString, - path::{Component, Path}, -}; - use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext}; +use std::ffi::OsStr; +use std::path::{Component, Path}; declare_clippy_lint! { /// ### What it does @@ -82,11 +79,7 @@ impl EarlyLintPass for ModStyle { let files = cx.sess().source_map().files(); - let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess().opts.working_dir { - p.to_string_lossy() - } else { - return; - }; + let RealFileName::LocalPath(trim_to_src) = &cx.sess().opts.working_dir else { return }; // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives // `[path, to]` but not foo @@ -97,26 +90,27 @@ impl EarlyLintPass for ModStyle { // `{ foo => path/to/foo.rs, .. } let mut file_map = FxHashMap::default(); for file in files.iter() { - match &file.name { - FileName::Real(RealFileName::LocalPath(lp)) - if lp.to_string_lossy().starts_with(trim_to_src.as_ref()) => - { - let p = lp.to_string_lossy(); - let path = Path::new(p.trim_start_matches(trim_to_src.as_ref())); - if let Some(stem) = path.file_stem() { - file_map.insert(stem.to_os_string(), (file, path.to_owned())); - } - process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); - check_self_named_mod_exists(cx, path, file); - }, - _ => {}, + if let FileName::Real(RealFileName::LocalPath(lp)) = &file.name { + let path = if lp.is_relative() { + lp + } else if let Ok(relative) = lp.strip_prefix(trim_to_src) { + relative + } else { + continue; + }; + + if let Some(stem) = path.file_stem() { + file_map.insert(stem, (file, path)); + } + process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); + check_self_named_mod_exists(cx, path, file); } } for folder in &folder_segments { if !mod_folders.contains(folder) { if let Some((file, path)) = file_map.get(folder) { - let mut correct = path.clone(); + let mut correct = path.to_path_buf(); correct.pop(); correct.push(folder); correct.push("mod.rs"); @@ -138,25 +132,17 @@ impl EarlyLintPass for ModStyle { /// For each `path` we add each folder component to `folder_segments` and if the file name /// is `mod.rs` we add it's parent folder to `mod_folders`. -fn process_paths_for_mod_files( - path: &Path, - folder_segments: &mut FxHashSet, - mod_folders: &mut FxHashSet, +fn process_paths_for_mod_files<'a>( + path: &'a Path, + folder_segments: &mut FxHashSet<&'a OsStr>, + mod_folders: &mut FxHashSet<&'a OsStr>, ) { let mut comp = path.components().rev().peekable(); let _ = comp.next(); if path.ends_with("mod.rs") { - mod_folders.insert(comp.peek().map(|c| c.as_os_str().to_owned()).unwrap_or_default()); + mod_folders.insert(comp.peek().map(|c| c.as_os_str()).unwrap_or_default()); } - let folders = comp - .filter_map(|c| { - if let Component::Normal(s) = c { - Some(s.to_os_string()) - } else { - None - } - }) - .collect::>(); + let folders = comp.filter_map(|c| if let Component::Normal(s) = c { Some(s) } else { None }); folder_segments.extend(folders); } diff --git a/tests/ui-cargo/module_style/fail_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_mod/src/main.stderr index af4c298b3108..e2010e998131 100644 --- a/tests/ui-cargo/module_style/fail_mod/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_mod/src/main.stderr @@ -1,19 +1,19 @@ -error: `mod.rs` files are required, found `/bad/inner.rs` +error: `mod.rs` files are required, found `bad/inner.rs` --> $DIR/bad/inner.rs:1:1 | LL | pub mod stuff; | ^ | = note: `-D clippy::self-named-module-files` implied by `-D warnings` - = help: move `/bad/inner.rs` to `/bad/inner/mod.rs` + = help: move `bad/inner.rs` to `bad/inner/mod.rs` -error: `mod.rs` files are required, found `/bad/inner/stuff.rs` +error: `mod.rs` files are required, found `bad/inner/stuff.rs` --> $DIR/bad/inner/stuff.rs:1:1 | LL | pub mod most; | ^ | - = help: move `/bad/inner/stuff.rs` to `/bad/inner/stuff/mod.rs` + = help: move `bad/inner/stuff.rs` to `bad/inner/stuff/mod.rs` error: aborting due to 2 previous errors diff --git a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr index 11e15db7fb96..f91940209383 100644 --- a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr @@ -1,11 +1,11 @@ -error: `mod.rs` files are not allowed, found `/bad/mod.rs` +error: `mod.rs` files are not allowed, found `bad/mod.rs` --> $DIR/bad/mod.rs:1:1 | LL | pub struct Thing; | ^ | = note: `-D clippy::mod-module-files` implied by `-D warnings` - = help: move `/bad/mod.rs` to `/bad.rs` + = help: move `bad/mod.rs` to `bad.rs` error: aborting due to previous error From d6f05c6a89e7bbaafc2ca30f845de488cd5a8978 Mon Sep 17 00:00:00 2001 From: SabrinaJewson Date: Wed, 30 Mar 2022 18:47:50 +0100 Subject: [PATCH 40/82] Don't warn int-to-char transmutes in const contexts --- clippy_lints/src/transmute/mod.rs | 9 ++-- .../src/transmute/transmute_int_to_char.rs | 3 +- tests/ui/transmute.rs | 4 ++ tests/ui/transmute.stderr | 42 +++++++++---------- 4 files changed, 32 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 02569bd3a476..342f23f030cd 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -410,9 +410,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if cx.tcx.is_diagnostic_item(sym::transmute, def_id); then { - // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts. - // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. - // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers. + // Avoid suggesting non-const operations in const contexts: + // - from/to bits (https://github.com/rust-lang/rust/issues/73736) + // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911) + // - char conversions (https://github.com/rust-lang/rust/issues/89259) let const_context = in_constant(cx, e.hir_id); let from_ty = cx.typeck_results().expr_ty_adjusted(arg); @@ -427,7 +428,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath) - | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg) + | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs index 3eb07b68992a..9e1823c373bf 100644 --- a/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -15,9 +15,10 @@ pub(super) fn check<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, + const_context: bool, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) => { + (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) if !const_context => { span_lint_and_then( cx, TRANSMUTE_INT_TO_CHAR, diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 9b681a79aae7..54f9727d8ea3 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -73,6 +73,10 @@ fn crosspointer() { fn int_to_char() { let _: char = unsafe { std::mem::transmute(0_u32) }; let _: char = unsafe { std::mem::transmute(0_i32) }; + + // These shouldn't warn + const _: char = unsafe { std::mem::transmute(0_u32) }; + const _: char = unsafe { std::mem::transmute(0_i32) }; } #[warn(clippy::transmute_int_to_bool)] diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 86537153e322..5c60fc2d5115 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -107,7 +107,7 @@ LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:80:28 + --> $DIR/transmute.rs:84:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -115,7 +115,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:86:31 + --> $DIR/transmute.rs:90:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` @@ -123,25 +123,25 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:87:31 + --> $DIR/transmute.rs:91:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> $DIR/transmute.rs:88:31 + --> $DIR/transmute.rs:92:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> $DIR/transmute.rs:89:31 + --> $DIR/transmute.rs:93:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:109:30 + --> $DIR/transmute.rs:113:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -149,85 +149,85 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:110:30 + --> $DIR/transmute.rs:114:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:111:31 + --> $DIR/transmute.rs:115:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:112:30 + --> $DIR/transmute.rs:116:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:113:30 + --> $DIR/transmute.rs:117:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:114:31 + --> $DIR/transmute.rs:118:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> $DIR/transmute.rs:115:30 + --> $DIR/transmute.rs:119:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> $DIR/transmute.rs:116:30 + --> $DIR/transmute.rs:120:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:121:30 + --> $DIR/transmute.rs:125:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:122:30 + --> $DIR/transmute.rs:126:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:123:31 + --> $DIR/transmute.rs:127:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:124:30 + --> $DIR/transmute.rs:128:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:125:30 + --> $DIR/transmute.rs:129:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:126:31 + --> $DIR/transmute.rs:130:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:134:28 + --> $DIR/transmute.rs:138:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -235,7 +235,7 @@ LL | let _: &str = unsafe { std::mem::transmute(b) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:135:32 + --> $DIR/transmute.rs:139:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` From 528ada958bf51b6a48430f357f6ae77a9e81c650 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Sun, 27 Mar 2022 14:16:08 +0200 Subject: [PATCH 41/82] add unit_like_struct_brackets --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/unit_like_struct_brackets.rs | 53 +++++++++++++++++++ tests/ui/unit_like_struct_brackets.fixed | 13 +++++ tests/ui/unit_like_struct_brackets.rs | 13 +++++ tests/ui/unit_like_struct_brackets.stderr | 16 ++++++ 9 files changed, 101 insertions(+) create mode 100644 clippy_lints/src/unit_like_struct_brackets.rs create mode 100644 tests/ui/unit_like_struct_brackets.fixed create mode 100644 tests/ui/unit_like_struct_brackets.rs create mode 100644 tests/ui/unit_like_struct_brackets.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d1e081e839a..49a44d988139 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3636,6 +3636,7 @@ Released 2018-09-13 [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp [`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash +[`unit_like_struct_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_like_struct_brackets [`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 1fb3ca1fd9b2..f488aab46ecc 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -301,6 +301,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), + LintId::of(unit_like_struct_brackets::UNIT_LIKE_STRUCT_BRACKETS), LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(unit_types::UNIT_ARG), LintId::of(unit_types::UNIT_CMP), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index e3161795139d..a1e6ca76a16b 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -512,6 +512,7 @@ store.register_lints(&[ unicode::UNICODE_NOT_NFC, uninit_vec::UNINIT_VEC, unit_hash::UNIT_HASH, + unit_like_struct_brackets::UNIT_LIKE_STRUCT_BRACKETS, unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, unit_types::LET_UNIT_VALUE, unit_types::UNIT_ARG, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index dcf399cf9562..89657cfb7842 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -105,6 +105,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(unit_like_struct_brackets::UNIT_LIKE_STRUCT_BRACKETS), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_unit::UNUSED_UNIT), LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c8b57956b1b6..af8d9b3655c5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -380,6 +380,7 @@ mod undropped_manually_drops; mod unicode; mod uninit_vec; mod unit_hash; +mod unit_like_struct_brackets; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; @@ -869,6 +870,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }) }); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); + store.register_early_pass(|| Box::new(unit_like_struct_brackets::UnitLikeStructBrackets)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unit_like_struct_brackets.rs b/clippy_lints/src/unit_like_struct_brackets.rs new file mode 100644 index 000000000000..0eeb765be851 --- /dev/null +++ b/clippy_lints/src/unit_like_struct_brackets.rs @@ -0,0 +1,53 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::*; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Finds structs without fields ("unit-like structs") that are declared with brackets. + /// + /// ### Why is this bad? + /// Empty brackets after a struct declaration can be omitted. + /// + /// ### Example + /// ```rust + /// struct Cookie {} + /// ``` + /// Use instead: + /// ```rust + /// struct Cookie; + /// ``` + #[clippy::version = "1.61.0"] + pub UNIT_LIKE_STRUCT_BRACKETS, + style, + "finds struct declarations with empty brackets" +} +declare_lint_pass!(UnitLikeStructBrackets => [UNIT_LIKE_STRUCT_BRACKETS]); + +impl EarlyLintPass for UnitLikeStructBrackets { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::Struct(var_data, _) = &item.kind && has_no_fields(var_data) { + let span_after_ident = item.span.with_lo(item.ident.span.hi()); + + span_lint_and_sugg( + cx, + UNIT_LIKE_STRUCT_BRACKETS, + span_after_ident, + "found empty brackets on struct declaration", + "remove the brackets", + ";".to_string(), + Applicability::MachineApplicable + ); + } + } +} + +fn has_no_fields(var_data: &VariantData) -> bool { + match var_data { + VariantData::Struct(defs, _) => defs.is_empty(), + VariantData::Tuple(defs, _) => defs.is_empty(), + VariantData::Unit(_) => false, + } +} diff --git a/tests/ui/unit_like_struct_brackets.fixed b/tests/ui/unit_like_struct_brackets.fixed new file mode 100644 index 000000000000..a69c310e9736 --- /dev/null +++ b/tests/ui/unit_like_struct_brackets.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::unit_like_struct_brackets)] +#![allow(dead_code)] + +pub struct MyEmptyStruct; // should trigger lint +struct MyEmptyTupleStruct; // should trigger lint + +struct MyStruct { // should not trigger lint + field: u8, +} +struct MyTupleStruct(usize, String); // should not trigger lint + +fn main() {} diff --git a/tests/ui/unit_like_struct_brackets.rs b/tests/ui/unit_like_struct_brackets.rs new file mode 100644 index 000000000000..8697a24f1d77 --- /dev/null +++ b/tests/ui/unit_like_struct_brackets.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::unit_like_struct_brackets)] +#![allow(dead_code)] + +pub struct MyEmptyStruct {} // should trigger lint +struct MyEmptyTupleStruct(); // should trigger lint + +struct MyStruct { // should not trigger lint + field: u8, +} +struct MyTupleStruct(usize, String); // should not trigger lint + +fn main() {} diff --git a/tests/ui/unit_like_struct_brackets.stderr b/tests/ui/unit_like_struct_brackets.stderr new file mode 100644 index 000000000000..146ede19c071 --- /dev/null +++ b/tests/ui/unit_like_struct_brackets.stderr @@ -0,0 +1,16 @@ +error: found empty brackets on struct declaration + --> $DIR/unit_like_struct_brackets.rs:5:25 + | +LL | pub struct MyEmptyStruct {} // should trigger lint + | ^^^ help: remove the brackets: `;` + | + = note: `-D clippy::unit-like-struct-brackets` implied by `-D warnings` + +error: found empty brackets on struct declaration + --> $DIR/unit_like_struct_brackets.rs:6:26 + | +LL | struct MyEmptyTupleStruct(); // should trigger lint + | ^^^ help: remove the brackets: `;` + +error: aborting due to 2 previous errors + From 9be3945be740b12c333a68db6fa33b8878acdeb9 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Sun, 27 Mar 2022 14:41:09 +0200 Subject: [PATCH 42/82] fix existing clippy tests --- clippy_lints/src/use_self.rs | 4 ++-- tests/ui-toml/struct_excessive_bools/test.rs | 2 +- ...se_sensitive_file_extension_comparisons.rs | 2 +- tests/ui/crashes/ice-2774.rs | 2 +- tests/ui/crashes/ice-6179.rs | 2 +- tests/ui/crashes/ice-6792.rs | 2 +- .../crashes/needless_lifetimes_impl_trait.rs | 2 +- tests/ui/crashes/regressions.rs | 2 +- tests/ui/default_numeric_fallback_f64.fixed | 2 +- tests/ui/default_numeric_fallback_f64.rs | 2 +- tests/ui/default_numeric_fallback_i32.fixed | 2 +- tests/ui/default_numeric_fallback_i32.rs | 2 +- tests/ui/drop_forget_copy.rs | 2 +- tests/ui/fn_params_excessive_bools.rs | 2 +- tests/ui/implicit_clone.rs | 4 ++-- tests/ui/iter_nth_zero.fixed | 2 +- tests/ui/iter_nth_zero.rs | 2 +- tests/ui/large_types_passed_by_value.rs | 2 +- tests/ui/let_and_return.rs | 2 +- tests/ui/let_underscore_must_use.rs | 2 +- tests/ui/manual_async_fn.fixed | 2 +- tests/ui/manual_async_fn.rs | 2 +- tests/ui/manual_unwrap_or.fixed | 2 +- tests/ui/manual_unwrap_or.rs | 2 +- tests/ui/map_unit_fn.rs | 2 +- tests/ui/min_rust_version_attr.rs | 2 +- tests/ui/missing_inline.rs | 6 ++--- tests/ui/module_name_repetitions.rs | 2 +- .../needless_arbitrary_self_type_unfixable.rs | 4 ++-- tests/ui/needless_lifetimes.rs | 2 +- tests/ui/no_effect.rs | 4 ++-- tests/ui/or_then_unwrap.fixed | 4 ++-- tests/ui/or_then_unwrap.rs | 4 ++-- tests/ui/ptr_arg.rs | 2 +- tests/ui/recursive_format_impl.rs | 24 +++++++++---------- tests/ui/redundant_allocation.rs | 2 +- tests/ui/redundant_allocation_fixable.fixed | 2 +- tests/ui/redundant_static_lifetimes.fixed | 2 +- tests/ui/redundant_static_lifetimes.rs | 2 +- tests/ui/same_item_push.rs | 2 +- tests/ui/trait_duplication_in_bounds.rs | 4 ++-- tests/ui/unit_like_struct_brackets.fixed | 9 +++---- tests/ui/unit_like_struct_brackets.rs | 9 +++---- tests/ui/unsafe_derive_deserialize.rs | 14 +++++------ tests/ui/unsafe_removed_from_name.rs | 4 ++-- tests/ui/unused_self.rs | 10 ++++---- tests/ui/use_self.fixed | 22 ++++++++--------- tests/ui/use_self.rs | 22 ++++++++--------- tests/ui/useless_attribute.fixed | 2 +- tests/ui/useless_attribute.rs | 2 +- 50 files changed, 109 insertions(+), 107 deletions(-) diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 09d671e11184..f8e1021af0ea 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -34,7 +34,7 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// struct Foo {} + /// struct Foo; /// impl Foo { /// fn new() -> Foo { /// Foo {} @@ -43,7 +43,7 @@ declare_clippy_lint! { /// ``` /// could be /// ```rust - /// struct Foo {} + /// struct Foo; /// impl Foo { /// fn new() -> Self { /// Self {} diff --git a/tests/ui-toml/struct_excessive_bools/test.rs b/tests/ui-toml/struct_excessive_bools/test.rs index 242984680e16..32dd80246fab 100644 --- a/tests/ui-toml/struct_excessive_bools/test.rs +++ b/tests/ui-toml/struct_excessive_bools/test.rs @@ -4,6 +4,6 @@ struct S { a: bool, } -struct Foo {} +struct Foo; fn main() {} diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs index 68719c2bc6d0..0d65071af15e 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -2,7 +2,7 @@ use std::string::String; -struct TestStruct {} +struct TestStruct; impl TestStruct { fn ends_with(self, arg: &str) {} diff --git a/tests/ui/crashes/ice-2774.rs b/tests/ui/crashes/ice-2774.rs index d44b0fae8200..88cfa1f923c0 100644 --- a/tests/ui/crashes/ice-2774.rs +++ b/tests/ui/crashes/ice-2774.rs @@ -8,7 +8,7 @@ pub struct Bar { } #[derive(Eq, PartialEq, Debug, Hash)] -pub struct Foo {} +pub struct Foo; #[allow(clippy::implicit_hasher)] // This should not cause a "cannot relate bound region" ICE. diff --git a/tests/ui/crashes/ice-6179.rs b/tests/ui/crashes/ice-6179.rs index 8d9a1af8ff11..4fe92d356c44 100644 --- a/tests/ui/crashes/ice-6179.rs +++ b/tests/ui/crashes/ice-6179.rs @@ -4,7 +4,7 @@ #![warn(clippy::use_self)] #![allow(dead_code)] -struct Foo {} +struct Foo; impl Foo { fn new() -> Self { diff --git a/tests/ui/crashes/ice-6792.rs b/tests/ui/crashes/ice-6792.rs index 0e2ab1a39b82..9cbafc716b50 100644 --- a/tests/ui/crashes/ice-6792.rs +++ b/tests/ui/crashes/ice-6792.rs @@ -7,7 +7,7 @@ trait Trait { fn broken() -> Self::Ty; } -struct Foo {} +struct Foo; impl Trait for Foo { type Ty = Foo; diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.rs b/tests/ui/crashes/needless_lifetimes_impl_trait.rs index 676564b2445d..376ff97ba603 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.rs +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.rs @@ -3,7 +3,7 @@ trait Foo {} -struct Bar {} +struct Bar; struct Baz<'a> { bar: &'a Bar, diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs index a41bcb33b446..6f9d98bbfe7f 100644 --- a/tests/ui/crashes/regressions.rs +++ b/tests/ui/crashes/regressions.rs @@ -6,6 +6,6 @@ pub fn foo(bar: *const u8) { // Regression test for https://github.com/rust-lang/rust-clippy/issues/4917 /// , _: Vec) {} fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} -struct S {} +struct S; trait Trait { fn f(_: bool, _: bool, _: bool, _: bool); fn g(_: bool, _: bool, _: bool, _: Vec); diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index 639fecb8927b..2549c9f32f90 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -30,7 +30,7 @@ where } #[derive(Copy, Clone)] -struct Kitten {} +struct Kitten; impl Kitten { // badly named method fn to_vec(self) -> Kitten { @@ -44,7 +44,7 @@ impl Borrow for Kitten { } } -struct BorrowedKitten {} +struct BorrowedKitten; impl ToOwned for BorrowedKitten { type Owned = Kitten; fn to_owned(&self) -> Kitten { diff --git a/tests/ui/iter_nth_zero.fixed b/tests/ui/iter_nth_zero.fixed index b54147c94d19..f23671c26e4c 100644 --- a/tests/ui/iter_nth_zero.fixed +++ b/tests/ui/iter_nth_zero.fixed @@ -3,7 +3,7 @@ #![warn(clippy::iter_nth_zero)] use std::collections::HashSet; -struct Foo {} +struct Foo; impl Foo { fn nth(&self, index: usize) -> usize { diff --git a/tests/ui/iter_nth_zero.rs b/tests/ui/iter_nth_zero.rs index b92c7d18adb4..7c968d498457 100644 --- a/tests/ui/iter_nth_zero.rs +++ b/tests/ui/iter_nth_zero.rs @@ -3,7 +3,7 @@ #![warn(clippy::iter_nth_zero)] use std::collections::HashSet; -struct Foo {} +struct Foo; impl Foo { fn nth(&self, index: usize) -> usize { diff --git a/tests/ui/large_types_passed_by_value.rs b/tests/ui/large_types_passed_by_value.rs index e4a2e9df4d7b..7601b5c66fa3 100644 --- a/tests/ui/large_types_passed_by_value.rs +++ b/tests/ui/large_types_passed_by_value.rs @@ -37,7 +37,7 @@ pub trait PubLargeTypeDevourer { fn devoure_array_in_public(&self, array: [u8; 6666]); } -struct S {} +struct S; impl LargeTypeDevourer for S { fn devoure_array(&self, array: [u8; 6666]) { todo!(); diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index e3561863c1e1..bb162adc9adb 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -88,7 +88,7 @@ mod no_lint_if_stmt_borrows { ret } - struct Bar {} + struct Bar; impl Bar { fn new() -> Self { diff --git a/tests/ui/let_underscore_must_use.rs b/tests/ui/let_underscore_must_use.rs index a842e872a37b..1edb77c748bf 100644 --- a/tests/ui/let_underscore_must_use.rs +++ b/tests/ui/let_underscore_must_use.rs @@ -26,7 +26,7 @@ fn h() -> u32 { 0 } -struct S {} +struct S; impl S { #[must_use] diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 136cc96be70c..b7e46a4a8ccc 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -38,7 +38,7 @@ async fn already_async() -> impl Future { async { 42 } } -struct S {} +struct S; impl S { async fn inh_fut() -> i32 { // NOTE: this code is here just to check that the indentation is correct in the suggested fix diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index ddc453ffdb75..b05429da6622 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -52,7 +52,7 @@ async fn already_async() -> impl Future { async { 42 } } -struct S {} +struct S; impl S { fn inh_fut() -> impl Future { async { diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 05d6c56f2aca..7d68978216c9 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -78,7 +78,7 @@ fn result_unwrap_or() { (Ok(1) as Result).unwrap_or(42); // method call case, suggestion must not surround Result expr `s.method()` with parentheses - struct S {} + struct S; impl S { fn method(self) -> Option { Some(42) diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 09f62c69b71d..b937fe6f977e 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -102,7 +102,7 @@ fn result_unwrap_or() { }; // method call case, suggestion must not surround Result expr `s.method()` with parentheses - struct S {} + struct S; impl S { fn method(self) -> Option { Some(42) diff --git a/tests/ui/map_unit_fn.rs b/tests/ui/map_unit_fn.rs index 9a74da4e3b8b..e7f07b50f3ab 100644 --- a/tests/ui/map_unit_fn.rs +++ b/tests/ui/map_unit_fn.rs @@ -1,5 +1,5 @@ #![allow(unused)] -struct Mappable {} +struct Mappable; impl Mappable { pub fn map(&self) {} diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index c5f221220ece..72e9bf9eb368 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -99,7 +99,7 @@ pub fn manual_range_contains() { } pub fn use_self() { - struct Foo {} + struct Foo; impl Foo { fn new() -> Foo { diff --git a/tests/ui/missing_inline.rs b/tests/ui/missing_inline.rs index b73b24b8e0a3..07f8e3888c99 100644 --- a/tests/ui/missing_inline.rs +++ b/tests/ui/missing_inline.rs @@ -7,8 +7,8 @@ type Typedef = String; pub type PubTypedef = String; -struct Foo {} // ok -pub struct PubFoo {} // ok +struct Foo; // ok +pub struct PubFoo; // ok enum FooE {} // ok pub enum PubFooE {} // ok @@ -63,4 +63,4 @@ impl PubFoo { // do not lint this since users cannot control the external code #[derive(Debug)] -pub struct S {} +pub struct S; diff --git a/tests/ui/module_name_repetitions.rs b/tests/ui/module_name_repetitions.rs index f5908cb5701f..ebaa77cc283e 100644 --- a/tests/ui/module_name_repetitions.rs +++ b/tests/ui/module_name_repetitions.rs @@ -7,7 +7,7 @@ mod foo { pub fn foo() {} pub fn foo_bar() {} pub fn bar_foo() {} - pub struct FooCake {} + pub struct FooCake; pub enum CakeFoo {} pub struct Foo7Bar; diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.rs b/tests/ui/needless_arbitrary_self_type_unfixable.rs index ad0d694a2174..02b43cce2bd4 100644 --- a/tests/ui/needless_arbitrary_self_type_unfixable.rs +++ b/tests/ui/needless_arbitrary_self_type_unfixable.rs @@ -14,7 +14,7 @@ mod issue_6089 { fn test(self: &Self); } - struct S1 {} + struct S1; impl T1 for S1 { fn test(self: &Self) {} @@ -32,7 +32,7 @@ mod issue_6089 { fn call_with_mut_self(&mut self); } - struct S2 {} + struct S2; // The method's signature will be expanded to: // fn call_with_mut_self<'life0>(self: &'life0 mut Self) {} diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index f3eafe8e9279..1456204ca869 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -268,7 +268,7 @@ mod issue4291 { mod issue2944 { trait Foo {} - struct Bar {} + struct Bar; struct Baz<'a> { bar: &'a Bar, } diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index 5427c88faf34..0a67fb72044c 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -68,7 +68,7 @@ impl FnOnce<(&str,)> for GreetStruct1 { } } -struct GreetStruct2(); +struct GreetStruct2; impl FnOnce<(&str,)> for GreetStruct2 { type Output = (); @@ -78,7 +78,7 @@ impl FnOnce<(&str,)> for GreetStruct2 { } } -struct GreetStruct3 {} +struct GreetStruct3; impl FnOnce<(&str,)> for GreetStruct3 { type Output = (); diff --git a/tests/ui/or_then_unwrap.fixed b/tests/ui/or_then_unwrap.fixed index 27d4b795a5ee..6e0d5a87f680 100644 --- a/tests/ui/or_then_unwrap.fixed +++ b/tests/ui/or_then_unwrap.fixed @@ -3,7 +3,7 @@ #![warn(clippy::or_then_unwrap)] #![allow(clippy::map_identity)] -struct SomeStruct {} +struct SomeStruct; impl SomeStruct { fn or(self, _: Option) -> Self { self @@ -11,7 +11,7 @@ impl SomeStruct { fn unwrap(&self) {} } -struct SomeOtherStruct {} +struct SomeOtherStruct; impl SomeOtherStruct { fn or(self) -> Self { self diff --git a/tests/ui/or_then_unwrap.rs b/tests/ui/or_then_unwrap.rs index 0dab5ae2f1c0..e406a71d46d0 100644 --- a/tests/ui/or_then_unwrap.rs +++ b/tests/ui/or_then_unwrap.rs @@ -3,7 +3,7 @@ #![warn(clippy::or_then_unwrap)] #![allow(clippy::map_identity)] -struct SomeStruct {} +struct SomeStruct; impl SomeStruct { fn or(self, _: Option) -> Self { self @@ -11,7 +11,7 @@ impl SomeStruct { fn unwrap(&self) {} } -struct SomeOtherStruct {} +struct SomeOtherStruct; impl SomeOtherStruct { fn or(self) -> Self { self diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index 03dd938a2339..814bbc7af713 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -112,7 +112,7 @@ mod issue_5644 { ) { } - struct S {} + struct S; impl S { fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec, diff --git a/tests/ui/recursive_format_impl.rs b/tests/ui/recursive_format_impl.rs index 9241bf7ed740..f72fc77ab997 100644 --- a/tests/ui/recursive_format_impl.rs +++ b/tests/ui/recursive_format_impl.rs @@ -66,7 +66,7 @@ impl std::fmt::Display for D { // Check for use of self as Display, in Display impl // Triggers on direct use of self -struct G {} +struct G; impl std::fmt::Display for G { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -75,7 +75,7 @@ impl std::fmt::Display for G { } // Triggers on reference to self -struct H {} +struct H; impl std::fmt::Display for H { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -90,7 +90,7 @@ impl std::fmt::Debug for H { } // Triggers on multiple reference to self -struct H2 {} +struct H2; impl std::fmt::Display for H2 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -99,7 +99,7 @@ impl std::fmt::Display for H2 { } // Doesn't trigger on correct deref -struct I {} +struct I; impl std::ops::Deref for I { type Target = str; @@ -122,7 +122,7 @@ impl std::fmt::Debug for I { } // Doesn't trigger on multiple correct deref -struct I2 {} +struct I2; impl std::ops::Deref for I2 { type Target = str; @@ -139,7 +139,7 @@ impl std::fmt::Display for I2 { } // Doesn't trigger on multiple correct deref -struct I3 {} +struct I3; impl std::ops::Deref for I3 { type Target = str; @@ -156,7 +156,7 @@ impl std::fmt::Display for I3 { } // Does trigger when deref resolves to self -struct J {} +struct J; impl std::ops::Deref for J { type Target = str; @@ -178,7 +178,7 @@ impl std::fmt::Debug for J { } } -struct J2 {} +struct J2; impl std::ops::Deref for J2 { type Target = str; @@ -194,7 +194,7 @@ impl std::fmt::Display for J2 { } } -struct J3 {} +struct J3; impl std::ops::Deref for J3 { type Target = str; @@ -210,7 +210,7 @@ impl std::fmt::Display for J3 { } } -struct J4 {} +struct J4; impl std::ops::Deref for J4 { type Target = str; @@ -227,7 +227,7 @@ impl std::fmt::Display for J4 { } // Doesn't trigger on Debug from Display -struct K {} +struct K; impl std::fmt::Debug for K { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -242,7 +242,7 @@ impl std::fmt::Display for K { } // Doesn't trigger on Display from Debug -struct K2 {} +struct K2; impl std::fmt::Debug for K2 { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index 52fbc91e3255..80f94e5f3cbb 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -3,7 +3,7 @@ #![allow(clippy::blacklisted_name, unused_variables, dead_code)] #![allow(unused_imports)] -pub struct MyStruct {} +pub struct MyStruct; pub struct SubT { foo: T, diff --git a/tests/ui/redundant_allocation_fixable.fixed b/tests/ui/redundant_allocation_fixable.fixed index ef089b25f47f..e7ed84731c02 100644 --- a/tests/ui/redundant_allocation_fixable.fixed +++ b/tests/ui/redundant_allocation_fixable.fixed @@ -4,7 +4,7 @@ #![allow(clippy::blacklisted_name, unused_variables, dead_code)] #![allow(unused_imports)] -pub struct MyStruct {} +pub struct MyStruct; pub struct SubT { foo: T, diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index 921249606ad2..acc8f1e25b6e 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -3,7 +3,7 @@ #![allow(unused)] #[derive(Debug)] -struct Foo {} +struct Foo; const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static. diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index 4d4b249d076f..f2f0f78659c9 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -3,7 +3,7 @@ #![allow(unused)] #[derive(Debug)] -struct Foo {} +struct Foo; const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 9d420ec672a2..99964f0de075 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -120,7 +120,7 @@ fn main() { } // Fix #5979 #[derive(Clone)] - struct S {} + struct S; trait T {} impl T for S {} diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index 21de19a26014..f5ca91143af2 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -62,7 +62,7 @@ trait BadTrait: Default + Clone { } #[derive(Default, Clone)] -struct Life {} +struct Life; impl T for Life { // this should not warn @@ -85,7 +85,7 @@ trait Iter: Iterator { } } -struct Foo {} +struct Foo; trait FooIter: Iterator { fn bar() diff --git a/tests/ui/unit_like_struct_brackets.fixed b/tests/ui/unit_like_struct_brackets.fixed index a69c310e9736..e6cf4a0c0b46 100644 --- a/tests/ui/unit_like_struct_brackets.fixed +++ b/tests/ui/unit_like_struct_brackets.fixed @@ -2,12 +2,13 @@ #![warn(clippy::unit_like_struct_brackets)] #![allow(dead_code)] -pub struct MyEmptyStruct; // should trigger lint -struct MyEmptyTupleStruct; // should trigger lint +pub struct MyEmptyStruct; // should trigger lint +struct MyEmptyTupleStruct; // should trigger lint -struct MyStruct { // should not trigger lint +struct MyStruct { + // should not trigger lint field: u8, } -struct MyTupleStruct(usize, String); // should not trigger lint +struct MyTupleStruct(usize, String); // should not trigger lint fn main() {} diff --git a/tests/ui/unit_like_struct_brackets.rs b/tests/ui/unit_like_struct_brackets.rs index 8697a24f1d77..306e4c207d85 100644 --- a/tests/ui/unit_like_struct_brackets.rs +++ b/tests/ui/unit_like_struct_brackets.rs @@ -2,12 +2,13 @@ #![warn(clippy::unit_like_struct_brackets)] #![allow(dead_code)] -pub struct MyEmptyStruct {} // should trigger lint -struct MyEmptyTupleStruct(); // should trigger lint +pub struct MyEmptyStruct {} // should trigger lint +struct MyEmptyTupleStruct(); // should trigger lint -struct MyStruct { // should not trigger lint +struct MyStruct { + // should not trigger lint field: u8, } -struct MyTupleStruct(usize, String); // should not trigger lint +struct MyTupleStruct(usize, String); // should not trigger lint fn main() {} diff --git a/tests/ui/unsafe_derive_deserialize.rs b/tests/ui/unsafe_derive_deserialize.rs index 690d705573d3..bafca91917aa 100644 --- a/tests/ui/unsafe_derive_deserialize.rs +++ b/tests/ui/unsafe_derive_deserialize.rs @@ -6,7 +6,7 @@ extern crate serde; use serde::Deserialize; #[derive(Deserialize)] -pub struct A {} +pub struct A; impl A { pub unsafe fn new(_a: i32, _b: i32) -> Self { Self {} @@ -14,13 +14,13 @@ impl A { } #[derive(Deserialize)] -pub struct B {} +pub struct B; impl B { pub unsafe fn unsafe_method(&self) {} } #[derive(Deserialize)] -pub struct C {} +pub struct C; impl C { pub fn unsafe_block(&self) { unsafe {} @@ -28,7 +28,7 @@ impl C { } #[derive(Deserialize)] -pub struct D {} +pub struct D; impl D { pub fn inner_unsafe_fn(&self) { unsafe fn inner() {} @@ -36,7 +36,7 @@ impl D { } // Does not derive `Deserialize`, should be ignored -pub struct E {} +pub struct E; impl E { pub unsafe fn new(_a: i32, _b: i32) -> Self { Self {} @@ -55,12 +55,12 @@ impl E { // Does not have methods using `unsafe`, should be ignored #[derive(Deserialize)] -pub struct F {} +pub struct F; // Check that we honor the `allow` attribute on the ADT #[allow(clippy::unsafe_derive_deserialize)] #[derive(Deserialize)] -pub struct G {} +pub struct G; impl G { pub fn unsafe_block(&self) { unsafe {} diff --git a/tests/ui/unsafe_removed_from_name.rs b/tests/ui/unsafe_removed_from_name.rs index a1f616733bd9..cde4e96d668c 100644 --- a/tests/ui/unsafe_removed_from_name.rs +++ b/tests/ui/unsafe_removed_from_name.rs @@ -14,8 +14,8 @@ use std::cell::UnsafeCell as Dangerunsafe; use std::cell::UnsafeCell as Bombsawayunsafe; mod mod_with_some_unsafe_things { - pub struct Safe {} - pub struct Unsafe {} + pub struct Safe; + pub struct Unsafe; } use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; diff --git a/tests/ui/unused_self.rs b/tests/ui/unused_self.rs index 7a4bbdda1ab2..08bf58fec7c3 100644 --- a/tests/ui/unused_self.rs +++ b/tests/ui/unused_self.rs @@ -5,7 +5,7 @@ mod unused_self { use std::pin::Pin; use std::sync::{Arc, Mutex}; - struct A {} + struct A; impl A { fn unused_self_move(self) {} @@ -27,7 +27,7 @@ mod unused_self { } mod unused_self_allow { - struct A {} + struct A; impl A { // shouldn't trigger @@ -35,7 +35,7 @@ mod unused_self_allow { fn unused_self_move(self) {} } - struct B {} + struct B; // shouldn't trigger #[allow(clippy::unused_self)] @@ -43,7 +43,7 @@ mod unused_self_allow { fn unused_self_move(self) {} } - struct C {} + struct C; #[allow(clippy::unused_self)] impl C { @@ -120,7 +120,7 @@ mod used_self { mod not_applicable { use std::fmt; - struct A {} + struct A; impl fmt::Debug for A { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 9d216f56ae60..3e62ffe74fed 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -16,7 +16,7 @@ extern crate proc_macro_derive; fn main() {} mod use_self { - struct Foo {} + struct Foo; impl Foo { fn new() -> Self { @@ -35,7 +35,7 @@ mod use_self { } mod better { - struct Foo {} + struct Foo; impl Foo { fn new() -> Self { @@ -123,7 +123,7 @@ mod macros { }; } - struct Foo {} + struct Foo; impl Foo { use_self_expand!(); // Should not lint in local macros @@ -134,7 +134,7 @@ mod macros { } mod nesting { - struct Foo {} + struct Foo; impl Foo { fn foo() { #[allow(unused_imports)] @@ -209,7 +209,7 @@ mod issue3410 { #[allow(clippy::no_effect, path_statements)] mod rustfix { mod nested { - pub struct A {} + pub struct A; } impl nested::A { @@ -227,7 +227,7 @@ mod rustfix { } mod issue3567 { - struct TestStruct {} + struct TestStruct; impl TestStruct { fn from_something() -> Self { Self {} @@ -248,7 +248,7 @@ mod issue3567 { mod paths_created_by_lowering { use std::ops::Range; - struct S {} + struct S; impl S { const A: usize = 0; @@ -382,7 +382,7 @@ mod issue4305 { } mod lint_at_item_level { - struct Foo {} + struct Foo; #[allow(clippy::use_self)] impl Foo { @@ -400,7 +400,7 @@ mod lint_at_item_level { } mod lint_at_impl_item_level { - struct Foo {} + struct Foo; impl Foo { #[allow(clippy::use_self)] @@ -433,8 +433,8 @@ mod issue4734 { mod nested_paths { use std::convert::Into; mod submod { - pub struct B {} - pub struct C {} + pub struct B; + pub struct C; impl Into for B { fn into(self) -> C { diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 5f604fe25e41..da2faddee12a 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -16,7 +16,7 @@ extern crate proc_macro_derive; fn main() {} mod use_self { - struct Foo {} + struct Foo; impl Foo { fn new() -> Foo { @@ -35,7 +35,7 @@ mod use_self { } mod better { - struct Foo {} + struct Foo; impl Foo { fn new() -> Self { @@ -123,7 +123,7 @@ mod macros { }; } - struct Foo {} + struct Foo; impl Foo { use_self_expand!(); // Should not lint in local macros @@ -134,7 +134,7 @@ mod macros { } mod nesting { - struct Foo {} + struct Foo; impl Foo { fn foo() { #[allow(unused_imports)] @@ -209,7 +209,7 @@ mod issue3410 { #[allow(clippy::no_effect, path_statements)] mod rustfix { mod nested { - pub struct A {} + pub struct A; } impl nested::A { @@ -227,7 +227,7 @@ mod rustfix { } mod issue3567 { - struct TestStruct {} + struct TestStruct; impl TestStruct { fn from_something() -> Self { Self {} @@ -248,7 +248,7 @@ mod issue3567 { mod paths_created_by_lowering { use std::ops::Range; - struct S {} + struct S; impl S { const A: usize = 0; @@ -382,7 +382,7 @@ mod issue4305 { } mod lint_at_item_level { - struct Foo {} + struct Foo; #[allow(clippy::use_self)] impl Foo { @@ -400,7 +400,7 @@ mod lint_at_item_level { } mod lint_at_impl_item_level { - struct Foo {} + struct Foo; impl Foo { #[allow(clippy::use_self)] @@ -433,8 +433,8 @@ mod issue4734 { mod nested_paths { use std::convert::Into; mod submod { - pub struct B {} - pub struct C {} + pub struct B; + pub struct C; impl Into for B { fn into(self) -> C { diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index a5fcde768f18..ce58a80347b5 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -42,7 +42,7 @@ mod a { mod b { #[allow(dead_code)] #[allow(unreachable_pub)] - pub struct C {} + pub struct C; } #[allow(unreachable_pub)] diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 0396d39e3d54..c82bb9ba07fd 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -42,7 +42,7 @@ mod a { mod b { #[allow(dead_code)] #[allow(unreachable_pub)] - pub struct C {} + pub struct C; } #[allow(unreachable_pub)] From 315521afc6a574eab8b9bbc6b14b412f9cdc0810 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Sun, 27 Mar 2022 15:26:36 +0200 Subject: [PATCH 43/82] fix uitests --- tests/ui/module_name_repetitions.stderr | 4 +- tests/ui/no_effect.rs | 3 +- tests/ui/no_effect.stderr | 60 +++++++++++------------ tests/ui/redundant_allocation_fixable.rs | 2 +- tests/ui/unit_like_struct_brackets.stderr | 4 +- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/tests/ui/module_name_repetitions.stderr b/tests/ui/module_name_repetitions.stderr index bdd217a969c0..3f343a3e4301 100644 --- a/tests/ui/module_name_repetitions.stderr +++ b/tests/ui/module_name_repetitions.stderr @@ -15,8 +15,8 @@ LL | pub fn bar_foo() {} error: item name starts with its containing module's name --> $DIR/module_name_repetitions.rs:10:5 | -LL | pub struct FooCake {} - | ^^^^^^^^^^^^^^^^^^^^^ +LL | pub struct FooCake; + | ^^^^^^^^^^^^^^^^^^^ error: item name ends with its containing module's name --> $DIR/module_name_repetitions.rs:11:5 diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index 0a67fb72044c..291dab4fb314 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -68,7 +68,8 @@ impl FnOnce<(&str,)> for GreetStruct1 { } } -struct GreetStruct2; +#[allow(clippy::unit_like_struct_brackets)] +struct GreetStruct2(); impl FnOnce<(&str,)> for GreetStruct2 { type Output = (); diff --git a/tests/ui/no_effect.stderr b/tests/ui/no_effect.stderr index 06b88bb5bee7..ee57e0d26df8 100644 --- a/tests/ui/no_effect.stderr +++ b/tests/ui/no_effect.stderr @@ -1,5 +1,5 @@ error: statement with no effect - --> $DIR/no_effect.rs:95:5 + --> $DIR/no_effect.rs:96:5 | LL | 0; | ^^ @@ -7,157 +7,157 @@ LL | 0; = note: `-D clippy::no-effect` implied by `-D warnings` error: statement with no effect - --> $DIR/no_effect.rs:96:5 + --> $DIR/no_effect.rs:97:5 | LL | s2; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:97:5 + --> $DIR/no_effect.rs:98:5 | LL | Unit; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:98:5 + --> $DIR/no_effect.rs:99:5 | LL | Tuple(0); | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:99:5 + --> $DIR/no_effect.rs:100:5 | LL | Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:100:5 + --> $DIR/no_effect.rs:101:5 | LL | Struct { ..s }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:101:5 + --> $DIR/no_effect.rs:102:5 | LL | Union { a: 0 }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:102:5 + --> $DIR/no_effect.rs:103:5 | LL | Enum::Tuple(0); | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:103:5 + --> $DIR/no_effect.rs:104:5 | LL | Enum::Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:104:5 + --> $DIR/no_effect.rs:105:5 | LL | 5 + 6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:105:5 + --> $DIR/no_effect.rs:106:5 | LL | *&42; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:106:5 + --> $DIR/no_effect.rs:107:5 | LL | &6; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:107:5 + --> $DIR/no_effect.rs:108:5 | LL | (5, 6, 7); | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:108:5 + --> $DIR/no_effect.rs:109:5 | LL | box 42; | ^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:109:5 + --> $DIR/no_effect.rs:110:5 | LL | ..; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:110:5 + --> $DIR/no_effect.rs:111:5 | LL | 5..; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:111:5 + --> $DIR/no_effect.rs:112:5 | LL | ..5; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:112:5 + --> $DIR/no_effect.rs:113:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:113:5 + --> $DIR/no_effect.rs:114:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:114:5 + --> $DIR/no_effect.rs:115:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:115:5 + --> $DIR/no_effect.rs:116:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:116:5 + --> $DIR/no_effect.rs:117:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:117:5 + --> $DIR/no_effect.rs:118:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:118:5 + --> $DIR/no_effect.rs:119:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:120:5 + --> $DIR/no_effect.rs:121:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:122:5 + --> $DIR/no_effect.rs:123:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:123:5 + --> $DIR/no_effect.rs:124:5 | LL | let _unused = 1; | ^^^^^^^^^^^^^^^^ @@ -165,19 +165,19 @@ LL | let _unused = 1; = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings` error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:124:5 + --> $DIR/no_effect.rs:125:5 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:125:5 + --> $DIR/no_effect.rs:126:5 | LL | let _duck = Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:126:5 + --> $DIR/no_effect.rs:127:5 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/redundant_allocation_fixable.rs b/tests/ui/redundant_allocation_fixable.rs index fefa87721d72..de763f98b5c8 100644 --- a/tests/ui/redundant_allocation_fixable.rs +++ b/tests/ui/redundant_allocation_fixable.rs @@ -4,7 +4,7 @@ #![allow(clippy::blacklisted_name, unused_variables, dead_code)] #![allow(unused_imports)] -pub struct MyStruct {} +pub struct MyStruct; pub struct SubT { foo: T, diff --git a/tests/ui/unit_like_struct_brackets.stderr b/tests/ui/unit_like_struct_brackets.stderr index 146ede19c071..7e7ba11cc719 100644 --- a/tests/ui/unit_like_struct_brackets.stderr +++ b/tests/ui/unit_like_struct_brackets.stderr @@ -1,7 +1,7 @@ error: found empty brackets on struct declaration --> $DIR/unit_like_struct_brackets.rs:5:25 | -LL | pub struct MyEmptyStruct {} // should trigger lint +LL | pub struct MyEmptyStruct {} // should trigger lint | ^^^ help: remove the brackets: `;` | = note: `-D clippy::unit-like-struct-brackets` implied by `-D warnings` @@ -9,7 +9,7 @@ LL | pub struct MyEmptyStruct {} // should trigger lint error: found empty brackets on struct declaration --> $DIR/unit_like_struct_brackets.rs:6:26 | -LL | struct MyEmptyTupleStruct(); // should trigger lint +LL | struct MyEmptyTupleStruct(); // should trigger lint | ^^^ help: remove the brackets: `;` error: aborting due to 2 previous errors From 1a5ff38f92971141d2f1d39cd0ddc80188f746de Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Sun, 27 Mar 2022 15:33:31 +0200 Subject: [PATCH 44/82] fix godfood test --- clippy_lints/src/unit_like_struct_brackets.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/unit_like_struct_brackets.rs b/clippy_lints/src/unit_like_struct_brackets.rs index 0eeb765be851..c4623df46bda 100644 --- a/clippy_lints/src/unit_like_struct_brackets.rs +++ b/clippy_lints/src/unit_like_struct_brackets.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::*; +use rustc_ast::ast::{Item, ItemKind, VariantData}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -46,8 +46,7 @@ impl EarlyLintPass for UnitLikeStructBrackets { fn has_no_fields(var_data: &VariantData) -> bool { match var_data { - VariantData::Struct(defs, _) => defs.is_empty(), - VariantData::Tuple(defs, _) => defs.is_empty(), + VariantData::Struct(defs, _) | VariantData::Tuple(defs, _) => defs.is_empty(), VariantData::Unit(_) => false, } } From 7192297c2878b22e44ca5bc58f5f7544b33cdf4a Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Sun, 27 Mar 2022 19:58:23 +0200 Subject: [PATCH 45/82] additional checks for conditionally compiled code --- clippy_lints/src/unit_like_struct_brackets.rs | 56 +++++++++++++++++-- tests/ui/unit_like_struct_brackets.fixed | 11 +++- tests/ui/unit_like_struct_brackets.rs | 11 +++- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/unit_like_struct_brackets.rs b/clippy_lints/src/unit_like_struct_brackets.rs index c4623df46bda..cb6e2a44afbe 100644 --- a/clippy_lints/src/unit_like_struct_brackets.rs +++ b/clippy_lints/src/unit_like_struct_brackets.rs @@ -1,8 +1,10 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt}; use rustc_ast::ast::{Item, ItemKind, VariantData}; use rustc_errors::Applicability; +use rustc_lexer::TokenKind; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -28,9 +30,9 @@ declare_lint_pass!(UnitLikeStructBrackets => [UNIT_LIKE_STRUCT_BRACKETS]); impl EarlyLintPass for UnitLikeStructBrackets { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::Struct(var_data, _) = &item.kind && has_no_fields(var_data) { - let span_after_ident = item.span.with_lo(item.ident.span.hi()); + let span_after_ident = item.span.with_lo(item.ident.span.hi()); + if let ItemKind::Struct(var_data, _) = &item.kind && has_no_fields(cx, var_data, span_after_ident) { span_lint_and_sugg( cx, UNIT_LIKE_STRUCT_BRACKETS, @@ -44,9 +46,51 @@ impl EarlyLintPass for UnitLikeStructBrackets { } } -fn has_no_fields(var_data: &VariantData) -> bool { +fn has_fields_in_hir(var_data: &VariantData) -> bool { match var_data { - VariantData::Struct(defs, _) | VariantData::Tuple(defs, _) => defs.is_empty(), - VariantData::Unit(_) => false, + VariantData::Struct(defs, _) | VariantData::Tuple(defs, _) => !defs.is_empty(), + VariantData::Unit(_) => true, + } +} + +fn has_no_ident_token(braces_span_str: &str) -> bool { + !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident) +} + +fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool { + if has_fields_in_hir(var_data) { + return false; + } + + // there might still be field declarations hidden from HIR + // (conditionaly compiled code using #[cfg(..)]) + + let Some(braces_span_str) = snippet_opt(cx, braces_span) else { + return false; + }; + + has_no_ident_token(braces_span_str.as_ref()) +} + +#[cfg(test)] +mod unit_test { + use super::*; + + #[test] + fn test_has_no_ident_token() { + let input = "{ field: u8 }"; + assert!(!has_no_ident_token(input)); + + let input = "(u8, String);"; + assert!(!has_no_ident_token(input)); + + let input = " { + // test = 5 + } + "; + assert!(has_no_ident_token(input)); + + let input = " ();"; + assert!(has_no_ident_token(input)); } } diff --git a/tests/ui/unit_like_struct_brackets.fixed b/tests/ui/unit_like_struct_brackets.fixed index e6cf4a0c0b46..78764337ec0f 100644 --- a/tests/ui/unit_like_struct_brackets.fixed +++ b/tests/ui/unit_like_struct_brackets.fixed @@ -5,8 +5,17 @@ pub struct MyEmptyStruct; // should trigger lint struct MyEmptyTupleStruct; // should trigger lint +// should not trigger lint +struct MyCfgStruct { + #[cfg(feature = "thisisneverenabled")] + field: u8, +} + +// should not trigger lint +struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8); + +// should not trigger lint struct MyStruct { - // should not trigger lint field: u8, } struct MyTupleStruct(usize, String); // should not trigger lint diff --git a/tests/ui/unit_like_struct_brackets.rs b/tests/ui/unit_like_struct_brackets.rs index 306e4c207d85..c43f97adfe90 100644 --- a/tests/ui/unit_like_struct_brackets.rs +++ b/tests/ui/unit_like_struct_brackets.rs @@ -5,8 +5,17 @@ pub struct MyEmptyStruct {} // should trigger lint struct MyEmptyTupleStruct(); // should trigger lint +// should not trigger lint +struct MyCfgStruct { + #[cfg(feature = "thisisneverenabled")] + field: u8, +} + +// should not trigger lint +struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8); + +// should not trigger lint struct MyStruct { - // should not trigger lint field: u8, } struct MyTupleStruct(usize, String); // should not trigger lint From 33383a418d6e57995a35a08b6b3d74dc010b9189 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Sun, 27 Mar 2022 20:10:10 +0200 Subject: [PATCH 46/82] use span_suggestion_hidden --- clippy_lints/src/unit_like_struct_brackets.rs | 14 +++++++++----- tests/ui/unit_like_struct_brackets.stderr | 7 +++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/unit_like_struct_brackets.rs b/clippy_lints/src/unit_like_struct_brackets.rs index cb6e2a44afbe..7c93aa57d9bf 100644 --- a/clippy_lints/src/unit_like_struct_brackets.rs +++ b/clippy_lints/src/unit_like_struct_brackets.rs @@ -1,4 +1,4 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt}; +use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt}; use rustc_ast::ast::{Item, ItemKind, VariantData}; use rustc_errors::Applicability; use rustc_lexer::TokenKind; @@ -33,14 +33,18 @@ impl EarlyLintPass for UnitLikeStructBrackets { let span_after_ident = item.span.with_lo(item.ident.span.hi()); if let ItemKind::Struct(var_data, _) = &item.kind && has_no_fields(cx, var_data, span_after_ident) { - span_lint_and_sugg( + span_lint_and_then( cx, UNIT_LIKE_STRUCT_BRACKETS, span_after_ident, "found empty brackets on struct declaration", - "remove the brackets", - ";".to_string(), - Applicability::MachineApplicable + |diagnostic| { + diagnostic.span_suggestion_hidden( + span_after_ident, + "remove the brackets", + ";".to_string(), + Applicability::MachineApplicable); + }, ); } } diff --git a/tests/ui/unit_like_struct_brackets.stderr b/tests/ui/unit_like_struct_brackets.stderr index 7e7ba11cc719..d3037f5d350a 100644 --- a/tests/ui/unit_like_struct_brackets.stderr +++ b/tests/ui/unit_like_struct_brackets.stderr @@ -2,15 +2,18 @@ error: found empty brackets on struct declaration --> $DIR/unit_like_struct_brackets.rs:5:25 | LL | pub struct MyEmptyStruct {} // should trigger lint - | ^^^ help: remove the brackets: `;` + | ^^^ | = note: `-D clippy::unit-like-struct-brackets` implied by `-D warnings` + = help: remove the brackets error: found empty brackets on struct declaration --> $DIR/unit_like_struct_brackets.rs:6:26 | LL | struct MyEmptyTupleStruct(); // should trigger lint - | ^^^ help: remove the brackets: `;` + | ^^^ + | + = help: remove the brackets error: aborting due to 2 previous errors From 37d5a6264c3ad22f878351267fbbfe47e62cd35b Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Mon, 28 Mar 2022 11:18:20 +0200 Subject: [PATCH 47/82] changes after review --- clippy_lints/src/unit_like_struct_brackets.rs | 21 +++++++++---------- tests/ui/unit_like_struct_brackets.fixed | 2 ++ tests/ui/unit_like_struct_brackets.rs | 2 ++ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/unit_like_struct_brackets.rs b/clippy_lints/src/unit_like_struct_brackets.rs index 7c93aa57d9bf..6269719e696c 100644 --- a/clippy_lints/src/unit_like_struct_brackets.rs +++ b/clippy_lints/src/unit_like_struct_brackets.rs @@ -21,7 +21,7 @@ declare_clippy_lint! { /// ```rust /// struct Cookie; /// ``` - #[clippy::version = "1.61.0"] + #[clippy::version = "1.62.0"] pub UNIT_LIKE_STRUCT_BRACKETS, style, "finds struct declarations with empty brackets" @@ -32,7 +32,9 @@ impl EarlyLintPass for UnitLikeStructBrackets { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { let span_after_ident = item.span.with_lo(item.ident.span.hi()); - if let ItemKind::Struct(var_data, _) = &item.kind && has_no_fields(cx, var_data, span_after_ident) { + if let ItemKind::Struct(var_data, _) = &item.kind + && !is_unit_like_struct(var_data) + && has_no_fields(cx, var_data, span_after_ident) { span_lint_and_then( cx, UNIT_LIKE_STRUCT_BRACKETS, @@ -50,23 +52,20 @@ impl EarlyLintPass for UnitLikeStructBrackets { } } -fn has_fields_in_hir(var_data: &VariantData) -> bool { - match var_data { - VariantData::Struct(defs, _) | VariantData::Tuple(defs, _) => !defs.is_empty(), - VariantData::Unit(_) => true, - } -} - fn has_no_ident_token(braces_span_str: &str) -> bool { !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident) } +fn is_unit_like_struct(var_data: &VariantData) -> bool { + matches!(var_data, VariantData::Unit(_)) +} + fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool { - if has_fields_in_hir(var_data) { + if !var_data.fields().is_empty() { return false; } - // there might still be field declarations hidden from HIR + // there might still be field declarations hidden from the AST // (conditionaly compiled code using #[cfg(..)]) let Some(braces_span_str) = snippet_opt(cx, braces_span) else { diff --git a/tests/ui/unit_like_struct_brackets.fixed b/tests/ui/unit_like_struct_brackets.fixed index 78764337ec0f..2769920c5fe9 100644 --- a/tests/ui/unit_like_struct_brackets.fixed +++ b/tests/ui/unit_like_struct_brackets.fixed @@ -19,5 +19,7 @@ struct MyStruct { field: u8, } struct MyTupleStruct(usize, String); // should not trigger lint +struct MySingleTupleStruct(usize); // should not trigger lint +struct MyUnitLikeStruct; // should not trigger lint fn main() {} diff --git a/tests/ui/unit_like_struct_brackets.rs b/tests/ui/unit_like_struct_brackets.rs index c43f97adfe90..b20f8516bd11 100644 --- a/tests/ui/unit_like_struct_brackets.rs +++ b/tests/ui/unit_like_struct_brackets.rs @@ -19,5 +19,7 @@ struct MyStruct { field: u8, } struct MyTupleStruct(usize, String); // should not trigger lint +struct MySingleTupleStruct(usize); // should not trigger lint +struct MyUnitLikeStruct; // should not trigger lint fn main() {} From 2953cba1160cb4dd8fe4923a4e9166b3ca6867f1 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Mon, 28 Mar 2022 11:35:43 +0200 Subject: [PATCH 48/82] unit_like_struct_brackets -> empty_structs_with_brackets --- CHANGELOG.md | 2 +- ..._struct_brackets.rs => empty_structs_with_brackets.rs} | 8 ++++---- clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_style.rs | 2 +- clippy_lints/src/lib.rs | 4 ++-- ...t_brackets.fixed => empty_structs_with_brackets.fixed} | 2 +- ..._struct_brackets.rs => empty_structs_with_brackets.rs} | 2 +- ...brackets.stderr => empty_structs_with_brackets.stderr} | 6 +++--- tests/ui/no_effect.rs | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) rename clippy_lints/src/{unit_like_struct_brackets.rs => empty_structs_with_brackets.rs} (92%) rename tests/ui/{unit_like_struct_brackets.fixed => empty_structs_with_brackets.fixed} (92%) rename tests/ui/{unit_like_struct_brackets.rs => empty_structs_with_brackets.rs} (92%) rename tests/ui/{unit_like_struct_brackets.stderr => empty_structs_with_brackets.stderr} (70%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49a44d988139..0aa6a0197624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3263,6 +3263,7 @@ Released 2018-09-13 [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop +[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets [`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant [`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use [`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names @@ -3636,7 +3637,6 @@ Released 2018-09-13 [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp [`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash -[`unit_like_struct_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_like_struct_brackets [`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map diff --git a/clippy_lints/src/unit_like_struct_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs similarity index 92% rename from clippy_lints/src/unit_like_struct_brackets.rs rename to clippy_lints/src/empty_structs_with_brackets.rs index 6269719e696c..e3f33c1a4a30 100644 --- a/clippy_lints/src/unit_like_struct_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -22,13 +22,13 @@ declare_clippy_lint! { /// struct Cookie; /// ``` #[clippy::version = "1.62.0"] - pub UNIT_LIKE_STRUCT_BRACKETS, + pub EMPTY_STRUCTS_WITH_BRACKETS, style, "finds struct declarations with empty brackets" } -declare_lint_pass!(UnitLikeStructBrackets => [UNIT_LIKE_STRUCT_BRACKETS]); +declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]); -impl EarlyLintPass for UnitLikeStructBrackets { +impl EarlyLintPass for EmptyStructsWithBrackets { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { let span_after_ident = item.span.with_lo(item.ident.span.hi()); @@ -37,7 +37,7 @@ impl EarlyLintPass for UnitLikeStructBrackets { && has_no_fields(cx, var_data, span_after_ident) { span_lint_and_then( cx, - UNIT_LIKE_STRUCT_BRACKETS, + EMPTY_STRUCTS_WITH_BRACKETS, span_after_ident, "found empty brackets on struct declaration", |diagnostic| { diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index f488aab46ecc..bdad518a3e83 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -54,6 +54,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(drop_forget_ref::FORGET_COPY), LintId::of(drop_forget_ref::FORGET_REF), LintId::of(duration_subsec::DURATION_SUBSEC), + LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(entry::MAP_ENTRY), LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(enum_variants::ENUM_VARIANT_NAMES), @@ -301,7 +302,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), - LintId::of(unit_like_struct_brackets::UNIT_LIKE_STRUCT_BRACKETS), LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(unit_types::UNIT_ARG), LintId::of(unit_types::UNIT_CMP), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index a1e6ca76a16b..bba3cae45f5e 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -129,6 +129,7 @@ store.register_lints(&[ duration_subsec::DURATION_SUBSEC, else_if_without_else::ELSE_IF_WITHOUT_ELSE, empty_enum::EMPTY_ENUM, + empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS, entry::MAP_ENTRY, enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, enum_variants::ENUM_VARIANT_NAMES, @@ -512,7 +513,6 @@ store.register_lints(&[ unicode::UNICODE_NOT_NFC, uninit_vec::UNINIT_VEC, unit_hash::UNIT_HASH, - unit_like_struct_brackets::UNIT_LIKE_STRUCT_BRACKETS, unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, unit_types::LET_UNIT_VALUE, unit_types::UNIT_ARG, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 89657cfb7842..b2465d1a0cd9 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -20,6 +20,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), LintId::of(doc::NEEDLESS_DOCTEST_MAIN), + LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(enum_variants::ENUM_VARIANT_NAMES), LintId::of(enum_variants::MODULE_INCEPTION), LintId::of(eq_op::OP_REF), @@ -105,7 +106,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(unit_like_struct_brackets::UNIT_LIKE_STRUCT_BRACKETS), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_unit::UNUSED_UNIT), LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index af8d9b3655c5..3138ee9bce5c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -209,6 +209,7 @@ mod drop_forget_ref; mod duration_subsec; mod else_if_without_else; mod empty_enum; +mod empty_structs_with_brackets; mod entry; mod enum_clike; mod enum_variants; @@ -380,7 +381,6 @@ mod undropped_manually_drops; mod unicode; mod uninit_vec; mod unit_hash; -mod unit_like_struct_brackets; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; @@ -870,7 +870,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }) }); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); - store.register_early_pass(|| Box::new(unit_like_struct_brackets::UnitLikeStructBrackets)); + store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/unit_like_struct_brackets.fixed b/tests/ui/empty_structs_with_brackets.fixed similarity index 92% rename from tests/ui/unit_like_struct_brackets.fixed rename to tests/ui/empty_structs_with_brackets.fixed index 2769920c5fe9..80f07603b8d4 100644 --- a/tests/ui/unit_like_struct_brackets.fixed +++ b/tests/ui/empty_structs_with_brackets.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::unit_like_struct_brackets)] +#![warn(clippy::empty_structs_with_brackets)] #![allow(dead_code)] pub struct MyEmptyStruct; // should trigger lint diff --git a/tests/ui/unit_like_struct_brackets.rs b/tests/ui/empty_structs_with_brackets.rs similarity index 92% rename from tests/ui/unit_like_struct_brackets.rs rename to tests/ui/empty_structs_with_brackets.rs index b20f8516bd11..1d1ed4c76902 100644 --- a/tests/ui/unit_like_struct_brackets.rs +++ b/tests/ui/empty_structs_with_brackets.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::unit_like_struct_brackets)] +#![warn(clippy::empty_structs_with_brackets)] #![allow(dead_code)] pub struct MyEmptyStruct {} // should trigger lint diff --git a/tests/ui/unit_like_struct_brackets.stderr b/tests/ui/empty_structs_with_brackets.stderr similarity index 70% rename from tests/ui/unit_like_struct_brackets.stderr rename to tests/ui/empty_structs_with_brackets.stderr index d3037f5d350a..0308cb5571af 100644 --- a/tests/ui/unit_like_struct_brackets.stderr +++ b/tests/ui/empty_structs_with_brackets.stderr @@ -1,14 +1,14 @@ error: found empty brackets on struct declaration - --> $DIR/unit_like_struct_brackets.rs:5:25 + --> $DIR/empty_structs_with_brackets.rs:5:25 | LL | pub struct MyEmptyStruct {} // should trigger lint | ^^^ | - = note: `-D clippy::unit-like-struct-brackets` implied by `-D warnings` + = note: `-D clippy::empty-structs-with-brackets` implied by `-D warnings` = help: remove the brackets error: found empty brackets on struct declaration - --> $DIR/unit_like_struct_brackets.rs:6:26 + --> $DIR/empty_structs_with_brackets.rs:6:26 | LL | struct MyEmptyTupleStruct(); // should trigger lint | ^^^ diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index 291dab4fb314..0847297ade79 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -68,7 +68,7 @@ impl FnOnce<(&str,)> for GreetStruct1 { } } -#[allow(clippy::unit_like_struct_brackets)] +#[allow(clippy::empty_structs_with_brackets)] struct GreetStruct2(); impl FnOnce<(&str,)> for GreetStruct2 { From e552267db33377d913ca1c94c7377b1f5623cbf3 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Mon, 28 Mar 2022 11:45:06 +0200 Subject: [PATCH 49/82] style -> pedantic --- .../src/empty_structs_with_brackets.rs | 2 +- clippy_lints/src/lib.register_all.rs | 1 - clippy_lints/src/lib.register_pedantic.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 - tests/ui/no_effect.rs | 1 - tests/ui/no_effect.stderr | 60 +++++++++---------- 6 files changed, 32 insertions(+), 34 deletions(-) diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs index e3f33c1a4a30..0077cc5364fc 100644 --- a/clippy_lints/src/empty_structs_with_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.62.0"] pub EMPTY_STRUCTS_WITH_BRACKETS, - style, + pedantic, "finds struct declarations with empty brackets" } declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]); diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index bdad518a3e83..1fb3ca1fd9b2 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -54,7 +54,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(drop_forget_ref::FORGET_COPY), LintId::of(drop_forget_ref::FORGET_REF), LintId::of(duration_subsec::DURATION_SUBSEC), - LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(entry::MAP_ENTRY), LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(enum_variants::ENUM_VARIANT_NAMES), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index eb6534cb8cae..3f0f438568fd 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -27,6 +27,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(doc::MISSING_ERRORS_DOC), LintId::of(doc::MISSING_PANICS_DOC), LintId::of(empty_enum::EMPTY_ENUM), + LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(enum_variants::MODULE_NAME_REPETITIONS), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index b2465d1a0cd9..dcf399cf9562 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -20,7 +20,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), LintId::of(doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(enum_variants::ENUM_VARIANT_NAMES), LintId::of(enum_variants::MODULE_INCEPTION), LintId::of(eq_op::OP_REF), diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index 0847297ade79..7ece66a1ccb6 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -68,7 +68,6 @@ impl FnOnce<(&str,)> for GreetStruct1 { } } -#[allow(clippy::empty_structs_with_brackets)] struct GreetStruct2(); impl FnOnce<(&str,)> for GreetStruct2 { diff --git a/tests/ui/no_effect.stderr b/tests/ui/no_effect.stderr index ee57e0d26df8..06b88bb5bee7 100644 --- a/tests/ui/no_effect.stderr +++ b/tests/ui/no_effect.stderr @@ -1,5 +1,5 @@ error: statement with no effect - --> $DIR/no_effect.rs:96:5 + --> $DIR/no_effect.rs:95:5 | LL | 0; | ^^ @@ -7,157 +7,157 @@ LL | 0; = note: `-D clippy::no-effect` implied by `-D warnings` error: statement with no effect - --> $DIR/no_effect.rs:97:5 + --> $DIR/no_effect.rs:96:5 | LL | s2; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:98:5 + --> $DIR/no_effect.rs:97:5 | LL | Unit; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:99:5 + --> $DIR/no_effect.rs:98:5 | LL | Tuple(0); | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:100:5 + --> $DIR/no_effect.rs:99:5 | LL | Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:101:5 + --> $DIR/no_effect.rs:100:5 | LL | Struct { ..s }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:102:5 + --> $DIR/no_effect.rs:101:5 | LL | Union { a: 0 }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:103:5 + --> $DIR/no_effect.rs:102:5 | LL | Enum::Tuple(0); | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:104:5 + --> $DIR/no_effect.rs:103:5 | LL | Enum::Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:105:5 + --> $DIR/no_effect.rs:104:5 | LL | 5 + 6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:106:5 + --> $DIR/no_effect.rs:105:5 | LL | *&42; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:107:5 + --> $DIR/no_effect.rs:106:5 | LL | &6; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:108:5 + --> $DIR/no_effect.rs:107:5 | LL | (5, 6, 7); | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:109:5 + --> $DIR/no_effect.rs:108:5 | LL | box 42; | ^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:110:5 + --> $DIR/no_effect.rs:109:5 | LL | ..; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:111:5 + --> $DIR/no_effect.rs:110:5 | LL | 5..; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:112:5 + --> $DIR/no_effect.rs:111:5 | LL | ..5; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:113:5 + --> $DIR/no_effect.rs:112:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:114:5 + --> $DIR/no_effect.rs:113:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:115:5 + --> $DIR/no_effect.rs:114:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:116:5 + --> $DIR/no_effect.rs:115:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:117:5 + --> $DIR/no_effect.rs:116:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:118:5 + --> $DIR/no_effect.rs:117:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:119:5 + --> $DIR/no_effect.rs:118:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:121:5 + --> $DIR/no_effect.rs:120:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:123:5 + --> $DIR/no_effect.rs:122:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:124:5 + --> $DIR/no_effect.rs:123:5 | LL | let _unused = 1; | ^^^^^^^^^^^^^^^^ @@ -165,19 +165,19 @@ LL | let _unused = 1; = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings` error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:125:5 + --> $DIR/no_effect.rs:124:5 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:126:5 + --> $DIR/no_effect.rs:125:5 | LL | let _duck = Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:127:5 + --> $DIR/no_effect.rs:126:5 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 49a9740b09448ac903a2a1a9b73f09e4ca7fc783 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Wed, 30 Mar 2022 13:08:39 +0200 Subject: [PATCH 50/82] update description Co-authored-by: giraffate --- clippy_lints/src/empty_structs_with_brackets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs index 0077cc5364fc..15c06126fd5d 100644 --- a/clippy_lints/src/empty_structs_with_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -8,7 +8,7 @@ use rustc_span::Span; declare_clippy_lint! { /// ### What it does - /// Finds structs without fields ("unit-like structs") that are declared with brackets. + /// Finds structs without fields (a so-called "empty struct") that are declared with brackets. /// /// ### Why is this bad? /// Empty brackets after a struct declaration can be omitted. From 0434b856ac4253364f2f622df41209412442bb75 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Wed, 30 Mar 2022 13:33:10 +0200 Subject: [PATCH 51/82] pedantic -> restriction --- clippy_lints/src/empty_structs_with_brackets.rs | 2 +- clippy_lints/src/lib.register_pedantic.rs | 1 - clippy_lints/src/lib.register_restriction.rs | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs index 15c06126fd5d..8f2a0306aa05 100644 --- a/clippy_lints/src/empty_structs_with_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.62.0"] pub EMPTY_STRUCTS_WITH_BRACKETS, - pedantic, + restriction, "finds struct declarations with empty brackets" } declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]); diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 3f0f438568fd..eb6534cb8cae 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -27,7 +27,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(doc::MISSING_ERRORS_DOC), LintId::of(doc::MISSING_PANICS_DOC), LintId::of(empty_enum::EMPTY_ENUM), - LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(enum_variants::MODULE_NAME_REPETITIONS), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 6ab139b2fb67..4802dd877e99 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION), LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), LintId::of(exit::EXIT), From 7a80c23f838b832e651a717767c41a2536f3ae42 Mon Sep 17 00:00:00 2001 From: SabrinaJewson Date: Wed, 30 Mar 2022 19:31:50 +0100 Subject: [PATCH 52/82] Suggest from_utf8_unchecked in const contexts --- .../src/transmute/transmute_ref_to_ref.rs | 12 +++++++----- tests/ui/transmute.rs | 7 +++++-- tests/ui/transmute.stderr | 16 +++++++++++----- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 7570bc2a7a8f..9c893cacf4fd 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -32,17 +32,19 @@ pub(super) fn check<'tcx>( "" }; + let snippet = snippet(cx, arg.span, ".."); + span_lint_and_sugg( cx, TRANSMUTE_BYTES_TO_STR, e.span, &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), "consider using", - format!( - "std::str::from_utf8{}({}).unwrap()", - postfix, - snippet(cx, arg.span, ".."), - ), + if const_context { + format!("unsafe {{ std::str::from_utf8_unchecked{postfix}({snippet}) }}") + } else { + format!("std::str::from_utf8{postfix}({snippet}).unwrap()") + }, Applicability::Unspecified, ); triggered = true; diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 54f9727d8ea3..5b688ce4d644 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -134,9 +134,12 @@ mod num_to_bytes { } } -fn bytes_to_str(b: &[u8], mb: &mut [u8]) { - let _: &str = unsafe { std::mem::transmute(b) }; +fn bytes_to_str(mb: &mut [u8]) { + const B: &[u8] = b""; + + let _: &str = unsafe { std::mem::transmute(B) }; let _: &mut str = unsafe { std::mem::transmute(mb) }; + const _: &str = unsafe { std::mem::transmute(B) }; } fn main() {} diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 5c60fc2d5115..1213d192ae9c 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -227,18 +227,24 @@ LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:138:28 + --> $DIR/transmute.rs:140:28 | -LL | let _: &str = unsafe { std::mem::transmute(b) }; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` +LL | let _: &str = unsafe { std::mem::transmute(B) }; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` | = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:139:32 + --> $DIR/transmute.rs:141:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 38 previous errors +error: transmute from a `&[u8]` to a `&str` + --> $DIR/transmute.rs:142:30 + | +LL | const _: &str = unsafe { std::mem::transmute(B) }; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `unsafe { std::str::from_utf8_unchecked(B) }` + +error: aborting due to 39 previous errors From 11045f94e267805e90662577ee7486cba5dbcf18 Mon Sep 17 00:00:00 2001 From: SabrinaJewson Date: Fri, 1 Apr 2022 06:32:22 +0100 Subject: [PATCH 53/82] Don't unnecessarily suggest unsafe block --- clippy_lints/src/transmute/transmute_ref_to_ref.rs | 2 +- tests/ui/transmute.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 9c893cacf4fd..fe3e88613f4e 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -41,7 +41,7 @@ pub(super) fn check<'tcx>( &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), "consider using", if const_context { - format!("unsafe {{ std::str::from_utf8_unchecked{postfix}({snippet}) }}") + format!("std::str::from_utf8_unchecked{postfix}({snippet})") } else { format!("std::str::from_utf8{postfix}({snippet}).unwrap()") }, diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 1213d192ae9c..72a386e8fa61 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -244,7 +244,7 @@ error: transmute from a `&[u8]` to a `&str` --> $DIR/transmute.rs:142:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `unsafe { std::str::from_utf8_unchecked(B) }` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` error: aborting due to 39 previous errors From 3bbb3e33295f4de11efb904192df1a3764d89e8d Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Fri, 1 Apr 2022 00:04:19 -0600 Subject: [PATCH 54/82] single_element_loop: handle arrays for Edition2021 also handle `.iter_mut()`, `.into_iter()`, and wrapping in parens if necessary --- clippy_lints/src/loops/single_element_loop.rs | 70 ++++++++++++++++-- tests/ui/single_element_loop.fixed | 24 +++++- tests/ui/single_element_loop.rs | 20 ++++- tests/ui/single_element_loop.stderr | 74 +++++++++++++++++-- 4 files changed, 172 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index 36ecd83f7d64..a0bd7ad0ac64 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -2,9 +2,12 @@ use super::SINGLE_ELEMENT_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet_with_applicability}; use if_chain::if_chain; +use rustc_ast::util::parser::PREC_PREFIX; +use rustc_ast::Mutability; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, Pat}; +use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat}; use rustc_lint::LateContext; +use rustc_span::edition::Edition; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -13,31 +16,84 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { - let arg_expr = match arg.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg, - ExprKind::MethodCall(method, [arg], _) if method.ident.name == rustc_span::sym::iter => arg, + let (arg_expression, prefix) = match arg.kind { + ExprKind::AddrOf( + BorrowKind::Ref, + Mutability::Not, + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ) => (arg, "&"), + ExprKind::AddrOf( + BorrowKind::Ref, + Mutability::Mut, + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ) => (arg, "&mut "), + ExprKind::MethodCall( + method, + [ + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ], + _, + ) if method.ident.name == rustc_span::sym::iter => (arg, "&"), + ExprKind::MethodCall( + method, + [ + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ], + _, + ) if method.ident.name.as_str() == "iter_mut" => (arg, "&mut "), + ExprKind::MethodCall( + method, + [ + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ], + _, + ) if method.ident.name == rustc_span::sym::into_iter => (arg, ""), + // Only check for arrays edition 2021 or later, as this case will trigger a compiler error otherwise. + ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""), _ => return, }; if_chain! { - if let ExprKind::Array([arg_expression]) = arg_expr.kind; if let ExprKind::Block(block, _) = body.kind; if !block.stmts.is_empty(); then { let mut applicability = Applicability::MachineApplicable; let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); - let arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); + let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned(); block_str.remove(0); block_str.pop(); let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)); + // Reference iterator from `&(mut) []` or `[].iter(_mut)()`. + if !prefix.is_empty() && ( + // Precedence of internal expression is less than or equal to precedence of `&expr`. + arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression) + ) { + arg_snip = format!("({arg_snip})").into(); + } + span_lint_and_sugg( cx, SINGLE_ELEMENT_LOOP, expr.span, "for loop over a single element", "try", - format!("{{\n{}let {} = &{};{}}}", indent, pat_snip, arg_snip, block_str), + format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"), applicability, ) } diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed index c307afffcb86..63d31ff83f9b 100644 --- a/tests/ui/single_element_loop.fixed +++ b/tests/ui/single_element_loop.fixed @@ -6,11 +6,31 @@ fn main() { let item1 = 2; { let item = &item1; - println!("{}", item); + dbg!(item); } { let item = &item1; - println!("{:?}", item); + dbg!(item); + } + + { + let item = &(0..5); + dbg!(item); + } + + { + let item = &mut (0..5); + dbg!(item); + } + + { + let item = 0..5; + dbg!(item); + } + + { + let item = 0..5; + dbg!(item); } } diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs index 2c0c03b72119..2cda5a329d25 100644 --- a/tests/ui/single_element_loop.rs +++ b/tests/ui/single_element_loop.rs @@ -5,10 +5,26 @@ fn main() { let item1 = 2; for item in &[item1] { - println!("{}", item); + dbg!(item); } for item in [item1].iter() { - println!("{:?}", item); + dbg!(item); + } + + for item in &[0..5] { + dbg!(item); + } + + for item in [0..5].iter_mut() { + dbg!(item); + } + + for item in [0..5] { + dbg!(item); + } + + for item in [0..5].into_iter() { + dbg!(item); } } diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr index f52ca8c5a9b0..0aeb8da1a2e2 100644 --- a/tests/ui/single_element_loop.stderr +++ b/tests/ui/single_element_loop.stderr @@ -2,7 +2,7 @@ error: for loop over a single element --> $DIR/single_element_loop.rs:7:5 | LL | / for item in &[item1] { -LL | | println!("{}", item); +LL | | dbg!(item); LL | | } | |_____^ | @@ -11,7 +11,7 @@ help: try | LL ~ { LL + let item = &item1; -LL + println!("{}", item); +LL + dbg!(item); LL + } | @@ -19,7 +19,7 @@ error: for loop over a single element --> $DIR/single_element_loop.rs:11:5 | LL | / for item in [item1].iter() { -LL | | println!("{:?}", item); +LL | | dbg!(item); LL | | } | |_____^ | @@ -27,9 +27,73 @@ help: try | LL ~ { LL + let item = &item1; -LL + println!("{:?}", item); +LL + dbg!(item); LL + } | -error: aborting due to 2 previous errors +error: for loop over a single element + --> $DIR/single_element_loop.rs:15:5 + | +LL | / for item in &[0..5] { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = &(0..5); +LL + dbg!(item); +LL + } + | + +error: for loop over a single element + --> $DIR/single_element_loop.rs:19:5 + | +LL | / for item in [0..5].iter_mut() { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = &mut (0..5); +LL + dbg!(item); +LL + } + | + +error: for loop over a single element + --> $DIR/single_element_loop.rs:23:5 + | +LL | / for item in [0..5] { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = 0..5; +LL + dbg!(item); +LL + } + | + +error: for loop over a single element + --> $DIR/single_element_loop.rs:27:5 + | +LL | / for item in [0..5].into_iter() { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = 0..5; +LL + dbg!(item); +LL + } + | + +error: aborting due to 6 previous errors From b3f8415032a4b86f72aa7a99c7fe62a25c302927 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 28 Mar 2022 22:08:04 -0400 Subject: [PATCH 55/82] Remove regex dependency from clippy_dev --- clippy_dev/Cargo.toml | 6 +- clippy_dev/src/lib.rs | 3 + clippy_dev/src/update_lints.rs | 653 ++++++++++++++------------------- 3 files changed, 280 insertions(+), 382 deletions(-) diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index d133e8cddabc..80423ea624fe 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -9,10 +9,14 @@ clap = "2.33" indoc = "1.0" itertools = "0.10.1" opener = "0.5" -regex = "1.5" shell-escape = "0.1" walkdir = "2.3" cargo_metadata = "0.14" + [features] deny-warnings = [] + +[package.metadata.rust-analyzer] +# This package uses #[feature(rustc_private)] +rustc_private = true diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 59fde4475471..f9e0f2ff69c2 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,8 +1,11 @@ #![feature(once_cell)] +#![feature(rustc_private)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] +extern crate rustc_lexer; + use std::path::PathBuf; pub mod bless; diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index d368ef1f46a2..4e48b670457e 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,9 +1,9 @@ +use core::fmt::Write; use itertools::Itertools; -use regex::Regex; +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use std::collections::HashMap; use std::ffi::OsStr; use std::fs; -use std::lazy::SyncLazy; use std::path::Path; use walkdir::WalkDir; @@ -13,35 +13,7 @@ const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev u // Use that command to update this file and do not edit by hand.\n\ // Manual edits will be overwritten.\n\n"; -static DEC_CLIPPY_LINT_RE: SyncLazy = SyncLazy::new(|| { - Regex::new( - r#"(?x) - declare_clippy_lint!\s*[\{(] - (?:\s+///.*)* - (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])? - \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* - (?P[a-z_]+)\s*,\s* - "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] -"#, - ) - .unwrap() -}); - -static DEC_DEPRECATED_LINT_RE: SyncLazy = SyncLazy::new(|| { - Regex::new( - r#"(?x) - declare_deprecated_lint!\s*[{(]\s* - (?:\s+///.*)* - (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])? - \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* - "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] -"#, - ) - .unwrap() -}); -static NL_ESCAPE_RE: SyncLazy = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap()); - -static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; +const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; #[derive(Clone, Copy, PartialEq)] pub enum UpdateMode { @@ -60,60 +32,52 @@ pub enum UpdateMode { /// Panics if a file path could not read from or then written to #[allow(clippy::too_many_lines)] pub fn run(update_mode: UpdateMode) { - let lint_list: Vec = gather_all().collect(); + let (lints, deprecated_lints) = gather_all(); - let internal_lints = Lint::internal_lints(&lint_list); - let deprecated_lints = Lint::deprecated_lints(&lint_list); - let usable_lints = Lint::usable_lints(&lint_list); + let internal_lints = Lint::internal_lints(&lints); + let usable_lints = Lint::usable_lints(&lints); let mut sorted_usable_lints = usable_lints.clone(); sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); - let usable_lint_count = round_to_fifty(usable_lints.len()); - - let mut file_change = false; - - file_change |= replace_region_in_file( + replace_region_in_file( + update_mode, Path::new("README.md"), - &format!( - r#"\[There are over \d+ lints included in this crate!\]\({}\)"#, - DOCS_LINK - ), - "", - true, - update_mode == UpdateMode::Change, - || { - vec![format!( - "[There are over {} lints included in this crate!]({})", - usable_lint_count, DOCS_LINK - )] + "[There are over ", + " lints included in this crate!]", + |res| { + write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap(); }, - ) - .changed; + ); - file_change |= replace_region_in_file( + replace_region_in_file( + update_mode, Path::new("CHANGELOG.md"), - "", + "\n", "", - false, - update_mode == UpdateMode::Change, - || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())), - ) - .changed; + |res| { + for lint in usable_lints + .iter() + .map(|l| &l.name) + .chain(deprecated_lints.iter().map(|l| &l.name)) + .sorted() + { + writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap(); + } + }, + ); // This has to be in lib.rs, otherwise rustfmt doesn't work - file_change |= replace_region_in_file( + replace_region_in_file( + update_mode, Path::new("clippy_lints/src/lib.rs"), - "begin lints modules", - "end lints modules", - false, - update_mode == UpdateMode::Change, - || gen_modules_list(usable_lints.iter()), - ) - .changed; - - if file_change && update_mode == UpdateMode::Check { - exit_with_failure(); - } + "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", + "// end lints modules, do not remove this comment, it’s used in `update_lints`", + |res| { + for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() { + writeln!(res, "mod {};", lint_mod).unwrap(); + } + }, + ); process_file( "clippy_lints/src/lib.register_lints.rs", @@ -123,7 +87,7 @@ pub fn run(update_mode: UpdateMode) { process_file( "clippy_lints/src/lib.deprecated.rs", update_mode, - &gen_deprecated(deprecated_lints.iter()), + &gen_deprecated(&deprecated_lints), ); let all_group_lints = usable_lints.iter().filter(|l| { @@ -146,15 +110,12 @@ pub fn run(update_mode: UpdateMode) { } pub fn print_lints() { - let lint_list: Vec = gather_all().collect(); + let (lint_list, _) = gather_all(); let usable_lints = Lint::usable_lints(&lint_list); let usable_lint_count = usable_lints.len(); let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); for (lint_group, mut lints) in grouped_by_lint_group { - if lint_group == "Deprecated" { - continue; - } println!("\n## {}", lint_group); lints.sort_by_key(|l| l.name.clone()); @@ -198,19 +159,17 @@ struct Lint { name: String, group: String, desc: String, - deprecation: Option, module: String, } impl Lint { #[must_use] - fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self { + fn new(name: &str, group: &str, desc: &str, module: &str) -> Self { Self { name: name.to_lowercase(), - group: group.to_string(), - desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(), - deprecation: deprecation.map(ToString::to_string), - module: module.to_string(), + group: group.into(), + desc: remove_line_splices(desc), + module: module.into(), } } @@ -219,7 +178,7 @@ impl Lint { fn usable_lints(lints: &[Self]) -> Vec { lints .iter() - .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal")) + .filter(|l| !l.group.starts_with("internal")) .cloned() .collect() } @@ -230,12 +189,6 @@ impl Lint { lints.iter().filter(|l| l.group == "internal").cloned().collect() } - /// Returns all deprecated lints - #[must_use] - fn deprecated_lints(lints: &[Self]) -> Vec { - lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect() - } - /// Returns the lints in a `HashMap`, grouped by the different lint groups #[must_use] fn by_lint_group(lints: impl Iterator) -> HashMap> { @@ -243,6 +196,20 @@ impl Lint { } } +#[derive(Clone, PartialEq, Debug)] +struct DeprecatedLint { + name: String, + reason: String, +} +impl DeprecatedLint { + fn new(name: &str, reason: &str) -> Self { + Self { + name: name.to_lowercase(), + reason: remove_line_splices(reason), + } + } +} + /// Generates the code for registering a group fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator) -> String { let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); @@ -262,32 +229,12 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator(lints: impl Iterator) -> Vec { - lints - .map(|l| &l.module) - .unique() - .map(|module| format!("mod {};", module)) - .sorted() - .collect::>() -} - -/// Generates the list of lint links at the bottom of the CHANGELOG -#[must_use] -fn gen_changelog_lint_list<'a>(lints: impl Iterator) -> Vec { - lints - .sorted_by_key(|l| &l.name) - .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name)) - .collect() -} - /// Generates the `register_removed` code #[must_use] -fn gen_deprecated<'a>(lints: impl Iterator) -> String { +fn gen_deprecated(lints: &[DeprecatedLint]) -> String { let mut output = GENERATED_FILE_COMMENT.to_string(); output.push_str("{\n"); - for Lint { name, deprecation, .. } in lints { + for lint in lints { output.push_str(&format!( concat!( " store.register_removed(\n", @@ -295,8 +242,7 @@ fn gen_deprecated<'a>(lints: impl Iterator) -> String { " \"{}\",\n", " );\n" ), - name, - deprecation.as_ref().expect("`lints` are deprecated") + lint.name, lint.reason, )); } output.push_str("}\n"); @@ -330,61 +276,133 @@ fn gen_register_lint_list<'a>( output } -/// Gathers all files in `src/clippy_lints` and gathers all lints inside -fn gather_all() -> impl Iterator { - lint_files().flat_map(|f| gather_from_file(&f)) -} +/// Gathers all lints defined in `clippy_lints/src` +fn gather_all() -> (Vec, Vec) { + let mut lints = Vec::with_capacity(1000); + let mut deprecated_lints = Vec::with_capacity(50); + let root_path = clippy_project_root().join("clippy_lints/src"); -fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator { - let content = fs::read_to_string(dir_entry.path()).unwrap(); - let path = dir_entry.path(); - let filename = path.file_stem().unwrap(); - let path_buf = path.with_file_name(filename); - let mut rel_path = path_buf - .strip_prefix(clippy_project_root().join("clippy_lints/src")) - .expect("only files in `clippy_lints/src` should be looked at"); - // If the lints are stored in mod.rs, we get the module name from - // the containing directory: - if filename == "mod" { - rel_path = rel_path.parent().unwrap(); - } - - let module = rel_path - .components() - .map(|c| c.as_os_str().to_str().unwrap()) - .collect::>() - .join("::"); - - parse_contents(&content, &module) -} - -fn parse_contents(content: &str, module: &str) -> impl Iterator { - let lints = DEC_CLIPPY_LINT_RE - .captures_iter(content) - .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module)); - let deprecated = DEC_DEPRECATED_LINT_RE - .captures_iter(content) - .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module)); - // Removing the `.collect::>().into_iter()` causes some lifetime issues due to the map - lints.chain(deprecated).collect::>().into_iter() -} - -/// Collects all .rs files in the `clippy_lints/src` directory -fn lint_files() -> impl Iterator { - // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories. - // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`. - let path = clippy_project_root().join("clippy_lints/src"); - WalkDir::new(path) + for (rel_path, file) in WalkDir::new(&root_path) .into_iter() - .filter_map(Result::ok) + .map(Result::unwrap) .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) + .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) + { + let path = file.path(); + let contents = + fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); + let module = rel_path + .components() + .map(|c| c.as_os_str().to_str().unwrap()) + .collect::>() + .join("::"); + + // If the lints are stored in mod.rs, we get the module name from + // the containing directory: + let module = if let Some(module) = module.strip_suffix("::mod.rs") { + module + } else { + module.strip_suffix(".rs").unwrap_or(&module) + }; + + if module == "deprecated_lints" { + parse_deprecated_contents(&contents, &mut deprecated_lints); + } else { + parse_contents(&contents, module, &mut lints); + } + } + (lints, deprecated_lints) } -/// Whether a file has had its text changed or not -#[derive(PartialEq, Debug)] -struct FileChange { - changed: bool, - new_lines: String, +macro_rules! match_tokens { + ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => { + { + $($(let $capture =)? if let Some((TokenKind::$token $({$($fields)*})?, _x)) = $iter.next() { + _x + } else { + continue; + };)* + #[allow(clippy::unused_unit)] + { ($($($capture,)?)*) } + } + } +} + +/// Parse a source file looking for `declare_clippy_lint` macro invocations. +fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { + let mut offset = 0usize; + let mut iter = tokenize(contents).map(|t| { + let range = offset..offset + t.len; + offset = range.end; + (t.kind, &contents[range]) + }); + + while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_clippy_lint") { + let mut iter = iter + .by_ref() + .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); + // matches `!{` + match_tokens!(iter, Bang OpenBrace); + match iter.next() { + // #[clippy::version = "version"] pub + Some((TokenKind::Pound, _)) => { + match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident); + }, + // pub + Some((TokenKind::Ident, _)) => (), + _ => continue, + } + let (name, group, desc) = match_tokens!( + iter, + // LINT_NAME + Ident(name) Comma + // group, + Ident(group) Comma + // "description" } + Literal{kind: LiteralKind::Str{..}, ..}(desc) CloseBrace + ); + lints.push(Lint::new(name, group, desc, module)); + } +} + +/// Parse a source file looking for `declare_deprecated_lint` macro invocations. +fn parse_deprecated_contents(contents: &str, lints: &mut Vec) { + let mut offset = 0usize; + let mut iter = tokenize(contents).map(|t| { + let range = offset..offset + t.len; + offset = range.end; + (t.kind, &contents[range]) + }); + while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_deprecated_lint") { + let mut iter = iter + .by_ref() + .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); + let (name, reason) = match_tokens!( + iter, + // !{ + Bang OpenBrace + // #[clippy::version = "version"] + Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket + // pub LINT_NAME, + Ident Ident(name) Comma + // "description" + Literal{kind: LiteralKind::Str{..},..}(reason) + // } + CloseBrace + ); + lints.push(DeprecatedLint::new(name, reason)); + } +} + +/// Removes the line splices and surrounding quotes from a string literal +fn remove_line_splices(s: &str) -> String { + let s = s + .strip_prefix('"') + .and_then(|s| s.strip_suffix('"')) + .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s)); + let mut res = String::with_capacity(s.len()); + unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, _| res.push_str(&s[range])); + res } /// Replaces a region in a file delimited by two lines matching regexes. @@ -396,144 +414,49 @@ struct FileChange { /// # Panics /// /// Panics if the path could not read or then written -fn replace_region_in_file( +fn replace_region_in_file( + update_mode: UpdateMode, path: &Path, start: &str, end: &str, - replace_start: bool, - write_back: bool, - replacements: F, -) -> FileChange -where - F: FnOnce() -> Vec, -{ - let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e)); - let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements); + write_replacement: impl FnMut(&mut String), +) { + let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); + let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) { + Ok(x) => x, + Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()), + }; - if write_back { - if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) { - panic!("Cannot write to {}: {}", path.display(), e); - } - } - file_change -} - -/// Replaces a region in a text delimited by two lines matching regexes. -/// -/// * `text` is the input text on which you want to perform the replacement -/// * `start` is a `&str` that describes the delimiter line before the region you want to replace. -/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. -/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen. -/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. -/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end` -/// delimiter line is never replaced. -/// * `replacements` is a closure that has to return a `Vec` which contains the new text. -/// -/// If you want to perform the replacement on files instead of already parsed text, -/// use `replace_region_in_file`. -/// -/// # Example -/// -/// ```ignore -/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end"; -/// let result = -/// replace_region_in_text(the_text, "replace_start", "replace_end", false, || { -/// vec!["a different".to_string(), "text".to_string()] -/// }) -/// .new_lines; -/// assert_eq!("replace_start\na different\ntext\nreplace_end", result); -/// ``` -/// -/// # Panics -/// -/// Panics if start or end is not valid regex -fn replace_region_in_text(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange -where - F: FnOnce() -> Vec, -{ - let replace_it = replacements(); - let mut in_old_region = false; - let mut found = false; - let mut new_lines = vec![]; - let start = Regex::new(start).unwrap(); - let end = Regex::new(end).unwrap(); - - for line in text.lines() { - if in_old_region { - if end.is_match(line) { - in_old_region = false; - new_lines.extend(replace_it.clone()); - new_lines.push(line.to_string()); + match update_mode { + UpdateMode::Check if contents != new_contents => exit_with_failure(), + UpdateMode::Check => (), + UpdateMode::Change => { + if let Err(e) = fs::write(path, new_contents.as_bytes()) { + panic!("Cannot write to `{}`: {}", path.display(), e); } - } else if start.is_match(line) { - if !replace_start { - new_lines.push(line.to_string()); - } - in_old_region = true; - found = true; - } else { - new_lines.push(line.to_string()); - } + }, } - - if !found { - // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the - // given text or file. Most likely this is an error on the programmer's side and the Regex - // is incorrect. - eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start); - std::process::exit(1); - } - - let mut new_lines = new_lines.join("\n"); - if text.ends_with('\n') { - new_lines.push('\n'); - } - let changed = new_lines != text; - FileChange { changed, new_lines } } -#[test] -fn test_parse_contents() { - let result: Vec = parse_contents( - r#" -declare_clippy_lint! { - #[clippy::version = "Hello Clippy!"] - pub PTR_ARG, - style, - "really long \ - text" -} +/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters +/// were found, or the missing delimiter if not. +fn replace_region_in_text<'a>( + text: &str, + start: &'a str, + end: &'a str, + mut write_replacement: impl FnMut(&mut String), +) -> Result { + let (text_start, rest) = text.split_once(start).ok_or(start)?; + let (_, text_end) = rest.split_once(end).ok_or(end)?; -declare_clippy_lint!{ - #[clippy::version = "Test version"] - pub DOC_MARKDOWN, - pedantic, - "single line" -} + let mut res = String::with_capacity(text.len() + 4096); + res.push_str(text_start); + res.push_str(start); + write_replacement(&mut res); + res.push_str(end); + res.push_str(text_end); -/// some doc comment -declare_deprecated_lint! { - #[clippy::version = "I'm a version"] - pub SHOULD_ASSERT_EQ, - "`assert!()` will be more flexible with RFC 2011" -} - "#, - "module_name", - ) - .collect(); - - let expected = vec![ - Lint::new("ptr_arg", "style", "really long text", None, "module_name"), - Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"), - Lint::new( - "should_assert_eq", - "Deprecated", - "`assert!()` will be more flexible with RFC 2011", - Some("`assert!()` will be more flexible with RFC 2011"), - "module_name", - ), - ]; - assert_eq!(expected, result); + Ok(res) } #[cfg(test)] @@ -541,55 +464,65 @@ mod tests { use super::*; #[test] - fn test_replace_region() { - let text = "\nabc\n123\n789\ndef\nghi"; - let expected = FileChange { - changed: true, - new_lines: "\nabc\nhello world\ndef\nghi".to_string(), - }; - let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || { - vec!["hello world".to_string()] - }); + fn test_parse_contents() { + static CONTENTS: &str = r#" + declare_clippy_lint! { + #[clippy::version = "Hello Clippy!"] + pub PTR_ARG, + style, + "really long \ + text" + } + + declare_clippy_lint!{ + #[clippy::version = "Test version"] + pub DOC_MARKDOWN, + pedantic, + "single line" + } + "#; + let mut result = Vec::new(); + parse_contents(CONTENTS, "module_name", &mut result); + + let expected = vec![ + Lint::new("ptr_arg", "style", "\"really long text\"", "module_name"), + Lint::new("doc_markdown", "pedantic", "\"single line\"", "module_name"), + ]; assert_eq!(expected, result); } #[test] - fn test_replace_region_with_start() { - let text = "\nabc\n123\n789\ndef\nghi"; - let expected = FileChange { - changed: true, - new_lines: "\nhello world\ndef\nghi".to_string(), - }; - let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || { - vec!["hello world".to_string()] - }); - assert_eq!(expected, result); - } + fn test_parse_deprecated_contents() { + static DEPRECATED_CONTENTS: &str = r#" + /// some doc comment + declare_deprecated_lint! { + #[clippy::version = "I'm a version"] + pub SHOULD_ASSERT_EQ, + "`assert!()` will be more flexible with RFC 2011" + } + "#; - #[test] - fn test_replace_region_no_changes() { - let text = "123\n456\n789"; - let expected = FileChange { - changed: false, - new_lines: "123\n456\n789".to_string(), - }; - let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new); + let mut result = Vec::new(); + parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result); + + let expected = vec![DeprecatedLint::new( + "should_assert_eq", + "\"`assert!()` will be more flexible with RFC 2011\"", + )]; assert_eq!(expected, result); } #[test] fn test_usable_lints() { let lints = vec![ - Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"), - Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"), + Lint::new("should_assert_eq2", "Not Deprecated", "\"abc\"", "module_name"), + Lint::new("should_assert_eq2", "internal", "\"abc\"", "module_name"), + Lint::new("should_assert_eq2", "internal_style", "\"abc\"", "module_name"), ]; let expected = vec![Lint::new( "should_assert_eq2", "Not Deprecated", - "abc", - None, + "\"abc\"", "module_name", )]; assert_eq!(expected, Lint::usable_lints(&lints)); @@ -598,55 +531,30 @@ mod tests { #[test] fn test_by_lint_group() { let lints = vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"), - Lint::new("incorrect_match", "group1", "abc", None, "module_name"), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"), + Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name"), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"), ]; let mut expected: HashMap> = HashMap::new(); expected.insert( "group1".to_string(), vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("incorrect_match", "group1", "abc", None, "module_name"), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"), ], ); expected.insert( "group2".to_string(), - vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")], + vec![Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name")], ); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); } - #[test] - fn test_gen_changelog_lint_list() { - let lints = vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"), - ]; - let expected = vec![ - format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK), - format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK), - ]; - assert_eq!(expected, gen_changelog_lint_list(lints.iter())); - } - #[test] fn test_gen_deprecated() { let lints = vec![ - Lint::new( - "should_assert_eq", - "group1", - "abc", - Some("has been superseded by should_assert_eq2"), - "module_name", - ), - Lint::new( - "another_deprecated", - "group2", - "abc", - Some("will be removed"), - "module_name", - ), + DeprecatedLint::new("should_assert_eq", "\"has been superseded by should_assert_eq2\""), + DeprecatedLint::new("another_deprecated", "\"will be removed\""), ]; let expected = GENERATED_FILE_COMMENT.to_string() @@ -665,32 +573,15 @@ mod tests { .join("\n") + "\n"; - assert_eq!(expected, gen_deprecated(lints.iter())); - } - - #[test] - #[should_panic] - fn test_gen_deprecated_fail() { - let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")]; - let _deprecated_lints = gen_deprecated(lints.iter()); - } - - #[test] - fn test_gen_modules_list() { - let lints = vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"), - ]; - let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()]; - assert_eq!(expected, gen_modules_list(lints.iter())); + assert_eq!(expected, gen_deprecated(&lints)); } #[test] fn test_gen_lint_group_list() { let lints = vec![ - Lint::new("abc", "group1", "abc", None, "module_name"), - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("internal", "internal_style", "abc", None, "module_name"), + Lint::new("abc", "group1", "\"abc\"", "module_name"), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"), + Lint::new("internal", "internal_style", "\"abc\"", "module_name"), ]; let expected = GENERATED_FILE_COMMENT.to_string() + &[ From 7025283f3ec649f0041c81a7e8269d38e0fc21ba Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 29 Mar 2022 08:32:12 -0400 Subject: [PATCH 56/82] Remove cargo_metadata dependency from clippy_dev --- clippy_dev/Cargo.toml | 3 --- clippy_dev/src/lib.rs | 1 + clippy_dev/src/new_lint.rs | 24 ++++++++++++++++-------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 80423ea624fe..1f2d8adecee7 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -4,15 +4,12 @@ version = "0.0.1" edition = "2021" [dependencies] -bytecount = "0.6" clap = "2.33" indoc = "1.0" itertools = "0.10.1" opener = "0.5" shell-escape = "0.1" walkdir = "2.3" -cargo_metadata = "0.14" - [features] deny-warnings = [] diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index f9e0f2ff69c2..414b403827d9 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(let_else)] #![feature(once_cell)] #![feature(rustc_private)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 59658b42c79b..7a3fd1317619 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -133,15 +133,23 @@ fn to_camel_case(name: &str) -> String { } fn get_stabilisation_version() -> String { - let mut command = cargo_metadata::MetadataCommand::new(); - command.no_deps(); - if let Ok(metadata) = command.exec() { - if let Some(pkg) = metadata.packages.iter().find(|pkg| pkg.name == "clippy") { - return format!("{}.{}.0", pkg.version.minor, pkg.version.patch); - } + fn parse_manifest(contents: &str) -> Option { + let version = contents + .lines() + .filter_map(|l| l.split_once('=')) + .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?; + let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else { + return None; + }; + let (minor, patch) = version.split_once('.')?; + Some(format!( + "{}.{}.0", + minor.parse::().ok()?, + patch.parse::().ok()? + )) } - - String::from("") + let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`"); + parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`") } fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { From ae5af0cd1a3f59cc3537ade7dfba06989969b918 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 29 Mar 2022 08:57:02 -0400 Subject: [PATCH 57/82] Remove cargo_metadata dependency from clippy --- Cargo.toml | 5 ++--- tests/versioncheck.rs | 34 ++++++++++++++++------------------ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 123af23881b6..814bfeaf8d46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,13 +21,12 @@ name = "clippy-driver" path = "src/driver.rs" [dependencies] -clippy_lints = { version = "0.1", path = "clippy_lints" } +clippy_lints = { path = "clippy_lints" } semver = "1.0" -rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } +rustc_tools_util = { path = "rustc_tools_util" } tempfile = { version = "3.2", optional = true } [dev-dependencies] -cargo_metadata = "0.14" compiletest_rs = { version = "0.7.1", features = ["tmp"] } tester = "0.9" regex = "1.5" diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 77102b8cac0c..38498ebdcf2c 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -3,34 +3,32 @@ #![allow(clippy::single_match_else)] use rustc_tools_util::VersionInfo; +use std::fs; #[test] fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { + fn read_version(path: &str) -> String { + let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{}`: {:?}", path, e)); + contents + .lines() + .filter_map(|l| l.split_once('=')) + .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim())) + .unwrap_or_else(|| panic!("error finding version in `{}`", path)) + .to_string() + } + // do not run this test inside the upstream rustc repo: // https://github.com/rust-lang/rust-clippy/issues/6683 if option_env!("RUSTC_TEST_SUITE").is_some() { return; } - let clippy_meta = cargo_metadata::MetadataCommand::new() - .no_deps() - .exec() - .expect("could not obtain cargo metadata"); + let clippy_version = read_version("Cargo.toml"); + let clippy_lints_version = read_version("clippy_lints/Cargo.toml"); + let clippy_utils_version = read_version("clippy_utils/Cargo.toml"); - for krate in &["clippy_lints", "clippy_utils"] { - let krate_meta = cargo_metadata::MetadataCommand::new() - .current_dir(std::env::current_dir().unwrap().join(krate)) - .no_deps() - .exec() - .expect("could not obtain cargo metadata"); - assert_eq!(krate_meta.packages[0].version, clippy_meta.packages[0].version); - for package in &clippy_meta.packages[0].dependencies { - if package.name == *krate { - assert!(package.req.matches(&krate_meta.packages[0].version)); - break; - } - } - } + assert_eq!(clippy_version, clippy_lints_version); + assert_eq!(clippy_version, clippy_utils_version); } #[test] From 17c8bee95a43cf9c39ec766cdaa8c6219f4126dd Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 2 Apr 2022 00:46:45 -0400 Subject: [PATCH 58/82] Add a couple of examples to `undocumented_unsafe_blocks` --- .../src/undocumented_unsafe_blocks.rs | 18 +++++++ tests/ui/undocumented_unsafe_blocks.rs | 5 ++ tests/ui/undocumented_unsafe_blocks.stderr | 48 +++++++++++-------- 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index ac13a309e7b8..c0947685fa1d 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -15,6 +15,24 @@ declare_clippy_lint! { /// explaining why the unsafe operations performed inside /// the block are safe. /// + /// Note the comment must appear on the line(s) preceding the unsafe block + /// with nothing appearing in between. The following is ok: + /// ```ignore + /// foo( + /// // SAFETY: + /// // This is a valid safety comment + /// unsafe { *x } + /// ) + /// ``` + /// But neither of these are: + /// ```ignore + /// // SAFETY: + /// // This is not a valid safety comment + /// foo( + /// /* SAFETY: Neither is this */ unsafe { *x }, + /// ); + /// ``` + /// /// ### Why is this bad? /// Undocumented unsafe blocks can make it difficult to /// read and maintain code, as well as uncover unsoundness diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index 80fb198ab538..afa337c45f41 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -251,6 +251,11 @@ fn from_proc_macro() { // Invalid comments +#[rustfmt::skip] +fn inline_block_comment() { + /* Safety: */ unsafe {} +} + fn no_comment() { unsafe {} } diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 746bbe2f1414..856a07fd3168 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -1,14 +1,22 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:255:5 + --> $DIR/undocumented_unsafe_blocks.rs:256:19 | -LL | unsafe {} - | ^^^^^^^^^ +LL | /* Safety: */ unsafe {} + | ^^^^^^^^^ | = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:259:14 + --> $DIR/undocumented_unsafe_blocks.rs:260:5 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:264:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -16,7 +24,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:259:29 + --> $DIR/undocumented_unsafe_blocks.rs:264:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -24,7 +32,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:259:48 + --> $DIR/undocumented_unsafe_blocks.rs:264:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -32,7 +40,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:263:18 + --> $DIR/undocumented_unsafe_blocks.rs:268:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -40,7 +48,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:263:37 + --> $DIR/undocumented_unsafe_blocks.rs:268:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -48,7 +56,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:267:14 + --> $DIR/undocumented_unsafe_blocks.rs:272:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -56,7 +64,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:272:19 + --> $DIR/undocumented_unsafe_blocks.rs:277:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -64,7 +72,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:278:14 + --> $DIR/undocumented_unsafe_blocks.rs:283:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -72,7 +80,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:282:14 + --> $DIR/undocumented_unsafe_blocks.rs:287:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -80,7 +88,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:286:13 + --> $DIR/undocumented_unsafe_blocks.rs:291:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -88,7 +96,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:296:8 + --> $DIR/undocumented_unsafe_blocks.rs:301:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -96,7 +104,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:302:13 + --> $DIR/undocumented_unsafe_blocks.rs:307:13 | LL | unsafe {} | ^^^^^^^^^ @@ -108,7 +116,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:310:5 + --> $DIR/undocumented_unsafe_blocks.rs:315:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -116,7 +124,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:314:5 + --> $DIR/undocumented_unsafe_blocks.rs:319:5 | LL | unsafe { | ^^^^^^^^ @@ -124,7 +132,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:324:5 + --> $DIR/undocumented_unsafe_blocks.rs:329:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -132,12 +140,12 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:328:20 + --> $DIR/undocumented_unsafe_blocks.rs:333:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: aborting due to 17 previous errors +error: aborting due to 18 previous errors From 515b4eceff5094533ba0815b5bec0b4d417bc2a2 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 2 Apr 2022 12:34:06 +0100 Subject: [PATCH 59/82] Run fmt test before compile-test/dogfood --- tests/{fmt.rs => check-fmt.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{fmt.rs => check-fmt.rs} (100%) diff --git a/tests/fmt.rs b/tests/check-fmt.rs similarity index 100% rename from tests/fmt.rs rename to tests/check-fmt.rs From 58833e58a643d1d3d4f2499d6395966245207e3d Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Mon, 4 Apr 2022 08:48:49 +0200 Subject: [PATCH 60/82] is_unit_like_struct -> has_brackets --- clippy_lints/src/empty_structs_with_brackets.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs index 8f2a0306aa05..fdeac8d82557 100644 --- a/clippy_lints/src/empty_structs_with_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -33,7 +33,7 @@ impl EarlyLintPass for EmptyStructsWithBrackets { let span_after_ident = item.span.with_lo(item.ident.span.hi()); if let ItemKind::Struct(var_data, _) = &item.kind - && !is_unit_like_struct(var_data) + && has_brackets(var_data) && has_no_fields(cx, var_data, span_after_ident) { span_lint_and_then( cx, @@ -56,8 +56,8 @@ fn has_no_ident_token(braces_span_str: &str) -> bool { !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident) } -fn is_unit_like_struct(var_data: &VariantData) -> bool { - matches!(var_data, VariantData::Unit(_)) +fn has_brackets(var_data: &VariantData) -> bool { + !matches!(var_data, VariantData::Unit(_)) } fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool { From 5cd711b4f18f674dbdbd794d4ac32a0aeb0ef6f2 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 4 Apr 2022 11:56:56 -0400 Subject: [PATCH 61/82] Add lints `drop_non_drop` and `forget_non_drop` --- CHANGELOG.md | 2 + clippy_lints/src/drop_forget_ref.rs | 181 +++++++++++++------ clippy_lints/src/lib.register_all.rs | 4 +- clippy_lints/src/lib.register_correctness.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 4 +- clippy_lints/src/lib.register_suspicious.rs | 2 + clippy_lints/src/lib.rs | 2 - clippy_lints/src/undropped_manually_drops.rs | 59 ------ tests/ui/drop_forget_copy.stderr | 12 +- tests/ui/drop_non_drop.rs | 40 ++++ tests/ui/drop_non_drop.stderr | 27 +++ tests/ui/drop_ref.rs | 2 +- tests/ui/forget_non_drop.rs | 27 +++ tests/ui/forget_non_drop.stderr | 27 +++ tests/ui/forget_ref.rs | 2 +- tests/ui/redundant_clone.fixed | 2 +- tests/ui/redundant_clone.rs | 2 +- 17 files changed, 269 insertions(+), 128 deletions(-) delete mode 100644 clippy_lints/src/undropped_manually_drops.rs create mode 100644 tests/ui/drop_non_drop.rs create mode 100644 tests/ui/drop_non_drop.stderr create mode 100644 tests/ui/forget_non_drop.rs create mode 100644 tests/ui/forget_non_drop.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d1e081e839a..46907f5c07b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3256,6 +3256,7 @@ Released 2018-09-13 [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens [`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy +[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec @@ -3307,6 +3308,7 @@ Released 2018-09-13 [`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map [`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy +[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 5c4b35fd4b9d..88c54828da83 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,9 +1,8 @@ -use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::ty::is_copy; -use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::is_must_use_func_call; +use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -103,6 +102,75 @@ declare_clippy_lint! { "calls to `std::mem::forget` with a value that implements Copy" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `std::mem::drop` with a value that does not implement `Drop`. + /// + /// ### Why is this bad? + /// Calling `std::mem::drop` is no different than dropping such a type. A different value may + /// have been intended. + /// + /// ### Example + /// ```rust + /// struct Foo; + /// let x = Foo; + /// std::mem::drop(x); + /// ``` + #[clippy::version = "1.61.0"] + pub DROP_NON_DROP, + suspicious, + "call to `std::mem::drop` with a value which does not implement `Drop`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `std::mem::forget` with a value that does not implement `Drop`. + /// + /// ### Why is this bad? + /// Calling `std::mem::forget` is no different than dropping such a type. A different value may + /// have been intended. + /// + /// ### Example + /// ```rust + /// struct Foo; + /// let x = Foo; + /// std::mem::forget(x); + /// ``` + #[clippy::version = "1.61.0"] + pub FORGET_NON_DROP, + suspicious, + "call to `std::mem::forget` with a value which does not implement `Drop`" +} + +declare_clippy_lint! { + /// ### What it does + /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. + /// + /// ### Why is this bad? + /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`. + /// + /// ### Known problems + /// Does not catch cases if the user binds `std::mem::drop` + /// to a different name and calls it that way. + /// + /// ### Example + /// ```rust + /// struct S; + /// drop(std::mem::ManuallyDrop::new(S)); + /// ``` + /// Use instead: + /// ```rust + /// struct S; + /// unsafe { + /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); + /// } + /// ``` + #[clippy::version = "1.49.0"] + pub UNDROPPED_MANUALLY_DROPS, + correctness, + "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value" +} + const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \ Dropping a reference does nothing"; const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \ @@ -111,60 +179,65 @@ const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that imp Dropping a copy leaves the original intact"; const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \ Forgetting a copy leaves the original intact"; +const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \ + Dropping such a type only extends it's contained lifetimes"; +const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \ + Forgetting such a type is the same as dropping it"; -declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]); +declare_lint_pass!(DropForgetRef => [ + DROP_REF, + FORGET_REF, + DROP_COPY, + FORGET_COPY, + DROP_NON_DROP, + FORGET_NON_DROP, + UNDROPPED_MANUALLY_DROPS +]); impl<'tcx> LateLintPass<'tcx> for DropForgetRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(path, args) = expr.kind; - if let ExprKind::Path(ref qpath) = path.kind; - if args.len() == 1; - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - then { - let lint; - let msg; - let arg = &args[0]; - let arg_ty = cx.typeck_results().expr_ty(arg); - - if let ty::Ref(..) = arg_ty.kind() { - match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::mem_drop) => { - lint = DROP_REF; - msg = DROP_REF_SUMMARY.to_string(); - }, - Some(sym::mem_forget) => { - lint = FORGET_REF; - msg = FORGET_REF_SUMMARY.to_string(); - }, - _ => return, - } - span_lint_and_note(cx, - lint, - expr.span, - &msg, - Some(arg.span), - &format!("argument has type `{}`", arg_ty)); - } else if is_copy(cx, arg_ty) { - match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::mem_drop) => { - lint = DROP_COPY; - msg = DROP_COPY_SUMMARY.to_string(); - }, - Some(sym::mem_forget) => { - lint = FORGET_COPY; - msg = FORGET_COPY_SUMMARY.to_string(); - }, - _ => return, - } - span_lint_and_note(cx, - lint, - expr.span, - &msg, - Some(arg.span), - &format!("argument has type {}", arg_ty)); + if let ExprKind::Call(path, [arg]) = expr.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id) + { + let arg_ty = cx.typeck_results().expr_ty(arg); + let (lint, msg) = match fn_name { + sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY), + sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY), + sym::mem_drop if is_copy(cx, arg_ty) => (DROP_COPY, DROP_COPY_SUMMARY), + sym::mem_forget if is_copy(cx, arg_ty) => (FORGET_COPY, FORGET_COPY_SUMMARY), + sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => { + span_lint_and_help( + cx, + UNDROPPED_MANUALLY_DROPS, + expr.span, + "the inner value of this ManuallyDrop will not be dropped", + None, + "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop", + ); + return; } - } + sym::mem_drop + if !(arg_ty.needs_drop(cx.tcx, cx.param_env) + || is_must_use_func_call(cx, arg) + || is_must_use_ty(cx, arg_ty)) => + { + (DROP_NON_DROP, DROP_NON_DROP_SUMMARY) + }, + sym::mem_forget if !arg_ty.needs_drop(cx.tcx, cx.param_env) => { + (FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY) + }, + _ => return, + }; + span_lint_and_note( + cx, + lint, + expr.span, + msg, + Some(arg.span), + &format!("argument has type `{}`", arg_ty), + ); } } } diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 1fb3ca1fd9b2..b2f674e9c882 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -50,9 +50,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(double_comparison::DOUBLE_COMPARISONS), LintId::of(double_parens::DOUBLE_PARENS), LintId::of(drop_forget_ref::DROP_COPY), + LintId::of(drop_forget_ref::DROP_NON_DROP), LintId::of(drop_forget_ref::DROP_REF), LintId::of(drop_forget_ref::FORGET_COPY), + LintId::of(drop_forget_ref::FORGET_NON_DROP), LintId::of(drop_forget_ref::FORGET_REF), + LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), LintId::of(duration_subsec::DURATION_SUBSEC), LintId::of(entry::MAP_ENTRY), LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), @@ -297,7 +300,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(types::REDUNDANT_ALLOCATION), LintId::of(types::TYPE_COMPLEXITY), LintId::of(types::VEC_BOX), - LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index df63f84463db..6bf2c4bbaedc 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -22,6 +22,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(drop_forget_ref::DROP_REF), LintId::of(drop_forget_ref::FORGET_COPY), LintId::of(drop_forget_ref::FORGET_REF), + LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(eq_op::EQ_OP), LintId::of(erasing_op::ERASING_OP), @@ -62,7 +63,6 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmuting_null::TRANSMUTING_NULL), - LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index e3161795139d..8a3c0fb60fa6 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -123,9 +123,12 @@ store.register_lints(&[ double_comparison::DOUBLE_COMPARISONS, double_parens::DOUBLE_PARENS, drop_forget_ref::DROP_COPY, + drop_forget_ref::DROP_NON_DROP, drop_forget_ref::DROP_REF, drop_forget_ref::FORGET_COPY, + drop_forget_ref::FORGET_NON_DROP, drop_forget_ref::FORGET_REF, + drop_forget_ref::UNDROPPED_MANUALLY_DROPS, duration_subsec::DURATION_SUBSEC, else_if_without_else::ELSE_IF_WITHOUT_ELSE, empty_enum::EMPTY_ENUM, @@ -506,7 +509,6 @@ store.register_lints(&[ types::TYPE_COMPLEXITY, types::VEC_BOX, undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS, - undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, unicode::INVISIBLE_CHARACTERS, unicode::NON_ASCII_LITERAL, unicode::UNICODE_NOT_NFC, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 0707e4f8f3d1..4c3fbb6c4a86 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -10,6 +10,8 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), + LintId::of(drop_forget_ref::DROP_NON_DROP), + LintId::of(drop_forget_ref::FORGET_NON_DROP), LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c8b57956b1b6..7536539dcdb9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -376,7 +376,6 @@ mod transmuting_null; mod try_err; mod types; mod undocumented_unsafe_blocks; -mod undropped_manually_drops; mod unicode; mod uninit_vec; mod unit_hash; @@ -813,7 +812,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); - store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops)); store.register_late_pass(|| Box::new(strings::StrToString)); store.register_late_pass(|| Box::new(strings::StringToString)); store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues)); diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs deleted file mode 100644 index db652766705c..000000000000 --- a/clippy_lints/src/undropped_manually_drops.rs +++ /dev/null @@ -1,59 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_res; -use clippy_utils::ty::is_type_lang_item; -use rustc_hir::{lang_items, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. - /// - /// ### Why is this bad? - /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`. - /// - /// ### Known problems - /// Does not catch cases if the user binds `std::mem::drop` - /// to a different name and calls it that way. - /// - /// ### Example - /// ```rust - /// struct S; - /// drop(std::mem::ManuallyDrop::new(S)); - /// ``` - /// Use instead: - /// ```rust - /// struct S; - /// unsafe { - /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); - /// } - /// ``` - #[clippy::version = "1.49.0"] - pub UNDROPPED_MANUALLY_DROPS, - correctness, - "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value" -} - -declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); - -impl<'tcx> LateLintPass<'tcx> for UndroppedManuallyDrops { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(fun, [arg_0, ..]) = expr.kind; - if path_res(cx, fun).opt_def_id() == cx.tcx.get_diagnostic_item(sym::mem_drop); - let ty = cx.typeck_results().expr_ty(arg_0); - if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop); - then { - span_lint_and_help( - cx, - UNDROPPED_MANUALLY_DROPS, - expr.span, - "the inner value of this ManuallyDrop will not be dropped", - None, - "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop", - ); - } - } - } -} diff --git a/tests/ui/drop_forget_copy.stderr b/tests/ui/drop_forget_copy.stderr index 01de0be7caea..88228afae89c 100644 --- a/tests/ui/drop_forget_copy.stderr +++ b/tests/ui/drop_forget_copy.stderr @@ -5,7 +5,7 @@ LL | drop(s1); | ^^^^^^^^ | = note: `-D clippy::drop-copy` implied by `-D warnings` -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:33:10 | LL | drop(s1); @@ -17,7 +17,7 @@ error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a LL | drop(s2); | ^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:34:10 | LL | drop(s2); @@ -29,7 +29,7 @@ error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a LL | drop(s4); | ^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:36:10 | LL | drop(s4); @@ -42,7 +42,7 @@ LL | forget(s1); | ^^^^^^^^^^ | = note: `-D clippy::forget-copy` implied by `-D warnings` -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:39:12 | LL | forget(s1); @@ -54,7 +54,7 @@ error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetti LL | forget(s2); | ^^^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:40:12 | LL | forget(s2); @@ -66,7 +66,7 @@ error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetti LL | forget(s4); | ^^^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:42:12 | LL | forget(s4); diff --git a/tests/ui/drop_non_drop.rs b/tests/ui/drop_non_drop.rs new file mode 100644 index 000000000000..5a0ebde82c5d --- /dev/null +++ b/tests/ui/drop_non_drop.rs @@ -0,0 +1,40 @@ +#![warn(clippy::drop_non_drop)] + +use core::mem::drop; + +fn make_result(t: T) -> Result { + Ok(t) +} + +#[must_use] +fn must_use(t: T) -> T { + t +} + +fn drop_generic(t: T) { + // Don't lint + drop(t) +} + +fn main() { + struct Foo; + // Lint + drop(Foo); + // Don't lint + drop(make_result(Foo)); + // Don't lint + drop(must_use(Foo)); + + struct Bar; + impl Drop for Bar { + fn drop(&mut self) {} + } + // Don't lint + drop(Bar); + + struct Baz(T); + // Lint + drop(Baz(Foo)); + // Don't lint + drop(Baz(Bar)); +} diff --git a/tests/ui/drop_non_drop.stderr b/tests/ui/drop_non_drop.stderr new file mode 100644 index 000000000000..f73068901c50 --- /dev/null +++ b/tests/ui/drop_non_drop.stderr @@ -0,0 +1,27 @@ +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes + --> $DIR/drop_non_drop.rs:22:5 + | +LL | drop(Foo); + | ^^^^^^^^^ + | + = note: `-D clippy::drop-non-drop` implied by `-D warnings` +note: argument has type `main::Foo` + --> $DIR/drop_non_drop.rs:22:10 + | +LL | drop(Foo); + | ^^^ + +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes + --> $DIR/drop_non_drop.rs:37:5 + | +LL | drop(Baz(Foo)); + | ^^^^^^^^^^^^^^ + | +note: argument has type `main::Baz` + --> $DIR/drop_non_drop.rs:37:10 + | +LL | drop(Baz(Foo)); + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index e1a15c609fd2..7de0b0bbdf9a 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -1,7 +1,7 @@ #![warn(clippy::drop_ref)] #![allow(clippy::toplevel_ref_arg)] #![allow(clippy::map_err_ignore)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::drop_non_drop)] use std::mem::drop; diff --git a/tests/ui/forget_non_drop.rs b/tests/ui/forget_non_drop.rs new file mode 100644 index 000000000000..7580cf95ebfa --- /dev/null +++ b/tests/ui/forget_non_drop.rs @@ -0,0 +1,27 @@ +#![warn(clippy::forget_non_drop)] + +use core::mem::forget; + +fn forget_generic(t: T) { + // Don't lint + forget(t) +} + +fn main() { + struct Foo; + // Lint + forget(Foo); + + struct Bar; + impl Drop for Bar { + fn drop(&mut self) {} + } + // Don't lint + forget(Bar); + + struct Baz(T); + // Lint + forget(Baz(Foo)); + // Don't lint + forget(Baz(Bar)); +} diff --git a/tests/ui/forget_non_drop.stderr b/tests/ui/forget_non_drop.stderr new file mode 100644 index 000000000000..03fb00960a44 --- /dev/null +++ b/tests/ui/forget_non_drop.stderr @@ -0,0 +1,27 @@ +error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it + --> $DIR/forget_non_drop.rs:13:5 + | +LL | forget(Foo); + | ^^^^^^^^^^^ + | + = note: `-D clippy::forget-non-drop` implied by `-D warnings` +note: argument has type `main::Foo` + --> $DIR/forget_non_drop.rs:13:12 + | +LL | forget(Foo); + | ^^^ + +error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it + --> $DIR/forget_non_drop.rs:24:5 + | +LL | forget(Baz(Foo)); + | ^^^^^^^^^^^^^^^^ + | +note: argument has type `main::Baz` + --> $DIR/forget_non_drop.rs:24:12 + | +LL | forget(Baz(Foo)); + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/forget_ref.rs b/tests/ui/forget_ref.rs index c49e6756a6c5..6c8c4c9c0ede 100644 --- a/tests/ui/forget_ref.rs +++ b/tests/ui/forget_ref.rs @@ -1,6 +1,6 @@ #![warn(clippy::forget_ref)] #![allow(clippy::toplevel_ref_arg)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::forget_non_drop)] use std::mem::forget; diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index 16b40dcd9028..1525f6a93dfd 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -1,7 +1,7 @@ // run-rustfix // rustfix-only-machine-applicable -#![allow(clippy::implicit_clone)] +#![allow(clippy::implicit_clone, clippy::drop_non_drop)] use std::ffi::OsString; use std::path::Path; diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index bd3d7365229f..2f82aefd9283 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -1,7 +1,7 @@ // run-rustfix // rustfix-only-machine-applicable -#![allow(clippy::implicit_clone)] +#![allow(clippy::implicit_clone, clippy::drop_non_drop)] use std::ffi::OsString; use std::path::Path; From e4fc15e6467826d1c59219f3963904c776ce7f13 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 4 Apr 2022 13:54:52 -0400 Subject: [PATCH 62/82] Don't lint `cast_ptr_alignment` when used for unaligned reads and writes --- clippy_lints/src/casts/cast_ptr_alignment.rs | 100 ++++++++++++------- clippy_utils/src/paths.rs | 2 + tests/ui/cast_alignment.rs | 14 +++ tests/ui/cast_alignment.stderr | 8 +- 4 files changed, 86 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index a4ef1344ab95..d476a1a7646c 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_hir_ty_cfg_dependant; use clippy_utils::ty::is_c_void; -use if_chain::if_chain; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, match_any_def_paths, paths}; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; @@ -20,45 +19,78 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } else if let ExprKind::MethodCall(method_path, [self_arg, ..], _) = &expr.kind { - if_chain! { - if method_path.ident.name == sym!(cast); - if let Some(generic_args) = method_path.args; - if let [GenericArg::Type(cast_to)] = generic_args.args; + if method_path.ident.name == sym!(cast) + && let Some(generic_args) = method_path.args + && let [GenericArg::Type(cast_to)] = generic_args.args // There probably is no obvious reason to do this, just to be consistent with `as` cases. - if !is_hir_ty_cfg_dependant(cx, cast_to); - then { - let (cast_from, cast_to) = - (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } + && !is_hir_ty_cfg_dependant(cx, cast_to) + { + let (cast_from, cast_to) = + (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } } } fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { - if_chain! { - if let ty::RawPtr(from_ptr_ty) = &cast_from.kind(); - if let ty::RawPtr(to_ptr_ty) = &cast_to.kind(); - if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty); - if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty); - if from_layout.align.abi < to_layout.align.abi; + if let ty::RawPtr(from_ptr_ty) = &cast_from.kind() + && let ty::RawPtr(to_ptr_ty) = &cast_to.kind() + && let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty) + && let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty) + && from_layout.align.abi < to_layout.align.abi // with c_void, we inherently need to trust the user - if !is_c_void(cx, from_ptr_ty.ty); + && !is_c_void(cx, from_ptr_ty.ty) // when casting from a ZST, we don't know enough to properly lint - if !from_layout.is_zst(); - then { - span_lint( - cx, - CAST_PTR_ALIGNMENT, - expr.span, - &format!( - "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", - cast_from, - cast_to, - from_layout.align.abi.bytes(), - to_layout.align.abi.bytes(), - ), - ); - } + && !from_layout.is_zst() + && !is_used_as_unaligned(cx, expr) + { + span_lint( + cx, + CAST_PTR_ALIGNMENT, + expr.span, + &format!( + "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", + cast_from, + cast_to, + from_layout.align.abi.bytes(), + to_layout.align.abi.bytes(), + ), + ); + } +} + +fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + let Some(parent) = get_parent_expr(cx, e) else { + return false; + }; + match parent.kind { + ExprKind::MethodCall(name, [self_arg, ..], _) if self_arg.hir_id == e.hir_id => { + if matches!(name.ident.as_str(), "read_unaligned" | "write_unaligned") + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) + && let Some(def_id) = cx.tcx.impl_of_method(def_id) + && cx.tcx.type_of(def_id).is_unsafe_ptr() + { + true + } else { + false + } + }, + ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => { + static PATHS: &[&[&str]] = &[ + paths::PTR_READ_UNALIGNED.as_slice(), + paths::PTR_WRITE_UNALIGNED.as_slice(), + paths::PTR_UNALIGNED_VOLATILE_LOAD.as_slice(), + paths::PTR_UNALIGNED_VOLATILE_STORE.as_slice(), + ]; + if let ExprKind::Path(path) = &func.kind + && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && match_any_def_paths(cx, def_id, PATHS).is_some() + { + true + } else { + false + } + }, + _ => false, } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 6f56f8d51365..79e6e92dc0aa 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -105,6 +105,8 @@ pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"]; pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"]; pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"]; pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"]; +pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"]; +pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"]; pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"]; pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"]; pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"]; diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs index 659591fffbec..e4e7290a30e9 100644 --- a/tests/ui/cast_alignment.rs +++ b/tests/ui/cast_alignment.rs @@ -1,6 +1,7 @@ //! Test casts for alignment issues #![feature(rustc_private)] +#![feature(core_intrinsics)] extern crate libc; #[warn(clippy::cast_ptr_alignment)] @@ -34,4 +35,17 @@ fn main() { (&1u32 as *const u32 as *const libc::c_void) as *const u32; // For ZST, we should trust the user. See #4256 (&1u32 as *const u32 as *const ()) as *const u32; + + // Issue #2881 + let mut data = [0u8, 0u8]; + unsafe { + let ptr = &data as *const [u8; 2] as *const u8; + let _ = (ptr as *const u16).read_unaligned(); + let _ = core::ptr::read_unaligned(ptr as *const u16); + let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16); + let ptr = &mut data as *mut [u8; 2] as *mut u8; + let _ = (ptr as *mut u16).write_unaligned(0); + let _ = core::ptr::write_unaligned(ptr as *mut u16, 0); + let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); + } } diff --git a/tests/ui/cast_alignment.stderr b/tests/ui/cast_alignment.stderr index aedd36844555..5df2b5b1094b 100644 --- a/tests/ui/cast_alignment.stderr +++ b/tests/ui/cast_alignment.stderr @@ -1,5 +1,5 @@ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:18:5 + --> $DIR/cast_alignment.rs:19:5 | LL | (&1u8 as *const u8) as *const u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,19 +7,19 @@ LL | (&1u8 as *const u8) as *const u16; = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings` error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:19:5 + --> $DIR/cast_alignment.rs:20:5 | LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:22:5 + --> $DIR/cast_alignment.rs:23:5 | LL | (&1u8 as *const u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:23:5 + --> $DIR/cast_alignment.rs:24:5 | LL | (&mut 1u8 as *mut u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 764dd1360f1fb1540778592a36fefe71f713ca83 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 4 Apr 2022 21:06:53 +0200 Subject: [PATCH 63/82] Escape `mod.rs` file mentions to avoid links in our documentation We can read them if they want to start sponsoring us xD --- clippy_lints/src/module_style.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 39c44ad6e2cb..0a393657267b 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -8,7 +8,7 @@ use std::path::{Component, Path}; declare_clippy_lint! { /// ### What it does - /// Checks that module layout uses only self named module files, bans mod.rs files. + /// Checks that module layout uses only self named module files, bans `mod.rs` files. /// /// ### Why is this bad? /// Having multiple module layout styles in a project can be confusing. @@ -37,7 +37,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks that module layout uses only mod.rs files. + /// Checks that module layout uses only `mod.rs` files. /// /// ### Why is this bad? /// Having multiple module layout styles in a project can be confusing. From c8b9e85b207b29043e19c7718137224c9ce70200 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 24 Mar 2022 02:03:04 +0000 Subject: [PATCH 64/82] span: move `MultiSpan` `MultiSpan` contains labels, which are more complicated with the introduction of diagnostic translation and will use types from `rustc_errors` - however, `rustc_errors` depends on `rustc_span` so `rustc_span` cannot use types like `DiagnosticMessage` without dependency cycles. Introduce a new `rustc_error_messages` crate that can contain `DiagnosticMessage` and `MultiSpan`. Signed-off-by: David Wood --- clippy_lints/src/collapsible_match.rs | 3 ++- clippy_lints/src/doc.rs | 4 ++-- clippy_lints/src/loops/needless_collect.rs | 4 ++-- clippy_lints/src/ptr.rs | 4 ++-- clippy_utils/src/diagnostics.rs | 21 ++++++++++++++++----- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index c71e9f10f79e..acb3c917d624 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -3,11 +3,12 @@ use clippy_utils::higher::IfLetOrMatch; use clippy_utils::visitors::is_local_used; use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq}; use if_chain::if_chain; +use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, Guard, HirId, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{MultiSpan, Span}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 703aa458f44e..d67c03714e38 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -11,7 +11,7 @@ use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Applicability, Handler, SuggestionStyle}; +use rustc_errors::{Applicability, Handler, MultiSpan, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, Expr}; @@ -25,7 +25,7 @@ use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::edition::Edition; -use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; +use rustc_span::source_map::{BytePos, FilePathMapping, SourceMap, Span}; use rustc_span::{sym, FileName, Pos}; use std::io; use std::ops::Range; diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 06190850bb00..c7772e483adb 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, Ty}; use rustc_span::sym; -use rustc_span::{MultiSpan, Span}; +use rustc_span::Span; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 9d4313827f7c..5f453dc16555 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -5,7 +5,7 @@ use clippy_utils::source::snippet_opt; use clippy_utils::ty::expr_sig; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths}; use if_chain::if_chain; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -19,8 +19,8 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; use rustc_span::symbol::Symbol; -use rustc_span::{sym, MultiSpan}; use std::fmt; use std::iter; diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 625a53899df9..b142397f71b9 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -8,10 +8,10 @@ //! Thank you! //! ~The `INTERNAL_METADATA_COLLECTOR` lint -use rustc_errors::{emitter::MAX_SUGGESTION_HIGHLIGHT_LINES, Applicability, Diagnostic}; +use rustc_errors::{emitter::MAX_SUGGESTION_HIGHLIGHT_LINES, Applicability, Diagnostic, MultiSpan}; use rustc_hir::HirId; use rustc_lint::{LateContext, Lint, LintContext}; -use rustc_span::source_map::{MultiSpan, Span}; +use rustc_span::source_map::Span; use std::env; fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { @@ -155,7 +155,13 @@ where }); } -pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { +pub fn span_lint_hir( + cx: &LateContext<'_>, + lint: &'static Lint, + hir_id: HirId, + sp: Span, + msg: &str, +) { cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| { let mut diag = diag.build(msg); docs_link(&mut diag, lint); @@ -272,9 +278,14 @@ pub fn span_lint_and_sugg_for_edges( let sugg_lines_count = sugg.lines().count(); if sugg_lines_count > MAX_SUGGESTION_HIGHLIGHT_LINES { let sm = cx.sess().source_map(); - if let (Ok(line_upper), Ok(line_bottom)) = (sm.lookup_line(sp.lo()), sm.lookup_line(sp.hi())) { + if let (Ok(line_upper), Ok(line_bottom)) = + (sm.lookup_line(sp.lo()), sm.lookup_line(sp.hi())) + { let split_idx = MAX_SUGGESTION_HIGHLIGHT_LINES / 2; - let span_upper = sm.span_until_char(sp.with_hi(line_upper.sf.lines[line_upper.line + split_idx]), '\n'); + let span_upper = sm.span_until_char( + sp.with_hi(line_upper.sf.lines[line_upper.line + split_idx]), + '\n', + ); let span_bottom = sp.with_lo(line_bottom.sf.lines[line_bottom.line - split_idx]); let sugg_lines_vec = sugg.lines().collect::>(); From 41d1340505d37058349ba016c9dc8c3d52f49bf3 Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 26 Mar 2022 07:27:43 +0000 Subject: [PATCH 65/82] errors: implement fallback diagnostic translation This commit updates the signatures of all diagnostic functions to accept types that can be converted into a `DiagnosticMessage`. This enables existing diagnostic calls to continue to work as before and Fluent identifiers to be provided. The `SessionDiagnostic` derive just generates normal diagnostic calls, so these APIs had to be modified to accept Fluent identifiers. In addition, loading of the "fallback" Fluent bundle, which contains the built-in English messages, has been implemented. Each diagnostic now has "arguments" which correspond to variables in the Fluent messages (necessary to render a Fluent message) but no API for adding arguments has been added yet. Therefore, diagnostics (that do not require interpolation) can be converted to use Fluent identifiers and will be output as before. --- clippy_lints/src/collapsible_match.rs | 4 ++-- clippy_lints/src/doc.rs | 12 +++++++++++- clippy_lints/src/loops/needless_collect.rs | 2 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 10 ++++++---- src/driver.rs | 4 +++- 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index acb3c917d624..cc354b50afa3 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -130,8 +130,8 @@ fn check_arm<'tcx>( &msg, |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); - help_span.push_span_label(binding_span, "replace this binding".into()); - help_span.push_span_label(inner_then_pat.span, "with this pattern".into()); + help_span.push_span_label(binding_span, "replace this binding"); + help_span.push_span_label(inner_then_pat.span, "with this pattern"); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index d67c03714e38..b836363b31b5 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -621,7 +621,17 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let emitter = EmitterWriter::new(Box::new(io::sink()), None, false, false, false, None, false); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let emitter = EmitterWriter::new( + Box::new(io::sink()), + None, + fallback_bundle, + false, + false, + false, + None, + false, + ); let handler = Handler::with_emitter(false, None, Box::new(emitter)); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index c7772e483adb..ddaffc751880 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -102,7 +102,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo // Suggest replacing iter_call with iter_replacement, and removing stmt let mut span = MultiSpan::from_span(method_name.ident.span); - span.push_span_label(iter_call.span, "the iterator could be used here instead".into()); + span.push_span_label(iter_call.span, "the iterator could be used here instead"); span_lint_hir_and_then( cx, super::NEEDLESS_COLLECT, diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index ecc9acf4445d..06209bfe7b08 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -148,7 +148,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { - cx.tcx.sess.span_err(span, &err); + cx.tcx.sess.span_err(span, err.as_ref()); } } else { span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 5eb7b0f0521e..d29d07da7b0f 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -235,11 +235,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - &snippet_opt(cx, span) + snippet_opt(cx, span) .map_or( "change the call to".into(), |x| Cow::from(format!("change `{}` to", x)), - ), + ) + .as_ref(), suggestion.into(), Applicability::Unspecified, ); @@ -264,11 +265,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - &snippet_opt(cx, span) + snippet_opt(cx, span) .map_or( "change the call to".into(), |x| Cow::from(format!("change `{}` to", x)) - ), + ) + .as_ref(), suggestion.into(), Applicability::Unspecified, ); diff --git a/src/driver.rs b/src/driver.rs index 855a6a6ef6ad..bfce787af5ea 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -165,9 +165,11 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, + fallback_bundle, false, false, None, @@ -191,7 +193,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { ]; for note in &xs { - handler.note_without_error(note); + handler.note_without_error(note.as_ref()); } // If backtraces are enabled, also print the query stack From 865386e9dcac24966ac09062e910482346cd40f4 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 28 Mar 2022 09:36:20 +0100 Subject: [PATCH 66/82] errors: implement sysroot/testing bundle loading Extend loading of Fluent bundles so that bundles can be loaded from the sysroot based on the language requested by the user, or using a nightly flag. Sysroot bundles are loaded from `$sysroot/share/locale/$locale/*.ftl`. Signed-off-by: David Wood --- clippy_lints/src/doc.rs | 4 +++- src/driver.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index b836363b31b5..e08e9e499938 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -621,10 +621,12 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let fallback_bundle = rustc_errors::fallback_fluent_bundle() + .expect("failed to load fallback fluent bundle"); let emitter = EmitterWriter::new( Box::new(io::sink()), None, + None, fallback_bundle, false, false, diff --git a/src/driver.rs b/src/driver.rs index bfce787af5ea..f04535b2bea0 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -165,10 +165,12 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let fallback_bundle = rustc_errors::fallback_fluent_bundle() + .expect("failed to load fallback fluent bundle"); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, + None, fallback_bundle, false, false, From ba718bbeeee95407f73f6c05c75284ff68d10fb6 Mon Sep 17 00:00:00 2001 From: David Wood Date: Sun, 3 Apr 2022 04:53:01 +0100 Subject: [PATCH 67/82] session: opt for enabling directionality markers Add an option for enabling and disabling Fluent's directionality isolation markers in output. Disabled by default as these can render in some terminals and applications. Signed-off-by: David Wood --- clippy_lints/src/doc.rs | 2 +- src/driver.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index e08e9e499938..92cf82bcd6a3 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -621,7 +621,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle() + let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) .expect("failed to load fallback fluent bundle"); let emitter = EmitterWriter::new( Box::new(io::sink()), diff --git a/src/driver.rs b/src/driver.rs index f04535b2bea0..bc1b0d745755 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -165,7 +165,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); - let fallback_bundle = rustc_errors::fallback_fluent_bundle() + let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) .expect("failed to load fallback fluent bundle"); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, From f8c2e6dc4eee2ad42fcdab0eb0bb321b6b9e60ac Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 5 Apr 2022 10:24:32 +0100 Subject: [PATCH 68/82] Add documentation on how to do a minimal changelog update This ensures that the link to the Clippy version in the Rust release blog post works correctly. The additional `(beta)` behind the previous beta version breaks that link otherwise. --- doc/release.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/release.md b/doc/release.md index afe3033c288c..c4f8f9893842 100644 --- a/doc/release.md +++ b/doc/release.md @@ -121,4 +121,25 @@ happened a stable backport, make sure to re-merge those changes just as with the For this see the document on [how to update the changelog]. +If you don't have time to do a complete changelog update right away, just update +the following parts: + +- Remove the `(beta)` from the new stable version: + + ```markdown + ## Rust 1.XX (beta) -> ## Rust 1.XX + ``` + +- Update the release date line of the new stable version: + + ```markdown + Current beta, release 20YY-MM-DD -> Current stable, released 20YY-MM-DD + ``` + +- Update the release date line of the previous stable version: + + ```markdown + Current stable, released 20YY-MM-DD -> Released 20YY-MM-DD + ``` + [how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md From 1582e7bf88175f7362f5bd12ad5835dcdfb58473 Mon Sep 17 00:00:00 2001 From: Akshay Date: Tue, 8 Feb 2022 16:51:02 +0530 Subject: [PATCH 69/82] fix mispelling in diagnostic message of bytes_nth --- clippy_lints/src/methods/bytes_nth.rs | 2 +- tests/ui/bytes_nth.stderr | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 76eaedea8a0d..44857d61fef8 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E cx, BYTES_NTH, expr.span, - &format!("called `.byte().nth()` on a `{}`", caller_type), + &format!("called `.bytes().nth()` on a `{}`", caller_type), "try", format!( "{}.as_bytes().get({})", diff --git a/tests/ui/bytes_nth.stderr b/tests/ui/bytes_nth.stderr index 536decf5e7fc..5641d798853a 100644 --- a/tests/ui/bytes_nth.stderr +++ b/tests/ui/bytes_nth.stderr @@ -1,4 +1,4 @@ -error: called `.byte().nth()` on a `String` +error: called `.bytes().nth()` on a `String` --> $DIR/bytes_nth.rs:8:5 | LL | s.bytes().nth(3); @@ -6,13 +6,13 @@ LL | s.bytes().nth(3); | = note: `-D clippy::bytes-nth` implied by `-D warnings` -error: called `.byte().nth()` on a `String` +error: called `.bytes().nth()` on a `String` --> $DIR/bytes_nth.rs:9:14 | LL | let _ = &s.bytes().nth(3); | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)` -error: called `.byte().nth()` on a `str` +error: called `.bytes().nth()` on a `str` --> $DIR/bytes_nth.rs:10:5 | LL | s[..].bytes().nth(3); From 41ef4f729f3df237918229ff960c84792dd3231d Mon Sep 17 00:00:00 2001 From: Sabrina Jewson Date: Wed, 6 Apr 2022 10:14:20 +0100 Subject: [PATCH 70/82] Report `from_utf8` suggestion as maybe incorrect Co-authored-by: Philipp Krones --- clippy_lints/src/transmute/transmute_ref_to_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index fe3e88613f4e..786e7bfc56f6 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -45,7 +45,7 @@ pub(super) fn check<'tcx>( } else { format!("std::str::from_utf8{postfix}({snippet}).unwrap()") }, - Applicability::Unspecified, + Applicability::MaybeIncorrect, ); triggered = true; } else { From 85b081b65aa36e0d7cbdfba7cd705517563c0bd4 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Wed, 6 Apr 2022 20:44:54 +0800 Subject: [PATCH 71/82] code refractor for `[needless_match]` --- clippy_lints/src/matches/needless_match.rs | 27 +++++++--------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 3703488632b8..2105a03e03a3 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -3,12 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ - eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, + eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over, peel_blocks_with_stmt, }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, PathSegment, QPath}; +use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::sym; use rustc_typeck::hir_ty_to_ty; @@ -157,8 +157,9 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { // Example: `Some(val) => Some(val)` (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => { if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind { - return same_segments(path.segments, call_path.segments) - && same_non_ref_symbols(tuple_params, call_params); + return over(path.segments, call_path.segments, |pat_seg, call_seg| { + pat_seg.ident.name == call_seg.ident.name + }) && same_non_ref_symbols(tuple_params, call_params); } }, // Example: `val => val` @@ -177,7 +178,9 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { }, // Example: `Custom::TypeA => Custom::TypeB`, or `None => None` (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => { - return same_segments(p_path.segments, e_path.segments); + return over(p_path.segments, e_path.segments, |p_seg, e_seg| { + p_seg.ident.name == e_seg.ident.name + }); }, // Example: `5 => 5` (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => { @@ -191,20 +194,6 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { false } -fn same_segments(left_segs: &[PathSegment<'_>], right_segs: &[PathSegment<'_>]) -> bool { - if left_segs.len() != right_segs.len() { - return false; - } - - for i in 0..left_segs.len() { - if left_segs[i].ident.name != right_segs[i].ident.name { - return false; - } - } - - true -} - fn same_non_ref_symbols(pats: &[Pat<'_>], exprs: &[Expr<'_>]) -> bool { if pats.len() != exprs.len() { return false; From 1ad6442e919315144c476b522ed9a43579a319e5 Mon Sep 17 00:00:00 2001 From: Yoav Lavi Date: Wed, 6 Apr 2022 15:59:38 +0200 Subject: [PATCH 72/82] update unnecessary_join documentation --- clippy_lints/src/methods/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 021337280d13..b5a9af8c3bb7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2055,7 +2055,7 @@ declare_clippy_lint! { /// Checks for use of `.collect::>().join("")` on iterators. /// /// ### Why is this bad? - /// `.collect::()` is more concise and usually more performant + /// `.collect::()` is more concise and might be more performant /// /// ### Example /// ```rust @@ -2070,9 +2070,12 @@ declare_clippy_lint! { /// println!("{}", output); /// ``` /// ### Known problems - /// While `.collect::()` is more performant in most cases, there are cases where + /// While `.collect::()` is sometimes more performant, there are cases where /// using `.collect::()` over `.collect::>().join("")` /// will prevent loop unrolling and will result in a negative performance impact. + /// + /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output, + /// with aarch64 tending to producing faster assembly in more cases when using `.collect::()` #[clippy::version = "1.61.0"] pub UNNECESSARY_JOIN, pedantic, From cebe575aad4cc24401d676299e31126ea3af26f6 Mon Sep 17 00:00:00 2001 From: InfRandomness Date: Tue, 29 Mar 2022 19:19:16 +0200 Subject: [PATCH 73/82] Add .err().expect() lint --- clippy_lints/src/methods/err_expect.rs | 59 ++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 28 ++++++++++++ clippy_lints/src/utils/conf.rs | 2 +- clippy_utils/src/msrvs.rs | 2 +- tests/ui/err_expect.fixed | 14 ++++++ tests/ui/err_expect.rs | 14 ++++++ tests/ui/err_expect.stderr | 10 +++++ tests/ui/min_rust_version_attr.rs | 6 +++ tests/ui/min_rust_version_attr.stderr | 8 ++-- 9 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 clippy_lints/src/methods/err_expect.rs create mode 100644 tests/ui/err_expect.fixed create mode 100644 tests/ui/err_expect.rs create mode 100644 tests/ui/err_expect.stderr diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs new file mode 100644 index 000000000000..887f5c1cb3b4 --- /dev/null +++ b/clippy_lints/src/methods/err_expect.rs @@ -0,0 +1,59 @@ +use super::ERR_EXPECT; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item}; +use rustc_errors::Applicability; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_middle::ty::Ty; +use rustc_semver::RustcVersion; +use rustc_span::{sym, Span}; + +pub(super) fn check( + cx: &LateContext<'_>, + _expr: &rustc_hir::Expr<'_>, + recv: &rustc_hir::Expr<'_>, + msrv: Option<&RustcVersion>, + expect_span: Span, + err_span: Span, +) { + if_chain! { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + // Test the version to make sure the lint can be showed (expect_err has been introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) + if meets_msrv(msrv, &msrvs::EXPECT_ERR); + + // Grabs the `Result` type + let result_type = cx.typeck_results().expr_ty(recv); + // Tests if the T type in a `Result` is not None + if let Some(data_type) = get_data_type(cx, result_type); + // Tests if the T type in a `Result` implements debug + if has_debug_impl(data_type, cx); + + then { + span_lint_and_sugg( + cx, + ERR_EXPECT, + err_span.to(expect_span), + "called `.err().expect()` on a `Result` value", + "try", + "expect_err".to_string(), + Applicability::MachineApplicable + ); + } + }; +} + +/// Given a `Result` type, return its data (`T`). +fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { + match ty.kind() { + ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::Result) => substs.types().next(), + _ => None, + } +} + +/// Given a type, very if the Debug trait has been impl'd +fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { + cx.tcx + .get_diagnostic_item(sym::Debug) + .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b5a9af8c3bb7..4475f8eaf597 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -9,6 +9,7 @@ mod chars_next_cmp_with_unwrap; mod clone_on_copy; mod clone_on_ref_ptr; mod cloned_instead_of_copied; +mod err_expect; mod expect_fun_call; mod expect_used; mod extend_with_drain; @@ -362,6 +363,29 @@ declare_clippy_lint! { "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `.err().expect()` calls on the `Result` type. + /// + /// ### Why is this bad? + /// `.expect_err()` can be called directly to avoid the extra type conversion from `ok()`. + /// + /// ### Example + /// ```should_panic + /// let x: Result = Ok(10); + /// x.err().expect("Testing err().expect()"); + /// ``` + /// Use instead: + /// ```should_panic + /// let x: Result = Ok(10); + /// x.expect_err("Testing expect_err"); + /// ``` + #[clippy::version = "1.61.0"] + pub ERR_EXPECT, + style, + r#"using `.err().expect("")` when `.expect_err("")` can be used"# +} + declare_clippy_lint! { /// ### What it does /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and @@ -2168,6 +2192,7 @@ impl_lint_pass!(Methods => [ NEEDLESS_SPLITN, UNNECESSARY_TO_OWNED, UNNECESSARY_JOIN, + ERR_EXPECT, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2431,8 +2456,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("expect", [_]) => match method_call(recv) { Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), + Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, msrv, span, err_span), _ => expect_used::check(cx, expr, recv), }, + ("extend", [arg]) => { string_extend_chars::check(cx, expr, recv, arg); extend_with_drain::check(cx, expr, recv, arg); @@ -2574,6 +2601,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); }, }, + _ => {}, } } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 680b2eb1da72..0bb818e20790 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -156,7 +156,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, EXPECT_ERR. /// /// The minimum rust version that the project supports (msrv: Option = None), diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index fce93153d96e..12191109b8c9 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -30,6 +30,6 @@ msrv_aliases! { 1,34,0 { TRY_FROM } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,28,0 { FROM_BOOL } - 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST } + 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } } diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed new file mode 100644 index 000000000000..7e18d70bae40 --- /dev/null +++ b/tests/ui/err_expect.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +struct MyTypeNonDebug; + +#[derive(Debug)] +struct MyTypeDebug; + +fn main() { + let test_debug: Result = Ok(MyTypeDebug); + test_debug.expect_err("Testing debug type"); + + let test_non_debug: Result = Ok(MyTypeNonDebug); + test_non_debug.err().expect("Testing non debug type"); +} diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs new file mode 100644 index 000000000000..bf8c3c9fb8c9 --- /dev/null +++ b/tests/ui/err_expect.rs @@ -0,0 +1,14 @@ +// run-rustfix + +struct MyTypeNonDebug; + +#[derive(Debug)] +struct MyTypeDebug; + +fn main() { + let test_debug: Result = Ok(MyTypeDebug); + test_debug.err().expect("Testing debug type"); + + let test_non_debug: Result = Ok(MyTypeNonDebug); + test_non_debug.err().expect("Testing non debug type"); +} diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr new file mode 100644 index 000000000000..ffd97e00a5c0 --- /dev/null +++ b/tests/ui/err_expect.stderr @@ -0,0 +1,10 @@ +error: called `.err().expect()` on a `Result` value + --> $DIR/err_expect.rs:10:16 + | +LL | test_debug.err().expect("Testing debug type"); + | ^^^^^^^^^^^^ help: try: `expect_err` + | + = note: `-D clippy::err-expect` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 72e9bf9eb368..7666d01ffe12 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -145,6 +145,11 @@ fn int_from_bool() -> u8 { true as u8 } +fn err_expect() { + let x: Result = Ok(10); + x.err().expect("Testing expect_err"); +} + fn main() { filter_map_next(); checked_conversion(); @@ -162,6 +167,7 @@ fn main() { missing_const_for_fn(); unnest_or_patterns(); int_from_bool(); + err_expect(); } mod just_under_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 6b3fdb0844b4..9ed6308f1153 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:186:24 + --> $DIR/min_rust_version_attr.rs:192:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:185:9 + --> $DIR/min_rust_version_attr.rs:191:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL ~ assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:198:24 + --> $DIR/min_rust_version_attr.rs:204:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:197:9 + --> $DIR/min_rust_version_attr.rs:203:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From a7125b0393c70c3d25feb7e063c312463694a1f6 Mon Sep 17 00:00:00 2001 From: infrandomness Date: Wed, 6 Apr 2022 19:24:49 +0200 Subject: [PATCH 74/82] Fix mistakes in documentation : - err() was meant to be employed instead of ok() - wraps comment --- clippy_lints/src/methods/err_expect.rs | 3 ++- clippy_lints/src/methods/mod.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index 887f5c1cb3b4..be9d4ad94fb8 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -19,7 +19,8 @@ pub(super) fn check( ) { if_chain! { if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); - // Test the version to make sure the lint can be showed (expect_err has been introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) + // Test the version to make sure the lint can be showed (expect_err has been + // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) if meets_msrv(msrv, &msrvs::EXPECT_ERR); // Grabs the `Result` type diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4475f8eaf597..c5c871c9d291 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -368,7 +368,7 @@ declare_clippy_lint! { /// Checks for `.err().expect()` calls on the `Result` type. /// /// ### Why is this bad? - /// `.expect_err()` can be called directly to avoid the extra type conversion from `ok()`. + /// `.expect_err()` can be called directly to avoid the extra type conversion from `err()`. /// /// ### Example /// ```should_panic From b0edbca0e6e4ce771bc5ecf54b9b35a824af07aa Mon Sep 17 00:00:00 2001 From: Paolo Borelli Date: Mon, 4 Apr 2022 18:38:38 +0200 Subject: [PATCH 75/82] new lint cast_abs_to_unsigned Add a lint to detect cast to unsigned for abs() and suggest unsigned_abs() to avoid panic when called on MIN. --- CHANGELOG.md | 1 + .../src/casts/cast_abs_to_unsigned.rs | 42 +++++++++++++++++++ clippy_lints/src/casts/mod.rs | 27 +++++++++++- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/utils/conf.rs | 2 +- clippy_utils/src/msrvs.rs | 2 +- tests/ui/cast.rs | 2 +- tests/ui/cast_abs_to_unsigned.fixed | 8 ++++ tests/ui/cast_abs_to_unsigned.rs | 8 ++++ tests/ui/cast_abs_to_unsigned.stderr | 10 +++++ tests/ui/min_rust_version_attr.rs | 6 +++ tests/ui/min_rust_version_attr.stderr | 8 ++-- 14 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 clippy_lints/src/casts/cast_abs_to_unsigned.rs create mode 100644 tests/ui/cast_abs_to_unsigned.fixed create mode 100644 tests/ui/cast_abs_to_unsigned.rs create mode 100644 tests/ui/cast_abs_to_unsigned.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 430154ac34d1..fd0e58d51165 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3201,6 +3201,7 @@ Released 2018-09-13 [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons +[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned [`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor [`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs new file mode 100644 index 000000000000..e9b0f1f672de --- /dev/null +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -0,0 +1,42 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::{meets_msrv, msrvs}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_semver::RustcVersion; + +use super::CAST_ABS_TO_UNSIGNED; + +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + msrv: &Option, +) { + if_chain! { + if meets_msrv(msrv.as_ref(), &msrvs::UNSIGNED_ABS); + if cast_from.is_integral(); + if cast_to.is_integral(); + if cast_from.is_signed(); + if !cast_to.is_signed(); + if let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind; + if let method_name = method_path.ident.name.as_str(); + if method_name == "abs"; + then { + span_lint_and_sugg( + cx, + CAST_ABS_TO_UNSIGNED, + expr.span, + &format!("casting the result of `{}::{}()` to {}", cast_from, method_name, cast_to), + "replace with", + format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index be59145afa00..55c1f085657b 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -1,3 +1,4 @@ +mod cast_abs_to_unsigned; mod cast_enum_constructor; mod cast_lossless; mod cast_possible_truncation; @@ -473,6 +474,28 @@ declare_clippy_lint! { "casts from an enum tuple constructor to an integer" } +declare_clippy_lint! { + /// ### What it does + /// Checks for uses of the `abs()` method that cast the result to unsigned. + /// + /// ### Why is this bad? + /// The `unsigned_abs()` method avoids panic when called on the MIN value. + /// + /// ### Example + /// ```rust + /// let x: i32 = -42; + /// let y: u32 = x.abs() as u32; + /// ``` + /// Use instead: + /// let x: i32 = -42; + /// let y: u32 = x.unsigned_abs(); + /// ``` + #[clippy::version = "1.61.0"] + pub CAST_ABS_TO_UNSIGNED, + suspicious, + "casting the result of `abs()` to an unsigned integer can panic" +} + pub struct Casts { msrv: Option, } @@ -500,7 +523,8 @@ impl_lint_pass!(Casts => [ CHAR_LIT_AS_U8, PTR_AS_PTR, CAST_ENUM_TRUNCATION, - CAST_ENUM_CONSTRUCTOR + CAST_ENUM_CONSTRUCTOR, + CAST_ABS_TO_UNSIGNED ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -536,6 +560,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); + cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); } cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index b2f674e9c882..3d092ac87a43 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -23,6 +23,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(casts::CAST_REF_TO_MUT), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index fb6612f5c32c..1aa98a6ec98d 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -70,6 +70,7 @@ store.register_lints(&[ cargo::REDUNDANT_FEATURE_NAMES, cargo::WILDCARD_DEPENDENCIES, case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + casts::CAST_ABS_TO_UNSIGNED, casts::CAST_ENUM_CONSTRUCTOR, casts::CAST_ENUM_TRUNCATION, casts::CAST_LOSSLESS, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 4c3fbb6c4a86..82f45b5fd58b 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -7,6 +7,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), + LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0bb818e20790..e149a201b7a7 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -156,7 +156,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, EXPECT_ERR. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, EXPECT_ERR, CAST_ABS_TO_UNSIGNED. /// /// The minimum rust version that the project supports (msrv: Option = None), diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 12191109b8c9..0424e0672026 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -14,7 +14,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,53,0 { OR_PATTERNS, MANUAL_BITS } 1,52,0 { STR_SPLIT_ONCE } - 1,51,0 { BORROW_AS_PTR } + 1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS } 1,50,0 { BOOL_THEN } 1,47,0 { TAU } 1,46,0 { CONST_IF_MATCH } diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index 2e31ad3172ee..cf85a5ca931d 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -7,7 +7,7 @@ clippy::cast_sign_loss, clippy::cast_possible_wrap )] -#[allow(clippy::no_effect, clippy::unnecessary_operation)] +#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] fn main() { // Test clippy::cast_precision_loss let x0 = 1i32; diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed new file mode 100644 index 000000000000..4ec2465be06d --- /dev/null +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -0,0 +1,8 @@ +// run-rustfix +#![warn(clippy::cast_abs_to_unsigned)] + +fn main() { + let x: i32 = -42; + let y: u32 = x.unsigned_abs(); + println!("The absolute value of {} is {}", x, y); +} diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs new file mode 100644 index 000000000000..59b9c8c36788 --- /dev/null +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -0,0 +1,8 @@ +// run-rustfix +#![warn(clippy::cast_abs_to_unsigned)] + +fn main() { + let x: i32 = -42; + let y: u32 = x.abs() as u32; + println!("The absolute value of {} is {}", x, y); +} diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr new file mode 100644 index 000000000000..eb12857357a4 --- /dev/null +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -0,0 +1,10 @@ +error: casting the result of `i32::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:6:18 + | +LL | let y: u32 = x.abs() as u32; + | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` + | + = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 7666d01ffe12..f83c3e0e281c 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -150,6 +150,11 @@ fn err_expect() { x.err().expect("Testing expect_err"); } +fn cast_abs_to_unsigned() { + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + fn main() { filter_map_next(); checked_conversion(); @@ -168,6 +173,7 @@ fn main() { unnest_or_patterns(); int_from_bool(); err_expect(); + cast_abs_to_unsigned(); } mod just_under_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 9ed6308f1153..de225eb740d0 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:192:24 + --> $DIR/min_rust_version_attr.rs:198:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:191:9 + --> $DIR/min_rust_version_attr.rs:197:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL ~ assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:204:24 + --> $DIR/min_rust_version_attr.rs:210:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:203:9 + --> $DIR/min_rust_version_attr.rs:209:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From f2bbb5fac0bd21c2ccd06f27c3b99d4f7ed51841 Mon Sep 17 00:00:00 2001 From: Paolo Borelli Date: Thu, 7 Apr 2022 11:29:02 +0200 Subject: [PATCH 76/82] conf: fix lint name in comment The lint name is ERR_EXPECT, not EXPECT_ERR --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index e149a201b7a7..271c3a3dd181 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -156,7 +156,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, EXPECT_ERR, CAST_ABS_TO_UNSIGNED. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED. /// /// The minimum rust version that the project supports (msrv: Option = None), From 182b7c38d7a55bb6f9f238085c6bebf7d28367eb Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 6 Apr 2022 15:35:49 +0100 Subject: [PATCH 77/82] Fix `as_deref_mut` false positives in `needless_option_as_deref` Also moves the lint to the methods directory --- clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_complexity.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 - clippy_lints/src/methods/mod.rs | 29 +++++++++ .../src/methods/needless_option_as_deref.rs | 37 +++++++++++ clippy_lints/src/needless_option_as_deref.rs | 65 ------------------- tests/ui/needless_option_as_deref.fixed | 30 ++++++++- tests/ui/needless_option_as_deref.rs | 30 ++++++++- tests/ui/needless_option_as_deref.stderr | 12 +++- 10 files changed, 136 insertions(+), 75 deletions(-) create mode 100644 clippy_lints/src/methods/needless_option_as_deref.rs delete mode 100644 clippy_lints/src/needless_option_as_deref.rs diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 1fb3ca1fd9b2..341c7e397d35 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -176,6 +176,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), + LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::OK_EXPECT), @@ -225,7 +226,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), - LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index a2ce69065f94..10369a855ae6 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -44,6 +44,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::MANUAL_SPLIT_ONCE), LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), + LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), @@ -60,7 +61,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index bba3cae45f5e..e5f5c6cd31c8 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -315,6 +315,7 @@ store.register_lints(&[ methods::MAP_FLATTEN, methods::MAP_IDENTITY, methods::MAP_UNWRAP_OR, + methods::NEEDLESS_OPTION_AS_DEREF, methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, methods::OK_EXPECT, @@ -386,7 +387,6 @@ store.register_lints(&[ needless_continue::NEEDLESS_CONTINUE, needless_for_each::NEEDLESS_FOR_EACH, needless_late_init::NEEDLESS_LATE_INIT, - needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF, needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, needless_question_mark::NEEDLESS_QUESTION_MARK, needless_update::NEEDLESS_UPDATE, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8dab039f24fe..a9a77f5ae046 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -308,7 +308,6 @@ mod needless_borrowed_ref; mod needless_continue; mod needless_for_each; mod needless_late_init; -mod needless_option_as_deref; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -536,7 +535,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(ptr::Ptr)); store.register_late_pass(|| Box::new(ptr_eq::PtrEq)); store.register_late_pass(|| Box::new(needless_bool::NeedlessBool)); - store.register_late_pass(|| Box::new(needless_option_as_deref::OptionNeedlessDeref)); store.register_late_pass(|| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach)); store.register_late_pass(|| Box::new(misc::MiscLints)); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c5c871c9d291..4a2bcca13b42 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -41,6 +41,7 @@ mod map_collect_result_unit; mod map_flatten; mod map_identity; mod map_unwrap_or; +mod needless_option_as_deref; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -2106,6 +2107,30 @@ declare_clippy_lint! { "using `.collect::>().join(\"\")` on an iterator" } +declare_clippy_lint! { + /// ### What it does + /// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`, + /// for example, `Option<&T>::as_deref()` returns the same type. + /// + /// ### Why is this bad? + /// Redundant code and improving readability. + /// + /// ### Example + /// ```rust + /// let a = Some(&1); + /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> + /// ``` + /// Could be written as: + /// ```rust + /// let a = Some(&1); + /// let b = a; + /// ``` + #[clippy::version = "1.57.0"] + pub NEEDLESS_OPTION_AS_DEREF, + complexity, + "no-op use of `deref` or `deref_mut` method to `Option`." +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2193,6 +2218,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_TO_OWNED, UNNECESSARY_JOIN, ERR_EXPECT, + NEEDLESS_OPTION_AS_DEREF, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2425,6 +2451,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, + ("as_deref" | "as_deref_mut", []) => { + needless_option_as_deref::check(cx, expr, recv, name); + }, ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs new file mode 100644 index 000000000000..7030baf19ff5 --- /dev/null +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -0,0 +1,37 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::path_res; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::usage::local_used_after_expr; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::NEEDLESS_OPTION_AS_DEREF; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: &str) { + let typeck = cx.typeck_results(); + let outer_ty = typeck.expr_ty(expr); + + if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) { + if name == "as_deref_mut" && recv.is_syntactic_place_expr() { + let Res::Local(binding_id) = path_res(cx, recv) else { return }; + + if local_used_after_expr(cx, binding_id, recv) { + return; + } + } + + span_lint_and_sugg( + cx, + NEEDLESS_OPTION_AS_DEREF, + expr.span, + "derefed type is same as origin", + "try this", + snippet_opt(cx, recv.span).unwrap(), + Applicability::MachineApplicable, + ); + } +} diff --git a/clippy_lints/src/needless_option_as_deref.rs b/clippy_lints/src/needless_option_as_deref.rs deleted file mode 100644 index 9d3d7d1f24cb..000000000000 --- a/clippy_lints/src/needless_option_as_deref.rs +++ /dev/null @@ -1,65 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; -use clippy_utils::ty::is_type_diagnostic_item; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for no-op uses of Option::{as_deref,as_deref_mut}, - /// for example, `Option<&T>::as_deref()` returns the same type. - /// - /// ### Why is this bad? - /// Redundant code and improving readability. - /// - /// ### Example - /// ```rust - /// let a = Some(&1); - /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> - /// ``` - /// Could be written as: - /// ```rust - /// let a = Some(&1); - /// let b = a; - /// ``` - #[clippy::version = "1.57.0"] - pub NEEDLESS_OPTION_AS_DEREF, - complexity, - "no-op use of `deref` or `deref_mut` method to `Option`." -} - -declare_lint_pass!(OptionNeedlessDeref=> [ - NEEDLESS_OPTION_AS_DEREF, -]); - -impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { - return; - } - let typeck = cx.typeck_results(); - let outer_ty = typeck.expr_ty(expr); - - if_chain! { - if is_type_diagnostic_item(cx,outer_ty,sym::Option); - if let ExprKind::MethodCall(path, [sub_expr], _) = expr.kind; - let symbol = path.ident.as_str(); - if symbol == "as_deref" || symbol == "as_deref_mut"; - if outer_ty == typeck.expr_ty(sub_expr); - then{ - span_lint_and_sugg( - cx, - NEEDLESS_OPTION_AS_DEREF, - expr.span, - "derefed type is same as origin", - "try this", - snippet_opt(cx,sub_expr.span).unwrap(), - Applicability::MachineApplicable - ); - } - } - } -} diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed index d721452ae888..c09b07db3dca 100644 --- a/tests/ui/needless_option_as_deref.fixed +++ b/tests/ui/needless_option_as_deref.fixed @@ -1,13 +1,41 @@ // run-rustfix -#[warn(clippy::needless_option_as_deref)] +#![allow(unused)] +#![warn(clippy::needless_option_as_deref)] fn main() { // should lint let _: Option<&usize> = Some(&1); let _: Option<&mut usize> = Some(&mut 1); + let mut y = 0; + let mut x = Some(&mut y); + let _ = x; + // should not lint let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + + // #7846 + let mut i = 0; + let mut opt_vec = vec![Some(&mut i)]; + opt_vec[0].as_deref_mut().unwrap(); + + let mut i = 0; + let x = &mut Some(&mut i); + (*x).as_deref_mut(); + + // #8047 + let mut y = 0; + let mut x = Some(&mut y); + x.as_deref_mut(); + dbg!(x); +} + +struct S<'a> { + opt: Option<&'a mut usize>, +} + +fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { + s.opt.as_deref_mut() } diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs index bb15512adf6a..c3ba27ecccf2 100644 --- a/tests/ui/needless_option_as_deref.rs +++ b/tests/ui/needless_option_as_deref.rs @@ -1,13 +1,41 @@ // run-rustfix -#[warn(clippy::needless_option_as_deref)] +#![allow(unused)] +#![warn(clippy::needless_option_as_deref)] fn main() { // should lint let _: Option<&usize> = Some(&1).as_deref(); let _: Option<&mut usize> = Some(&mut 1).as_deref_mut(); + let mut y = 0; + let mut x = Some(&mut y); + let _ = x.as_deref_mut(); + // should not lint let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + + // #7846 + let mut i = 0; + let mut opt_vec = vec![Some(&mut i)]; + opt_vec[0].as_deref_mut().unwrap(); + + let mut i = 0; + let x = &mut Some(&mut i); + (*x).as_deref_mut(); + + // #8047 + let mut y = 0; + let mut x = Some(&mut y); + x.as_deref_mut(); + dbg!(x); +} + +struct S<'a> { + opt: Option<&'a mut usize>, +} + +fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { + s.opt.as_deref_mut() } diff --git a/tests/ui/needless_option_as_deref.stderr b/tests/ui/needless_option_as_deref.stderr index 5dd507b4a71e..bc07db5b38ed 100644 --- a/tests/ui/needless_option_as_deref.stderr +++ b/tests/ui/needless_option_as_deref.stderr @@ -1,5 +1,5 @@ error: derefed type is same as origin - --> $DIR/needless_option_as_deref.rs:7:29 + --> $DIR/needless_option_as_deref.rs:8:29 | LL | let _: Option<&usize> = Some(&1).as_deref(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)` @@ -7,10 +7,16 @@ LL | let _: Option<&usize> = Some(&1).as_deref(); = note: `-D clippy::needless-option-as-deref` implied by `-D warnings` error: derefed type is same as origin - --> $DIR/needless_option_as_deref.rs:8:33 + --> $DIR/needless_option_as_deref.rs:9:33 | LL | let _: Option<&mut usize> = Some(&mut 1).as_deref_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)` -error: aborting due to 2 previous errors +error: derefed type is same as origin + --> $DIR/needless_option_as_deref.rs:13:13 + | +LL | let _ = x.as_deref_mut(); + | ^^^^^^^^^^^^^^^^ help: try this: `x` + +error: aborting due to 3 previous errors From db1d5dca5071104cbea2f23f5ec133d7bce66c15 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 7 Apr 2022 16:24:10 +0100 Subject: [PATCH 78/82] Fix internal::INVALID_PATHS lint --- clippy_lints/src/utils/internal_lints.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index e995dbd76ff7..25d74b8c4993 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -25,7 +25,7 @@ use rustc_hir::{ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::ty::{self, subst::GenericArgKind}; +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy}; use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; @@ -934,7 +934,16 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { // implementations of native types. Check lang items. let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); let lang_items = cx.tcx.lang_items(); - for item_def_id in lang_items.items().iter().flatten() { + // This list isn't complete, but good enough for our current list of paths. + let incoherent_impls = [ + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), + SimplifiedTypeGen::SliceSimplifiedType, + SimplifiedTypeGen::StrSimplifiedType, + ] + .iter() + .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); + for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { let lang_item_path = cx.get_def_path(*item_def_id); if path_syms.starts_with(&lang_item_path) { if let [item] = &path_syms[lang_item_path.len()..] { From f23a9fb295189ed542f8c4391d14c0f592ee0d28 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 7 Apr 2022 16:24:33 +0100 Subject: [PATCH 79/82] Bump Clippy Version -> 0.1.62 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bc7833f85816..dd6518d5241b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.61" +version = "0.1.62" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 66e61660d313..aebf9a87cabd 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.61" +version = "0.1.62" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index d3ed8da4499f..0b1fd95c3453 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.61" +version = "0.1.62" edition = "2021" publish = false From f5e32dc6471a791a763bbc0ac1617e5d76195637 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 7 Apr 2022 16:24:55 +0100 Subject: [PATCH 80/82] Bump nightly version -> 2022-04-07 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 5befb856a023..bb29c71e9f45 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-03-24" +channel = "nightly-2022-04-07" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From a2fdbb5f53bd0999db59fc53e9f411170b6556f9 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 7 Apr 2022 16:26:43 +0100 Subject: [PATCH 81/82] Bump changelog stable version -> 1.60 --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 430154ac34d1..efc5d8b370c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,9 @@ document. [57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master) -## Rust 1.60 (beta) +## Rust 1.60 -Current beta, release 2022-04-07 +Current stable, released 2022-04-07 [0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b) @@ -142,7 +142,7 @@ Current beta, release 2022-04-07 ## Rust 1.59 -Current stable, release 2022-02-24 +Released 2022-02-24 [e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589) From 6ab4508350add2549b2f4b7b052057992a804128 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 7 Apr 2022 18:05:20 +0100 Subject: [PATCH 82/82] Allow raw lint descriptions update_lints now understands raw strings in declare_clippy_lint descriptions. Co-authored-by: Alex Macleod --- CHANGELOG.md | 1 + clippy_dev/src/update_lints.rs | 5 ++++- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 + 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 918f0b48e507..b4097ea86a51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3272,6 +3272,7 @@ Released 2018-09-13 [`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op [`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op +[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 4e48b670457e..59db51fbfac5 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -359,7 +359,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { // group, Ident(group) Comma // "description" } - Literal{kind: LiteralKind::Str{..}, ..}(desc) CloseBrace + Literal{..}(desc) CloseBrace ); lints.push(Lint::new(name, group, desc, module)); } @@ -397,6 +397,9 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec) { /// Removes the line splices and surrounding quotes from a string literal fn remove_line_splices(s: &str) -> String { let s = s + .strip_prefix('r') + .unwrap_or(s) + .trim_matches('#') .strip_prefix('"') .and_then(|s| s.strip_suffix('"')) .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s)); diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index c95b791f43e6..14ca93b5f3c1 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -157,6 +157,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::CHARS_NEXT_CMP), LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::CLONE_ON_COPY), + LintId::of(methods::ERR_EXPECT), LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::FILTER_MAP_IDENTITY), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 1c4ccc15e831..532590aaa5a3 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -286,6 +286,7 @@ store.register_lints(&[ methods::CLONE_DOUBLE_REF, methods::CLONE_ON_COPY, methods::CLONE_ON_REF_PTR, + methods::ERR_EXPECT, methods::EXPECT_FUN_CALL, methods::EXPECT_USED, methods::EXTEND_WITH_DRAIN, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index dcf399cf9562..3114afac8863 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -59,6 +59,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(methods::BYTES_NTH), LintId::of(methods::CHARS_LAST_CMP), LintId::of(methods::CHARS_NEXT_CMP), + LintId::of(methods::ERR_EXPECT), LintId::of(methods::INTO_ITER_ON_REF), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_NEXT_SLICE),