From 59be3e856f218f55be0f73bcfed6265772437ec7 Mon Sep 17 00:00:00 2001 From: Chase Wilson Date: Fri, 17 Jun 2022 11:39:59 -0500 Subject: [PATCH 0001/1126] Stabilized Option::unzip() --- library/core/src/option.rs | 11 +++++++---- library/core/tests/lib.rs | 1 - 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 28ea45ed235d..1d6784af2a5c 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1711,8 +1711,6 @@ impl Option<(T, U)> { /// # Examples /// /// ``` - /// #![feature(unzip_option)] - /// /// let x = Some((1, "hi")); /// let y = None::<(u8, u32)>; /// @@ -1720,8 +1718,13 @@ impl Option<(T, U)> { /// assert_eq!(y.unzip(), (None, None)); /// ``` #[inline] - #[unstable(feature = "unzip_option", issue = "87800", reason = "recently added")] - pub const fn unzip(self) -> (Option, Option) { + #[stable(feature = "unzip_option", since = "1.63.0")] + #[rustc_const_unstable(feature = "const_option", issue = "67441")] + pub const fn unzip(self) -> (Option, Option) + where + T: ~const Destruct, + U: ~const Destruct, + { match self { Some((a, b)) => (Some(a), Some(b)), None => (None, None), diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 63c9602abe75..30b9cb4dadc8 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -91,7 +91,6 @@ #![feature(strict_provenance)] #![feature(trusted_random_access)] #![feature(unsize)] -#![feature(unzip_option)] #![feature(const_array_from_ref)] #![feature(const_slice_from_ref)] #![feature(waker_getters)] From df8a62d4f380d656444a6fc4e704bcd3693f4b9a Mon Sep 17 00:00:00 2001 From: Chase Wilson Date: Wed, 7 Sep 2022 10:27:42 -0500 Subject: [PATCH 0002/1126] Use `CURRENT_RUSTC_VERSION` --- library/core/src/option.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 1d6784af2a5c..df5a20869a3f 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1718,7 +1718,7 @@ impl Option<(T, U)> { /// assert_eq!(y.unzip(), (None, None)); /// ``` #[inline] - #[stable(feature = "unzip_option", since = "1.63.0")] + #[stable(feature = "unzip_option", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_option", issue = "67441")] pub const fn unzip(self) -> (Option, Option) where From 0958f9486ba0cb8ecd0093ff3100af45d19529f3 Mon Sep 17 00:00:00 2001 From: kraktus Date: Mon, 3 Oct 2022 13:57:42 +0200 Subject: [PATCH 0003/1126] Add `manual_filter` lint for `Option` Share much of its implementation with `manual_map` and should greatly benefit from its previous feedback. --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/matches/manual_filter.rs | 181 +++++++++++++++ clippy_lints/src/matches/manual_map.rs | 213 +++++++++++------ clippy_lints/src/matches/mod.rs | 39 ++++ clippy_utils/src/sugg.rs | 6 +- src/docs.rs | 1 + src/docs/manual_filter.txt | 21 ++ tests/ui/manual_filter.fixed | 119 ++++++++++ tests/ui/manual_filter.rs | 243 ++++++++++++++++++++ tests/ui/manual_filter.stderr | 191 +++++++++++++++ 13 files changed, 951 insertions(+), 67 deletions(-) create mode 100644 clippy_lints/src/matches/manual_filter.rs create mode 100644 src/docs/manual_filter.txt create mode 100644 tests/ui/manual_filter.fixed create mode 100644 tests/ui/manual_filter.rs create mode 100644 tests/ui/manual_filter.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f12a6735962..cab2808ee3b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3986,6 +3986,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp +[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 9ad2a88eb26c..21d686fcdf54 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -135,6 +135,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(match_result_ok::MATCH_RESULT_OK), LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_FILTER), LintId::of(matches::MANUAL_MAP), LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index a58d066fa6b6..e3849e5a626b 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -27,6 +27,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(manual_strip::MANUAL_STRIP), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(matches::MANUAL_FILTER), LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_SINGLE_BINDING), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 307ec40f40b3..70defe3bcbd5 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -257,6 +257,7 @@ store.register_lints(&[ match_result_ok::MATCH_RESULT_OK, matches::COLLAPSIBLE_MATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, + matches::MANUAL_FILTER, matches::MANUAL_MAP, matches::MANUAL_UNWRAP_OR, matches::MATCH_AS_REF, diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs new file mode 100644 index 000000000000..9931f1268ab3 --- /dev/null +++ b/clippy_lints/src/matches/manual_filter.rs @@ -0,0 +1,181 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::LangItem::OptionSome; +use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, HirId, Pat, PatKind, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +use super::manual_map::{check_with, SomeExpr}; +use super::MANUAL_FILTER; + +#[derive(Default)] +struct NeedsUnsafeBlock(pub bool); + +impl<'tcx> Visitor<'tcx> for NeedsUnsafeBlock { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match expr.kind { + ExprKind::Block( + Block { + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + .. + }, + _, + ) => { + self.0 = true; + }, + _ => walk_expr(self, expr), + } + } +} + +// Function called on the `expr` of `[&+]Some((ref | ref mut) x) => ` +// Need to check if it's of the `if {} else {}` +// AND that only one `then/else_expr` resolves to `Some(x)` while the other resolves to `None` +// return `cond` if +fn get_cond_expr<'tcx>( + cx: &LateContext<'tcx>, + pat: &Pat<'_>, + expr: &'tcx Expr<'_>, + ctxt: SyntaxContext, +) -> Option> { + if_chain! { + if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); + if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; + if let PatKind::Binding(_,target, ..) = pat.kind; + if let (then_visitor, else_visitor) + = (handle_if_or_else_expr(cx, target, ctxt, then_expr), + handle_if_or_else_expr(cx, target, ctxt, else_expr)); + if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None` + then { + let mut needs_unsafe_block = NeedsUnsafeBlock::default(); + needs_unsafe_block.visit_expr(expr); + return Some(SomeExpr { + expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), + needs_unsafe_block: needs_unsafe_block.0, + needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond + }) + } + }; + None +} + +fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { + // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's + // checked by `NeedsUnsafeBlock` + if let ExprKind::Block(block, None) = expr.kind { + if block.stmts.is_empty() { + return block.expr; + } + }; + None +} + +fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { + peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr) +} + +// function called for each expression: +// Some(x) => if { +// +// } else { +// +// } +// Returns true if resolves to `Some(x)`, `false` otherwise +fn handle_if_or_else_expr<'tcx>( + cx: &LateContext<'_>, + target: HirId, + ctxt: SyntaxContext, + if_or_else_expr: &'tcx Expr<'_>, +) -> bool { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(if_or_else_expr) { + // there can be not statements in the block as they would be removed when switching to `.filter` + if let ExprKind::Call(callee, [arg]) = inner_expr.kind { + return ctxt == if_or_else_expr.span.ctxt() + && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) + && path_to_local_id(arg, target); + } + }; + false +} + +// given the closure: `|| ` +// returns `|&| ` +fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String { + if has_copy_trait { + let mut with_ampersand = body_str; + with_ampersand.insert(1, '&'); + with_ampersand + } else { + body_str + } +} + +pub(super) fn check_match<'tcx>( + cx: &LateContext<'tcx>, + scrutinee: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], + expr: &'tcx Expr<'_>, +) { + let ty = cx.typeck_results().expr_ty(expr); + if_chain! { + if is_type_diagnostic_item(cx, ty, sym::Option); + if arms.len() == 2; + if arms[0].guard.is_none(); + if arms[1].guard.is_none(); + then { + check(cx, expr, scrutinee, arms[0].pat, arms[0].body, Some(arms[1].pat), arms[1].body) + } + } +} + +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, + then_expr: &'tcx Expr<'_>, + else_expr: &'tcx Expr<'_>, +) { + check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); +} + +fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, +) { + if let Some(sugg_info) = check_with( + cx, + expr, + scrutinee, + then_pat, + then_body, + else_pat, + else_body, + get_cond_expr, + ) { + let body_str = add_ampersand_if_copy(sugg_info.body_str, sugg_info.scrutinee_impl_copy); + span_lint_and_sugg( + cx, + MANUAL_FILTER, + expr.span, + "manual implementation of `Option::filter`", + "try this", + if sugg_info.needs_brackets { + format!( + "{{ {}{}.filter({body_str}) }}", + sugg_info.scrutinee_str, sugg_info.as_ref_str + ) + } else { + format!("{}{}.filter({body_str})", sugg_info.scrutinee_str, sugg_info.as_ref_str) + }, + sugg_info.app, + ); + } +} diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index 76f5e1c941c7..2b6a07c5d744 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -1,10 +1,11 @@ +use super::MANUAL_MAP; use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; use clippy_utils::{ can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, sugg::Sugg, CaptureKind, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; @@ -16,8 +17,6 @@ use rustc_hir::{ use rustc_lint::LateContext; use rustc_span::{sym, SyntaxContext}; -use super::MANUAL_MAP; - pub(super) fn check_match<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -43,7 +42,6 @@ pub(super) fn check_if_let<'tcx>( check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); } -#[expect(clippy::too_many_lines)] fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -53,12 +51,59 @@ fn check<'tcx>( else_pat: Option<&'tcx Pat<'_>>, else_body: &'tcx Expr<'_>, ) { + if let Some(sugg_info) = check_with( + cx, + expr, + scrutinee, + then_pat, + then_body, + else_pat, + else_body, + get_some_expr, + ) { + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + if sugg_info.needs_brackets { + format!( + "{{ {}{}.map({}) }}", + sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str + ) + } else { + format!( + "{}{}.map({})", + sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str + ) + }, + sugg_info.app, + ); + } +} + +#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_lines)] +pub(super) fn check_with<'tcx, F>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, + get_some_expr_fn: F, +) -> Option> +where + F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, +{ let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) { - return; + return None; } let expr_ctxt = expr.span.ctxt(); @@ -78,29 +123,29 @@ fn check<'tcx>( (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { (then_body, pattern, ref_count, false) }, - _ => return, + _ => return None, }; // Top level or patterns aren't allowed in closures. if matches!(some_pat.kind, PatKind::Or(_)) { - return; + return None; } - let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) { + let some_expr = match get_some_expr_fn(cx, some_pat, some_expr, expr_ctxt) { Some(expr) => expr, - None => return, + None => return None, }; // These two lints will go back and forth with each other. if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) { - return; + return None; } // `map` won't perform any adjustments. if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { - return; + return None; } // Determine which binding mode to use. @@ -125,16 +170,16 @@ fn check<'tcx>( }); if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return; + return None; }, Some(CaptureKind::Ref(Mutability::Not)) | None => (), } } } }, - None => return, + None => return None, }; let mut app = Applicability::MachineApplicable; @@ -149,6 +194,7 @@ fn check<'tcx>( scrutinee_str.into() }; + let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { if_chain! { if !some_expr.needs_unsafe_block; @@ -161,7 +207,7 @@ fn check<'tcx>( && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) && binding_ref.is_some() { - return; + return None; } // `ref` and `ref mut` annotations were handled earlier. @@ -170,41 +216,47 @@ fn check<'tcx>( } else { "" }; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { - format!("|{annotation}{some_binding}| unsafe {{ {expr_snip} }}") + format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") } else { - format!("|{annotation}{some_binding}| {expr_snip}") + format!("|{annotation}{some_binding}| {closure_expr_snip}") } } } } else if !is_wild_none && explicit_ref.is_none() { // TODO: handle explicit reference annotations. let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; if some_expr.needs_unsafe_block { - format!("|{pat_snip}| unsafe {{ {expr_snip} }}") + format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}") } else { - format!("|{pat_snip}| {expr_snip}") + format!("|{pat_snip}| {closure_expr_snip}") } } else { // Refutable bindings and mixed reference annotations can't be handled by `map`. - return; + return None; }; - span_lint_and_sugg( - cx, - MANUAL_MAP, - expr.span, - "manual implementation of `Option::map`", - "try this", - if else_pat.is_none() && is_else_clause(cx.tcx, expr) { - format!("{{ {scrutinee_str}{as_ref_str}.map({body_str}) }}") - } else { - format!("{scrutinee_str}{as_ref_str}.map({body_str})") - }, + // relies on the fact that Option: Copy where T: copy + let scrutinee_impl_copy = is_copy(cx, scrutinee_ty); + + Some(SuggInfo { + needs_brackets: else_pat.is_none() && is_else_clause(cx.tcx, expr), + scrutinee_impl_copy, + scrutinee_str, + as_ref_str, + body_str, app, - ); + }) +} + +pub struct SuggInfo<'a> { + pub needs_brackets: bool, + pub scrutinee_impl_copy: bool, + pub scrutinee_str: String, + pub as_ref_str: &'a str, + pub body_str: String, + pub app: Applicability, } // Checks whether the expression could be passed as a function, or whether a closure is needed. @@ -222,7 +274,8 @@ fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Ex } } -enum OptionPat<'a> { +#[derive(Debug)] +pub(super) enum OptionPat<'a> { Wild, None, Some { @@ -234,14 +287,39 @@ enum OptionPat<'a> { }, } -struct SomeExpr<'tcx> { - expr: &'tcx Expr<'tcx>, - needs_unsafe_block: bool, +pub(super) struct SomeExpr<'tcx> { + pub expr: &'tcx Expr<'tcx>, + pub needs_unsafe_block: bool, + pub needs_negated: bool, // for `manual_filter` lint +} + +impl<'tcx> SomeExpr<'tcx> { + pub fn new_no_negated(expr: &'tcx Expr<'tcx>, needs_unsafe_block: bool) -> Self { + Self { + expr, + needs_unsafe_block, + needs_negated: false, + } + } + + pub fn to_snippet_with_context( + &self, + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + app: &mut Applicability, + ) -> Sugg<'tcx> { + let sugg = Sugg::hir_with_context(cx, self.expr, ctxt, "..", app); + if self.needs_negated { !sugg } else { sugg } + } } // Try to parse into a recognized `Option` pattern. // i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { +pub(super) fn try_parse_pattern<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ctxt: SyntaxContext, +) -> Option> { fn f<'tcx>( cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, @@ -268,36 +346,41 @@ fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: Syn // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. fn get_some_expr<'tcx>( cx: &LateContext<'tcx>, + _: &'tcx Pat<'_>, expr: &'tcx Expr<'_>, - needs_unsafe_block: bool, ctxt: SyntaxContext, ) -> Option> { - // TODO: Allow more complex expressions. - match expr.kind { - ExprKind::Call(callee, [arg]) - if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => - { - Some(SomeExpr { - expr: arg, - needs_unsafe_block, - }) - }, - ExprKind::Block( - Block { - stmts: [], - expr: Some(expr), - rules, - .. + fn get_some_expr_internal<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + needs_unsafe_block: bool, + ctxt: SyntaxContext, + ) -> Option> { + // TODO: Allow more complex expressions. + match expr.kind { + ExprKind::Call(callee, [arg]) + if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => + { + Some(SomeExpr::new_no_negated(arg, needs_unsafe_block)) }, - _, - ) => get_some_expr( - cx, - expr, - needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), - ctxt, - ), - _ => None, + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + rules, + .. + }, + _, + ) => get_some_expr_internal( + cx, + expr, + needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + ctxt, + ), + _ => None, + } } + get_some_expr_internal(cx, expr, false, ctxt) } // Checks for the `None` value. diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index e6b183fc05f2..cf1bd7a12ade 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,5 +1,6 @@ mod collapsible_match; mod infallible_destructuring_match; +mod manual_filter; mod manual_map; mod manual_unwrap_or; mod match_as_ref; @@ -898,6 +899,34 @@ declare_clippy_lint! { "reimplementation of `map`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `match` which could be implemented using `filter` + /// + /// ### Why is this bad? + /// Using the `filter` method is clearer and more concise. + /// + /// ### Example + /// ```rust + /// match Some(0) { + /// Some(x) => if x % 2 == 0 { + /// Some(x) + /// } else { + /// None + /// }, + /// None => None, + /// }; + /// ``` + /// Use instead: + /// ```rust + /// Some(0).filter(|&x| x % 2 == 0); + /// ``` + #[clippy::version = "1.65.0"] + pub MANUAL_FILTER, + complexity, + "reimplentation of `filter`" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -939,6 +968,7 @@ impl_lint_pass!(Matches => [ SIGNIFICANT_DROP_IN_SCRUTINEE, TRY_ERR, MANUAL_MAP, + MANUAL_FILTER, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -988,6 +1018,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !in_constant(cx, expr.hir_id) { manual_unwrap_or::check(cx, expr, ex, arms); manual_map::check_match(cx, expr, ex, arms); + manual_filter::check_match(cx, ex, arms, expr); } if self.infallible_destructuring_match_linted { @@ -1014,6 +1045,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if !in_constant(cx, expr.hir_id) { manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr); + manual_filter::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_then, + else_expr, + ); } } redundant_pattern_match::check_if_let( diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index ef836e84829b..e88542b77a67 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -1,7 +1,9 @@ //! Contains utility functions to generate suggestions. #![deny(clippy::missing_docs_in_private_items)] -use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite}; +use crate::source::{ + snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite, +}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; use rustc_ast::util::parser::AssocOp; @@ -110,7 +112,7 @@ impl<'a> Sugg<'a> { if expr.span.ctxt() == ctxt { Self::hir_from_snippet(expr, |span| snippet(cx, span, default)) } else { - let snip = snippet_with_applicability(cx, expr.span, default, applicability); + let snip = snippet_with_context(cx, expr.span, ctxt, default, applicability).0; Sugg::NonParen(snip) } } diff --git a/src/docs.rs b/src/docs.rs index 39540e4b0489..d24342076b69 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -256,6 +256,7 @@ docs! { "manual_async_fn", "manual_bits", "manual_clamp", + "manual_filter", "manual_filter_map", "manual_find", "manual_find_map", diff --git a/src/docs/manual_filter.txt b/src/docs/manual_filter.txt new file mode 100644 index 000000000000..19a4d9319d94 --- /dev/null +++ b/src/docs/manual_filter.txt @@ -0,0 +1,21 @@ +### What it does +Checks for usages of `match` which could be implemented using `filter` + +### Why is this bad? +Using the `filter` method is clearer and more concise. + +### Example +``` +match Some(0) { + Some(x) => if x % 2 == 0 { + Some(x) + } else { + None + }, + None => None, +}; +``` +Use instead: +``` +Some(0).filter(|&x| x % 2 == 0); +``` \ No newline at end of file diff --git a/tests/ui/manual_filter.fixed b/tests/ui/manual_filter.fixed new file mode 100644 index 000000000000..3553291b87df --- /dev/null +++ b/tests/ui/manual_filter.fixed @@ -0,0 +1,119 @@ +// run-rustfix + +#![warn(clippy::manual_filter)] +#![allow(unused_variables, dead_code)] + +fn main() { + Some(0).filter(|&x| x <= 0); + + Some(1).filter(|&x| x <= 0); + + Some(2).filter(|&x| x <= 0); + + Some(3).filter(|&x| x > 0); + + let y = Some(4); + y.filter(|&x| x <= 0); + + Some(5).filter(|&x| x > 0); + + Some(6).as_ref().filter(|&x| x > &0); + + let external_cond = true; + Some(String::new()).filter(|x| external_cond); + + Some(7).filter(|&x| external_cond); + + Some(8).filter(|&x| x != 0); + + Some(9).filter(|&x| x > 10 && x < 100); + + const fn f1() { + // Don't lint, `.filter` is not const + match Some(10) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + } + + #[allow(clippy::blocks_in_if_conditions)] + Some(11).filter(|&x| { + println!("foo"); + x > 10 && x < 100 + }); + + match Some(12) { + // Don't Lint, statement is lost by `.filter` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(13) { + // Don't Lint, because of `None => Some(1)` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => Some(1), + }; + + unsafe fn f(x: u32) -> bool { + true + } + let _ = Some(14).filter(|&x| unsafe { f(x) }); + let _ = Some(15).filter(|&x| unsafe { f(x) }); + + #[allow(clippy::redundant_pattern_matching)] + if let Some(_) = Some(16) { + Some(16) + } else { Some(16).filter(|&x| x % 2 == 0) }; + + match Some((17, 17)) { + // Not linted for now could be + Some((x, y)) => { + if y != x { + Some((x, y)) + } else { + None + } + }, + None => None, + }; + + struct NamedTuple { + pub x: u8, + pub y: (i32, u32), + } + + match Some(NamedTuple { + // Not linted for now could be + x: 17, + y: (18, 19), + }) { + Some(NamedTuple { x, y }) => { + if y.1 != x as u32 { + Some(NamedTuple { x, y }) + } else { + None + } + }, + None => None, + }; +} diff --git a/tests/ui/manual_filter.rs b/tests/ui/manual_filter.rs new file mode 100644 index 000000000000..aa9f90f752b1 --- /dev/null +++ b/tests/ui/manual_filter.rs @@ -0,0 +1,243 @@ +// run-rustfix + +#![warn(clippy::manual_filter)] +#![allow(unused_variables, dead_code)] + +fn main() { + match Some(0) { + None => None, + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + }; + + match Some(1) { + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + None => None, + }; + + match Some(2) { + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + _ => None, + }; + + match Some(3) { + Some(x) => { + if x > 0 { + Some(x) + } else { + None + } + }, + None => None, + }; + + let y = Some(4); + match y { + // Some(4) + None => None, + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + }; + + match Some(5) { + Some(x) => { + if x > 0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + match Some(6) { + Some(ref x) => { + if x > &0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + let external_cond = true; + match Some(String::new()) { + Some(x) => { + if external_cond { + Some(x) + } else { + None + } + }, + _ => None, + }; + + if let Some(x) = Some(7) { + if external_cond { Some(x) } else { None } + } else { + None + }; + + match &Some(8) { + &Some(x) => { + if x != 0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + match Some(9) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + + const fn f1() { + // Don't lint, `.filter` is not const + match Some(10) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + } + + #[allow(clippy::blocks_in_if_conditions)] + match Some(11) { + // Lint, statement is preserved by `.filter` + Some(x) => { + if { + println!("foo"); + x > 10 && x < 100 + } { + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(12) { + // Don't Lint, statement is lost by `.filter` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(13) { + // Don't Lint, because of `None => Some(1)` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => Some(1), + }; + + unsafe fn f(x: u32) -> bool { + true + } + let _ = match Some(14) { + Some(x) => { + if unsafe { f(x) } { + Some(x) + } else { + None + } + }, + None => None, + }; + let _ = match Some(15) { + Some(x) => unsafe { + if f(x) { Some(x) } else { None } + }, + None => None, + }; + + #[allow(clippy::redundant_pattern_matching)] + if let Some(_) = Some(16) { + Some(16) + } else if let Some(x) = Some(16) { + // Lint starting from here + if x % 2 == 0 { Some(x) } else { None } + } else { + None + }; + + match Some((17, 17)) { + // Not linted for now could be + Some((x, y)) => { + if y != x { + Some((x, y)) + } else { + None + } + }, + None => None, + }; + + struct NamedTuple { + pub x: u8, + pub y: (i32, u32), + } + + match Some(NamedTuple { + // Not linted for now could be + x: 17, + y: (18, 19), + }) { + Some(NamedTuple { x, y }) => { + if y.1 != x as u32 { + Some(NamedTuple { x, y }) + } else { + None + } + }, + None => None, + }; +} diff --git a/tests/ui/manual_filter.stderr b/tests/ui/manual_filter.stderr new file mode 100644 index 000000000000..53dea9229306 --- /dev/null +++ b/tests/ui/manual_filter.stderr @@ -0,0 +1,191 @@ +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:7:5 + | +LL | / match Some(0) { +LL | | None => None, +LL | | Some(x) => { +LL | | if x > 0 { +... | +LL | | }, +LL | | }; + | |_____^ help: try this: `Some(0).filter(|&x| x <= 0)` + | + = note: `-D clippy::manual-filter` implied by `-D warnings` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:18:5 + | +LL | / match Some(1) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | None +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(1).filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:29:5 + | +LL | / match Some(2) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | None +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(2).filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:40:5 + | +LL | / match Some(3) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(3).filter(|&x| x > 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:52:5 + | +LL | / match y { +LL | | // Some(4) +LL | | None => None, +LL | | Some(x) => { +... | +LL | | }, +LL | | }; + | |_____^ help: try this: `y.filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:64:5 + | +LL | / match Some(5) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(5).filter(|&x| x > 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:75:5 + | +LL | / match Some(6) { +LL | | Some(ref x) => { +LL | | if x > &0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(6).as_ref().filter(|&x| x > &0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:87:5 + | +LL | / match Some(String::new()) { +LL | | Some(x) => { +LL | | if external_cond { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).filter(|x| external_cond)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:98:5 + | +LL | / if let Some(x) = Some(7) { +LL | | if external_cond { Some(x) } else { None } +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `Some(7).filter(|&x| external_cond)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:104:5 + | +LL | / match &Some(8) { +LL | | &Some(x) => { +LL | | if x != 0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(8).filter(|&x| x != 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:115:5 + | +LL | / match Some(9) { +LL | | Some(x) => { +LL | | if x > 10 && x < 100 { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(9).filter(|&x| x > 10 && x < 100)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:141:5 + | +LL | / match Some(11) { +LL | | // Lint, statement is preserved by `.filter` +LL | | Some(x) => { +LL | | if { +... | +LL | | None => None, +LL | | }; + | |_____^ + | +help: try this + | +LL ~ Some(11).filter(|&x| { +LL + println!("foo"); +LL + x > 10 && x < 100 +LL ~ }); + | + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:185:13 + | +LL | let _ = match Some(14) { + | _____________^ +LL | | Some(x) => { +LL | | if unsafe { f(x) } { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(14).filter(|&x| unsafe { f(x) })` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:195:13 + | +LL | let _ = match Some(15) { + | _____________^ +LL | | Some(x) => unsafe { +LL | | if f(x) { Some(x) } else { None } +LL | | }, +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(15).filter(|&x| unsafe { f(x) })` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:205:12 + | +LL | } else if let Some(x) = Some(16) { + | ____________^ +LL | | // Lint starting from here +LL | | if x % 2 == 0 { Some(x) } else { None } +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `{ Some(16).filter(|&x| x % 2 == 0) }` + +error: aborting due to 15 previous errors + From b5d5682ac30615b35bd2f011c24a3c927420c87f Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sun, 31 Jul 2022 19:14:03 -0500 Subject: [PATCH 0004/1126] Make `core::mem::copy` const --- library/core/src/mem/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index d2dd2941d590..ee29a233efe1 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1005,7 +1005,7 @@ pub fn drop(_x: T) {} /// ``` #[inline] #[unstable(feature = "mem_copy_fn", issue = "98262")] -pub fn copy(x: &T) -> T { +pub const fn copy(x: &T) -> T { *x } From c7961da935952380b150ff3ce8c6ca2323a182ec Mon Sep 17 00:00:00 2001 From: zhaixiaojuan Date: Sat, 17 Sep 2022 18:00:34 +0800 Subject: [PATCH 0005/1126] Add loongarch64 abi support --- .../rustc_target/src/abi/call/loongarch.rs | 342 ++++++++++++++++++ compiler/rustc_target/src/abi/call/mod.rs | 2 + 2 files changed, 344 insertions(+) create mode 100644 compiler/rustc_target/src/abi/call/loongarch.rs diff --git a/compiler/rustc_target/src/abi/call/loongarch.rs b/compiler/rustc_target/src/abi/call/loongarch.rs new file mode 100644 index 000000000000..d29b479de5da --- /dev/null +++ b/compiler/rustc_target/src/abi/call/loongarch.rs @@ -0,0 +1,342 @@ +use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; +use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; +use crate::spec::HasTargetSpec; + +#[derive(Copy, Clone)] +enum RegPassKind { + Float(Reg), + Integer(Reg), + Unknown, +} + +#[derive(Copy, Clone)] +enum FloatConv { + FloatPair(Reg, Reg), + Float(Reg), + MixedPair(Reg, Reg), +} + +#[derive(Copy, Clone)] +struct CannotUseFpConv; + +fn is_loongarch_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool { + match arg.layout.abi { + Abi::Vector { .. } => true, + _ => arg.layout.is_aggregate(), + } +} + +fn should_use_fp_conv_helper<'a, Ty, C>( + cx: &C, + arg_layout: &TyAndLayout<'a, Ty>, + xlen: u64, + flen: u64, + field1_kind: &mut RegPassKind, + field2_kind: &mut RegPassKind, +) -> Result<(), CannotUseFpConv> +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + match arg_layout.abi { + Abi::Scalar(scalar) => match scalar.primitive() { + abi::Int(..) | abi::Pointer => { + if arg_layout.size.bits() > xlen { + return Err(CannotUseFpConv); + } + match (*field1_kind, *field2_kind) { + (RegPassKind::Unknown, _) => { + *field1_kind = RegPassKind::Integer(Reg { + kind: RegKind::Integer, + size: arg_layout.size, + }); + } + (RegPassKind::Float(_), RegPassKind::Unknown) => { + *field2_kind = RegPassKind::Integer(Reg { + kind: RegKind::Integer, + size: arg_layout.size, + }); + } + _ => return Err(CannotUseFpConv), + } + } + abi::F32 | abi::F64 => { + if arg_layout.size.bits() > flen { + return Err(CannotUseFpConv); + } + match (*field1_kind, *field2_kind) { + (RegPassKind::Unknown, _) => { + *field1_kind = + RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + } + (_, RegPassKind::Unknown) => { + *field2_kind = + RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + } + _ => return Err(CannotUseFpConv), + } + } + }, + Abi::Vector { .. } | Abi::Uninhabited => return Err(CannotUseFpConv), + Abi::ScalarPair(..) | Abi::Aggregate { .. } => match arg_layout.fields { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") + } + FieldsShape::Union(_) => { + if !arg_layout.is_zst() { + return Err(CannotUseFpConv); + } + } + FieldsShape::Array { count, .. } => { + for _ in 0..count { + let elem_layout = arg_layout.field(cx, 0); + should_use_fp_conv_helper( + cx, + &elem_layout, + xlen, + flen, + field1_kind, + field2_kind, + )?; + } + } + FieldsShape::Arbitrary { .. } => { + match arg_layout.variants { + abi::Variants::Multiple { .. } => return Err(CannotUseFpConv), + abi::Variants::Single { .. } => (), + } + for i in arg_layout.fields.index_by_increasing_offset() { + let field = arg_layout.field(cx, i); + should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?; + } + } + }, + } + Ok(()) +} + +fn should_use_fp_conv<'a, Ty, C>( + cx: &C, + arg: &TyAndLayout<'a, Ty>, + xlen: u64, + flen: u64, +) -> Option +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + let mut field1_kind = RegPassKind::Unknown; + let mut field2_kind = RegPassKind::Unknown; + if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() { + return None; + } + match (field1_kind, field2_kind) { + (RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)), + (RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)), + (RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)), + (RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)), + _ => None, + } +} + +fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u64) -> bool +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) { + match conv { + FloatConv::Float(f) => { + arg.cast_to(f); + } + FloatConv::FloatPair(l, r) => { + arg.cast_to(CastTarget::pair(l, r)); + } + FloatConv::MixedPair(l, r) => { + arg.cast_to(CastTarget::pair(l, r)); + } + } + return false; + } + + let total = arg.layout.size; + + // "Scalars wider than 2✕XLEN are passed by reference and are replaced in + // the argument list with the address." + // "Aggregates larger than 2✕XLEN bits are passed by reference and are + // replaced in the argument list with the address, as are C++ aggregates + // with nontrivial copy constructors, destructors, or vtables." + if total.bits() > 2 * xlen { + // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN. + if is_loongarch_aggregate(arg) { + arg.make_indirect(); + } + return true; + } + + let xlen_reg = match xlen { + 32 => Reg::i32(), + 64 => Reg::i64(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + if is_loongarch_aggregate(arg) { + if total.bits() <= xlen { + arg.cast_to(xlen_reg); + } else { + arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) }); + } + return false; + } + + // "When passed in registers, scalars narrower than XLEN bits are widened + // according to the sign of their type up to 32 bits, then sign-extended to + // XLEN bits." + extend_integer_width(arg, xlen); + false +} + +fn classify_arg<'a, Ty, C>( + cx: &C, + arg: &mut ArgAbi<'a, Ty>, + xlen: u64, + flen: u64, + is_vararg: bool, + avail_gprs: &mut u64, + avail_fprs: &mut u64, +) where + Ty: TyAbiInterface<'a, C> + Copy, +{ + if !is_vararg { + match should_use_fp_conv(cx, &arg.layout, xlen, flen) { + Some(FloatConv::Float(f)) if *avail_fprs >= 1 => { + *avail_fprs -= 1; + arg.cast_to(f); + return; + } + Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => { + *avail_fprs -= 2; + arg.cast_to(CastTarget::pair(l, r)); + return; + } + Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => { + *avail_gprs -= 1; + *avail_fprs -= 1; + arg.cast_to(CastTarget::pair(l, r)); + return; + } + _ => (), + } + } + + let total = arg.layout.size; + let align = arg.layout.align.abi.bits(); + + // "Scalars wider than 2✕XLEN are passed by reference and are replaced in + // the argument list with the address." + // "Aggregates larger than 2✕XLEN bits are passed by reference and are + // replaced in the argument list with the address, as are C++ aggregates + // with nontrivial copy constructors, destructors, or vtables." + if total.bits() > 2 * xlen { + // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN. + if is_loongarch_aggregate(arg) { + arg.make_indirect(); + } + if *avail_gprs >= 1 { + *avail_gprs -= 1; + } + return; + } + + let double_xlen_reg = match xlen { + 32 => Reg::i64(), + 64 => Reg::i128(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + + let xlen_reg = match xlen { + 32 => Reg::i32(), + 64 => Reg::i64(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + + if total.bits() > xlen { + let align_regs = align > xlen; + if is_loongarch_aggregate(arg) { + arg.cast_to(Uniform { + unit: if align_regs { double_xlen_reg } else { xlen_reg }, + total: Size::from_bits(xlen * 2), + }); + } + if align_regs && is_vararg { + *avail_gprs -= *avail_gprs % 2; + } + if *avail_gprs >= 2 { + *avail_gprs -= 2; + } else { + *avail_gprs = 0; + } + return; + } else if is_loongarch_aggregate(arg) { + arg.cast_to(xlen_reg); + if *avail_gprs >= 1 { + *avail_gprs -= 1; + } + return; + } + + // "When passed in registers, scalars narrower than XLEN bits are widened + // according to the sign of their type up to 32 bits, then sign-extended to + // XLEN bits." + if *avail_gprs >= 1 { + extend_integer_width(arg, xlen); + *avail_gprs -= 1; + } +} + +fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) { + if let Abi::Scalar(scalar) = arg.layout.abi { + if let abi::Int(i, _) = scalar.primitive() { + // 32-bit integers are always sign-extended + if i.size().bits() == 32 && xlen > 32 { + if let PassMode::Direct(ref mut attrs) = arg.mode { + attrs.ext(ArgExtension::Sext); + return; + } + } + } + } + + arg.extend_integer_width_to(xlen); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + let xlen = cx.data_layout().pointer_size.bits(); + let flen = match &cx.target_spec().llvm_abiname[..] { + "ilp32f" | "lp64f" => 32, + "ilp32d" | "lp64d" => 64, + _ => 0, + }; + + let mut avail_gprs = 8; + let mut avail_fprs = 8; + + if !fn_abi.ret.is_ignore() && classify_ret(cx, &mut fn_abi.ret, xlen, flen) { + avail_gprs -= 1; + } + + for (i, arg) in fn_abi.args.iter_mut().enumerate() { + if arg.is_ignore() { + continue; + } + classify_arg( + cx, + arg, + xlen, + flen, + i >= fn_abi.fixed_count as usize, + &mut avail_gprs, + &mut avail_fprs, + ); + } +} diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index d2fb8c32ffd2..bd3cb7951edb 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -10,6 +10,7 @@ mod arm; mod avr; mod bpf; mod hexagon; +mod loongarch; mod m68k; mod mips; mod mips64; @@ -696,6 +697,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { "amdgpu" => amdgpu::compute_abi_info(cx, self), "arm" => arm::compute_abi_info(cx, self), "avr" => avr::compute_abi_info(self), + "loongarch64" => loongarch::compute_abi_info(cx, self), "m68k" => m68k::compute_abi_info(self), "mips" => mips::compute_abi_info(cx, self), "mips64" => mips64::compute_abi_info(cx, self), From fc380ecd13f0a16519ebf1df64648d006a4985fc Mon Sep 17 00:00:00 2001 From: John Millikin Date: Sun, 18 Sep 2022 15:13:42 +0900 Subject: [PATCH 0006/1126] Adjust `tcp_quickack` feature to allow other `os::linux::net` features. --- library/std/src/os/android/net.rs | 4 ++-- library/std/src/os/linux/net.rs | 4 ++-- library/std/src/os/net/linux_ext/mod.rs | 9 +++++++++ library/std/src/os/net/{ => linux_ext}/tcp.rs | 0 library/std/src/os/net/{ => linux_ext}/tests.rs | 3 +-- library/std/src/os/net/mod.rs | 9 +++------ 6 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 library/std/src/os/net/linux_ext/mod.rs rename library/std/src/os/net/{ => linux_ext}/tcp.rs (100%) rename library/std/src/os/net/{ => linux_ext}/tests.rs (88%) diff --git a/library/std/src/os/android/net.rs b/library/std/src/os/android/net.rs index ff96125c37bd..53ef7e114c07 100644 --- a/library/std/src/os/android/net.rs +++ b/library/std/src/os/android/net.rs @@ -1,4 +1,4 @@ -//! Linux and Android-specific definitions for socket options. +//! Android-specific networking functionality. #![unstable(feature = "tcp_quickack", issue = "96256")] -pub use crate::os::net::tcp::TcpStreamExt; +pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/linux/net.rs b/library/std/src/os/linux/net.rs index ff96125c37bd..d67b369be27b 100644 --- a/library/std/src/os/linux/net.rs +++ b/library/std/src/os/linux/net.rs @@ -1,4 +1,4 @@ -//! Linux and Android-specific definitions for socket options. +//! Linux-specific networking functionality. #![unstable(feature = "tcp_quickack", issue = "96256")] -pub use crate::os::net::tcp::TcpStreamExt; +pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/net/linux_ext/mod.rs b/library/std/src/os/net/linux_ext/mod.rs new file mode 100644 index 000000000000..9bd66f3bb80d --- /dev/null +++ b/library/std/src/os/net/linux_ext/mod.rs @@ -0,0 +1,9 @@ +//! Linux and Android-specific networking functionality. + +#![doc(cfg(any(target_os = "linux", target_os = "android")))] + +#[unstable(feature = "tcp_quickack", issue = "96256")] +pub(crate) mod tcp; + +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/net/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs similarity index 100% rename from library/std/src/os/net/tcp.rs rename to library/std/src/os/net/linux_ext/tcp.rs diff --git a/library/std/src/os/net/tests.rs b/library/std/src/os/net/linux_ext/tests.rs similarity index 88% rename from library/std/src/os/net/tests.rs rename to library/std/src/os/net/linux_ext/tests.rs index 4704e3156913..2db4deed0363 100644 --- a/library/std/src/os/net/tests.rs +++ b/library/std/src/os/net/linux_ext/tests.rs @@ -1,9 +1,8 @@ -#[cfg(any(target_os = "android", target_os = "linux",))] #[test] fn quickack() { use crate::{ net::{test::next_test_ip4, TcpListener, TcpStream}, - os::net::tcp::TcpStreamExt, + os::net::linux_ext::tcp::TcpStreamExt, }; macro_rules! t { diff --git a/library/std/src/os/net/mod.rs b/library/std/src/os/net/mod.rs index d6d84d24ec48..5ec267c41e97 100644 --- a/library/std/src/os/net/mod.rs +++ b/library/std/src/os/net/mod.rs @@ -1,7 +1,4 @@ -//! Linux and Android-specific definitions for socket options. +//! OS-specific networking functionality. -#![unstable(feature = "tcp_quickack", issue = "96256")] -#![doc(cfg(any(target_os = "linux", target_os = "android",)))] -pub mod tcp; -#[cfg(test)] -mod tests; +#[cfg(any(target_os = "linux", target_os = "android", doc))] +pub(super) mod linux_ext; From 8f1e6eba343452ac48412f11d57aa7d206c8c3dd Mon Sep 17 00:00:00 2001 From: John Millikin Date: Sun, 18 Sep 2022 16:20:11 +0900 Subject: [PATCH 0007/1126] Move `unix_socket_abstract` feature API to `SocketAddrExt`. --- library/std/src/os/android/net.rs | 5 ++ library/std/src/os/linux/net.rs | 5 ++ library/std/src/os/net/linux_ext/addr.rs | 64 ++++++++++++++++ library/std/src/os/net/linux_ext/mod.rs | 3 + library/std/src/os/unix/net/addr.rs | 93 +++++++----------------- library/std/src/os/unix/net/tests.rs | 36 +++++---- 6 files changed, 123 insertions(+), 83 deletions(-) create mode 100644 library/std/src/os/net/linux_ext/addr.rs diff --git a/library/std/src/os/android/net.rs b/library/std/src/os/android/net.rs index 53ef7e114c07..7cecd1bbfaa9 100644 --- a/library/std/src/os/android/net.rs +++ b/library/std/src/os/android/net.rs @@ -1,4 +1,9 @@ //! Android-specific networking functionality. #![unstable(feature = "tcp_quickack", issue = "96256")] + +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub use crate::os::net::linux_ext::addr::SocketAddrExt; + +#[unstable(feature = "tcp_quickack", issue = "96256")] pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/linux/net.rs b/library/std/src/os/linux/net.rs index d67b369be27b..94081c8dd31c 100644 --- a/library/std/src/os/linux/net.rs +++ b/library/std/src/os/linux/net.rs @@ -1,4 +1,9 @@ //! Linux-specific networking functionality. #![unstable(feature = "tcp_quickack", issue = "96256")] + +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub use crate::os::net::linux_ext::addr::SocketAddrExt; + +#[unstable(feature = "tcp_quickack", issue = "96256")] pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/net/linux_ext/addr.rs b/library/std/src/os/net/linux_ext/addr.rs new file mode 100644 index 000000000000..df3fc8e6a3b6 --- /dev/null +++ b/library/std/src/os/net/linux_ext/addr.rs @@ -0,0 +1,64 @@ +//! Linux and Android-specific extensions to socket addresses. + +use crate::os::unix::net::SocketAddr; +use crate::sealed::Sealed; + +/// Platform-specific extensions to [`SocketAddr`]. +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub trait SocketAddrExt: Sealed { + /// Creates a Unix socket address in the abstract namespace. + /// + /// The abstract namespace is a Linux-specific extension that allows Unix + /// sockets to be bound without creating an entry in the filesystem. + /// Abstract sockets are unaffected by filesystem layout or permissions, + /// and no cleanup is necessary when the socket is closed. + /// + /// An abstract socket address name may contain any bytes, including zero. + /// + /// # Errors + /// + /// Returns an error if the name is longer than `SUN_LEN - 1`. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixListener, SocketAddr}; + /// use std::os::linux::net::SocketAddrExt; + /// + /// fn main() -> std::io::Result<()> { + /// let addr = SocketAddr::from_abstract_name(b"hidden")?; + /// let listener = match UnixListener::bind_addr(&addr) { + /// Ok(sock) => sock, + /// Err(err) => { + /// println!("Couldn't bind: {err:?}"); + /// return Err(err); + /// } + /// }; + /// Ok(()) + /// } + /// ``` + fn from_abstract_name(name: &N) -> crate::io::Result + where + N: AsRef<[u8]>; + + /// Returns the contents of this address if it is in the abstract namespace. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixListener, SocketAddr}; + /// use std::os::linux::net::SocketAddrExt; + /// + /// fn main() -> std::io::Result<()> { + /// let name = b"hidden"; + /// let name_addr = SocketAddr::from_abstract_name(name)?; + /// let socket = UnixListener::bind_addr(&name_addr)?; + /// let local_addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(local_addr.as_abstract_name(), Some(&name[..])); + /// Ok(()) + /// } + /// ``` + fn as_abstract_name(&self) -> Option<&[u8]>; +} diff --git a/library/std/src/os/net/linux_ext/mod.rs b/library/std/src/os/net/linux_ext/mod.rs index 9bd66f3bb80d..318ebacfd7a0 100644 --- a/library/std/src/os/net/linux_ext/mod.rs +++ b/library/std/src/os/net/linux_ext/mod.rs @@ -2,6 +2,9 @@ #![doc(cfg(any(target_os = "linux", target_os = "android")))] +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +pub(crate) mod addr; + #[unstable(feature = "tcp_quickack", issue = "96256")] pub(crate) mod tcp; diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 094085e19428..81ac829d21bc 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -1,6 +1,9 @@ use crate::ffi::OsStr; +#[cfg(any(doc, target_os = "android", target_os = "linux"))] +use crate::os::net::linux_ext; use crate::os::unix::ffi::OsStrExt; use crate::path::Path; +use crate::sealed::Sealed; use crate::sys::cvt; use crate::{fmt, io, mem, ptr}; @@ -224,31 +227,6 @@ impl SocketAddr { if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } } - /// Returns the contents of this address if it is an abstract namespace - /// without the leading null byte. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(unix_socket_abstract)] - /// use std::os::unix::net::{UnixListener, SocketAddr}; - /// - /// fn main() -> std::io::Result<()> { - /// let namespace = b"hidden"; - /// let namespace_addr = SocketAddr::from_abstract_namespace(&namespace[..])?; - /// let socket = UnixListener::bind_addr(&namespace_addr)?; - /// let local_addr = socket.local_addr().expect("Couldn't get local address"); - /// assert_eq!(local_addr.as_abstract_namespace(), Some(&namespace[..])); - /// Ok(()) - /// } - /// ``` - #[doc(cfg(any(target_os = "android", target_os = "linux")))] - #[cfg(any(doc, target_os = "android", target_os = "linux",))] - #[unstable(feature = "unix_socket_abstract", issue = "85410")] - pub fn as_abstract_namespace(&self) -> Option<&[u8]> { - if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } - } - fn address(&self) -> AddressKind<'_> { let len = self.len as usize - sun_path_offset(&self.addr); let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; @@ -265,62 +243,41 @@ impl SocketAddr { AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) } } +} - /// Creates an abstract domain socket address from a namespace - /// - /// An abstract address does not create a file unlike traditional path-based - /// Unix sockets. The advantage of this is that the address will disappear when - /// the socket bound to it is closed, so no filesystem clean up is required. - /// - /// The leading null byte for the abstract namespace is automatically added. - /// - /// This is a Linux-specific extension. See more at [`unix(7)`]. - /// - /// [`unix(7)`]: https://man7.org/linux/man-pages/man7/unix.7.html - /// - /// # Errors - /// - /// This will return an error if the given namespace is too long - /// - /// # Examples - /// - /// ```no_run - /// #![feature(unix_socket_abstract)] - /// use std::os::unix::net::{UnixListener, SocketAddr}; - /// - /// fn main() -> std::io::Result<()> { - /// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; - /// let listener = match UnixListener::bind_addr(&addr) { - /// Ok(sock) => sock, - /// Err(err) => { - /// println!("Couldn't bind: {err:?}"); - /// return Err(err); - /// } - /// }; - /// Ok(()) - /// } - /// ``` - #[doc(cfg(any(target_os = "android", target_os = "linux")))] - #[cfg(any(doc, target_os = "android", target_os = "linux",))] - #[unstable(feature = "unix_socket_abstract", issue = "85410")] - pub fn from_abstract_namespace(namespace: &[u8]) -> io::Result { +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +impl Sealed for SocketAddr {} + +#[doc(cfg(any(target_os = "android", target_os = "linux")))] +#[cfg(any(doc, target_os = "android", target_os = "linux"))] +#[unstable(feature = "unix_socket_abstract", issue = "85410")] +impl linux_ext::addr::SocketAddrExt for SocketAddr { + fn as_abstract_name(&self) -> Option<&[u8]> { + if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } + } + + fn from_abstract_name(name: &N) -> crate::io::Result + where + N: AsRef<[u8]>, + { + let name = name.as_ref(); unsafe { let mut addr: libc::sockaddr_un = mem::zeroed(); addr.sun_family = libc::AF_UNIX as libc::sa_family_t; - if namespace.len() + 1 > addr.sun_path.len() { + if name.len() + 1 > addr.sun_path.len() { return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - "namespace must be shorter than SUN_LEN", + "abstract socket name must be shorter than SUN_LEN", )); } crate::ptr::copy_nonoverlapping( - namespace.as_ptr(), + name.as_ptr(), addr.sun_path.as_mut_ptr().add(1) as *mut u8, - namespace.len(), + name.len(), ); - let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t; + let len = (sun_path_offset(&addr) + 1 + name.len()) as libc::socklen_t; SocketAddr::from_parts(addr, len) } } diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index e4499f9b6a6d..37fcfa8446b0 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -7,6 +7,12 @@ use crate::sys_common::io::test::tmpdir; use crate::thread; use crate::time::Duration; +#[cfg(target_os = "android")] +use crate::os::android::net::SocketAddrExt; + +#[cfg(target_os = "linux")] +use crate::os::linux::net::SocketAddrExt; + macro_rules! or_panic { ($e:expr) => { match $e { @@ -404,7 +410,7 @@ fn test_abstract_stream_connect() { let msg1 = b"hello"; let msg2 = b"world"; - let socket_addr = or_panic!(SocketAddr::from_abstract_namespace(b"namespace")); + let socket_addr = or_panic!(SocketAddr::from_abstract_name(b"name")); let listener = or_panic!(UnixListener::bind_addr(&socket_addr)); let thread = thread::spawn(move || { @@ -418,7 +424,7 @@ fn test_abstract_stream_connect() { let mut stream = or_panic!(UnixStream::connect_addr(&socket_addr)); let peer = or_panic!(stream.peer_addr()); - assert_eq!(peer.as_abstract_namespace().unwrap(), b"namespace"); + assert_eq!(peer.as_abstract_name().unwrap(), b"name"); or_panic!(stream.write_all(msg1)); let mut buf = vec![]; @@ -432,7 +438,7 @@ fn test_abstract_stream_connect() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] fn test_abstract_stream_iter() { - let addr = or_panic!(SocketAddr::from_abstract_namespace(b"hidden")); + let addr = or_panic!(SocketAddr::from_abstract_name(b"hidden")); let listener = or_panic!(UnixListener::bind_addr(&addr)); let thread = thread::spawn(move || { @@ -454,13 +460,13 @@ fn test_abstract_stream_iter() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] fn test_abstract_datagram_bind_send_to_addr() { - let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns1")); + let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns1")); let sock1 = or_panic!(UnixDatagram::bind_addr(&addr1)); let local = or_panic!(sock1.local_addr()); - assert_eq!(local.as_abstract_namespace().unwrap(), b"ns1"); + assert_eq!(local.as_abstract_name().unwrap(), b"ns1"); - let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns2")); + let addr2 = or_panic!(SocketAddr::from_abstract_name(b"ns2")); let sock2 = or_panic!(UnixDatagram::bind_addr(&addr2)); let msg = b"hello world"; @@ -469,13 +475,13 @@ fn test_abstract_datagram_bind_send_to_addr() { let (len, addr) = or_panic!(sock2.recv_from(&mut buf)); assert_eq!(msg, &buf[..]); assert_eq!(len, 11); - assert_eq!(addr.as_abstract_namespace().unwrap(), b"ns1"); + assert_eq!(addr.as_abstract_name().unwrap(), b"ns1"); } #[cfg(any(target_os = "android", target_os = "linux"))] #[test] fn test_abstract_datagram_connect_addr() { - let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns3")); + let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns3")); let bsock1 = or_panic!(UnixDatagram::bind_addr(&addr1)); let sock = or_panic!(UnixDatagram::unbound()); @@ -489,7 +495,7 @@ fn test_abstract_datagram_connect_addr() { assert_eq!(addr.is_unnamed(), true); assert_eq!(msg, &buf[..]); - let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns4")); + let addr2 = or_panic!(SocketAddr::from_abstract_name(b"ns4")); let bsock2 = or_panic!(UnixDatagram::bind_addr(&addr2)); or_panic!(sock.connect_addr(&addr2)); @@ -499,8 +505,8 @@ fn test_abstract_datagram_connect_addr() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] -fn test_abstract_namespace_too_long() { - match SocketAddr::from_abstract_namespace( +fn test_abstract_name_too_long() { + match SocketAddr::from_abstract_name( b"abcdefghijklmnopqrstuvwxyzabcdefghijklmn\ opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi\ jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", @@ -513,11 +519,11 @@ fn test_abstract_namespace_too_long() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] -fn test_abstract_namespace_no_pathname_and_not_unnamed() { - let namespace = b"local"; - let addr = or_panic!(SocketAddr::from_abstract_namespace(&namespace[..])); +fn test_abstract_no_pathname_and_not_unnamed() { + let name = b"local"; + let addr = or_panic!(SocketAddr::from_abstract_name(name)); assert_eq!(addr.as_pathname(), None); - assert_eq!(addr.as_abstract_namespace(), Some(&namespace[..])); + assert_eq!(addr.as_abstract_name(), Some(&name[..])); assert_eq!(addr.is_unnamed(), false); } From a052f2cce1df8ac5ac9fcd104c948545f8b5f2f4 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 20 Sep 2022 11:55:07 +0000 Subject: [PATCH 0008/1126] Add the `#[derive_const]` attribute --- .../src/cfg_accessible.rs | 1 + compiler/rustc_builtin_macros/src/derive.rs | 9 ++--- .../src/deriving/bounds.rs | 2 ++ .../src/deriving/clone.rs | 2 ++ .../src/deriving/cmp/eq.rs | 2 ++ .../src/deriving/cmp/ord.rs | 2 ++ .../src/deriving/cmp/partial_eq.rs | 2 ++ .../src/deriving/cmp/partial_ord.rs | 2 ++ .../src/deriving/debug.rs | 2 ++ .../src/deriving/decodable.rs | 2 ++ .../src/deriving/default.rs | 2 ++ .../src/deriving/encodable.rs | 2 ++ .../src/deriving/generic/mod.rs | 6 ++-- .../rustc_builtin_macros/src/deriving/hash.rs | 2 ++ .../rustc_builtin_macros/src/deriving/mod.rs | 35 ++++++++++++------- compiler/rustc_builtin_macros/src/lib.rs | 3 +- compiler/rustc_expand/src/base.rs | 4 ++- compiler/rustc_expand/src/expand.rs | 13 +++---- compiler/rustc_expand/src/proc_macro.rs | 1 + compiler/rustc_resolve/src/macros.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/macros/mod.rs | 13 +++++++ library/core/src/prelude/v1.rs | 4 +++ library/std/src/prelude/v1.rs | 4 +++ src/test/ui/issues/issue-32655.stderr | 14 ++++++-- .../const_derives/derive-const-gate.rs | 4 +++ .../const_derives/derive-const-gate.stderr | 11 ++++++ .../derive-const-non-const-type.rs | 13 +++++++ .../derive-const-non-const-type.stderr | 14 ++++++++ .../const_derives/derive-const-use.rs | 19 ++++++++++ 30 files changed, 163 insertions(+), 30 deletions(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs index cb5359dd1e27..86df3c44eb33 100644 --- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs +++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs @@ -34,6 +34,7 @@ impl MultiItemModifier for Expander { span: Span, meta_item: &ast::MetaItem, item: Annotatable, + _is_derive_const: bool, ) -> ExpandResult, Annotatable> { let template = AttributeTemplate { list: Some("path"), ..Default::default() }; let attr = &ecx.attribute(meta_item.clone()); diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index e0fb7affb349..01f237e6ab5f 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -10,7 +10,7 @@ use rustc_session::Session; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; -pub(crate) struct Expander; +pub(crate) struct Expander(pub bool); impl MultiItemModifier for Expander { fn expand( @@ -19,6 +19,7 @@ impl MultiItemModifier for Expander { span: Span, meta_item: &ast::MetaItem, item: Annotatable, + _: bool, ) -> ExpandResult, Annotatable> { let sess = ecx.sess; if report_bad_target(sess, &item, span) { @@ -58,20 +59,20 @@ impl MultiItemModifier for Expander { report_path_args(sess, &meta); meta.path }) - .map(|path| (path, dummy_annotatable(), None)) + .map(|path| (path, dummy_annotatable(), None, self.0)) .collect(); // Do not configure or clone items unless necessary. match &mut resolutions[..] { [] => {} - [(_, first_item, _), others @ ..] => { + [(_, first_item, ..), others @ ..] => { *first_item = cfg_eval( sess, features, item.clone(), ecx.current_expansion.lint_node_id, ); - for (_, item, _) in others { + for (_, item, _, _) in others { *item = first_item.clone(); } } diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index 77e0b6c55a80..3a5b7ecde8a9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -12,6 +12,7 @@ pub fn expand_deriving_copy( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let trait_def = TraitDef { span, @@ -21,6 +22,7 @@ pub fn expand_deriving_copy( supports_unions: true, methods: Vec::new(), associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push); diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index c7f2d95e72f0..6d9be879cd19 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -14,6 +14,7 @@ pub fn expand_deriving_clone( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { // The simple form is `fn clone(&self) -> Self { *self }`, possibly with // some additional `AssertParamIsClone` assertions. @@ -86,6 +87,7 @@ pub fn expand_deriving_clone( combine_substructure: substructure, }], associated_types: Vec::new(), + is_const, }; trait_def.expand_ext(cx, mitem, item, push, is_simple) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 5b556c5c9b9d..9c01314112a5 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -15,6 +15,7 @@ pub fn expand_deriving_eq( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let span = cx.with_def_site_ctxt(span); let inline = cx.meta_word(span, sym::inline); @@ -41,6 +42,7 @@ pub fn expand_deriving_eq( })), }], associated_types: Vec::new(), + is_const, }; super::inject_impl_of_structural_trait(cx, span, item, path_std!(marker::StructuralEq), push); diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index 726258695581..d5c81c387834 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_ord( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let inline = cx.meta_word(span, sym::inline); let attrs = thin_vec![cx.attribute(inline)]; @@ -33,6 +34,7 @@ pub fn expand_deriving_ord( combine_substructure: combine_substructure(Box::new(|a, b, c| cs_cmp(a, b, c))), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index 42ee65b570a2..11b838a076c6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -14,6 +14,7 @@ pub fn expand_deriving_partial_eq( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let base = true; @@ -88,6 +89,7 @@ pub fn expand_deriving_partial_eq( supports_unions: false, methods, associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 516892aeda96..107a01190bb4 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_partial_ord( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let ordering_ty = Path(path_std!(cmp::Ordering)); let ret_ty = @@ -42,6 +43,7 @@ pub fn expand_deriving_partial_ord( supports_unions: false, methods: vec![partial_cmp_def], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 4af7fd816538..cf977c0824d8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_debug( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { // &mut ::std::fmt::Formatter let fmtr = Ref(Box::new(Path(path_std!(fmt::Formatter))), ast::Mutability::Mut); @@ -36,6 +37,7 @@ pub fn expand_deriving_debug( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 7174dbbe7ea8..a27a068f31a2 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -16,6 +16,7 @@ pub fn expand_deriving_rustc_decodable( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let krate = sym::rustc_serialize; let typaram = sym::__D; @@ -54,6 +55,7 @@ pub fn expand_deriving_rustc_decodable( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index a94c8a996e64..35b23f2f8f31 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -16,6 +16,7 @@ pub fn expand_deriving_default( mitem: &ast::MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { item.visit_with(&mut DetectNonVariantDefaultAttr { cx }); @@ -46,6 +47,7 @@ pub fn expand_deriving_default( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index b220e54238f4..f06cf0c56bc7 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -100,6 +100,7 @@ pub fn expand_deriving_rustc_encodable( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let krate = sym::rustc_serialize; let typaram = sym::__S; @@ -138,6 +139,7 @@ pub fn expand_deriving_rustc_encodable( })), }], associated_types: Vec::new(), + is_const, }; trait_def.expand(cx, mitem, item, push) diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 3cc160adb539..78dbe8e5979e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -171,7 +171,7 @@ use rustc_ast::{GenericArg, GenericParamKind, VariantData}; use rustc_attr as attr; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use std::cell::RefCell; use std::iter; use std::vec; @@ -200,6 +200,8 @@ pub struct TraitDef<'a> { pub methods: Vec>, pub associated_types: Vec<(Ident, Ty)>, + + pub is_const: bool, } pub struct MethodDef<'a> { @@ -726,7 +728,7 @@ impl<'a> TraitDef<'a> { unsafety: ast::Unsafe::No, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, - constness: ast::Const::No, + constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No }, generics: trait_generics, of_trait: opt_trait_ref, self_ty: self_type, diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index f1f02e7ce778..ef3da94f9e3f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -13,6 +13,7 @@ pub fn expand_deriving_hash( mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), + is_const: bool, ) { let path = Path::new_(pathvec_std!(hash::Hash), vec![], PathKind::Std); @@ -38,6 +39,7 @@ pub fn expand_deriving_hash( })), }], associated_types: Vec::new(), + is_const, }; hash_trait_def.expand(cx, mitem, item, push); diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index a65d0bad6de8..5b89da91d427 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -38,9 +38,10 @@ pub mod partial_ord; pub mod generic; -pub(crate) struct BuiltinDerive( - pub(crate) fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable)), -); +pub(crate) type BuiltinDeriveFn = + fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable), bool); + +pub(crate) struct BuiltinDerive(pub(crate) BuiltinDeriveFn); impl MultiItemModifier for BuiltinDerive { fn expand( @@ -49,6 +50,7 @@ impl MultiItemModifier for BuiltinDerive { span: Span, meta_item: &MetaItem, item: Annotatable, + is_derive_const: bool, ) -> ExpandResult, Annotatable> { // FIXME: Built-in derives often forget to give spans contexts, // so we are doing it here in a centralized way. @@ -57,21 +59,28 @@ impl MultiItemModifier for BuiltinDerive { match item { Annotatable::Stmt(stmt) => { if let ast::StmtKind::Item(item) = stmt.into_inner().kind { - (self.0)(ecx, span, meta_item, &Annotatable::Item(item), &mut |a| { - // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx' - // to the function - items.push(Annotatable::Stmt(P(ast::Stmt { - id: ast::DUMMY_NODE_ID, - kind: ast::StmtKind::Item(a.expect_item()), - span, - }))); - }); + (self.0)( + ecx, + span, + meta_item, + &Annotatable::Item(item), + &mut |a| { + // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx' + // to the function + items.push(Annotatable::Stmt(P(ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Item(a.expect_item()), + span, + }))); + }, + is_derive_const, + ); } else { unreachable!("should have already errored on non-item statement") } } _ => { - (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a)); + (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a), is_derive_const); } } ExpandResult::Ready(items) diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 8aeb3b82a9cd..ab1fcde16865 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -97,7 +97,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { bench: test::expand_bench, cfg_accessible: cfg_accessible::Expander, cfg_eval: cfg_eval::expand, - derive: derive::Expander, + derive: derive::Expander(false), + derive_const: derive::Expander(true), global_allocator: global_allocator::expand, test: test::expand_test, test_case: test::expand_test_case, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index e1da3ecdec7f..b3d187848be9 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -248,6 +248,7 @@ pub trait MultiItemModifier { span: Span, meta_item: &ast::MetaItem, item: Annotatable, + is_derive_const: bool, ) -> ExpandResult, Annotatable>; } @@ -261,6 +262,7 @@ where span: Span, meta_item: &ast::MetaItem, item: Annotatable, + _is_derive_const: bool, ) -> ExpandResult, Annotatable> { ExpandResult::Ready(self(ecx, span, meta_item, item)) } @@ -871,7 +873,7 @@ impl SyntaxExtension { /// Error type that denotes indeterminacy. pub struct Indeterminate; -pub type DeriveResolutions = Vec<(ast::Path, Annotatable, Option>)>; +pub type DeriveResolutions = Vec<(ast::Path, Annotatable, Option>, bool)>; pub trait ResolverExpand { fn next_node_id(&mut self) -> NodeId; diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index c2add852a067..a63b59d31cfd 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -319,6 +319,7 @@ pub enum InvocationKind { }, Derive { path: ast::Path, + is_const: bool, item: Annotatable, }, } @@ -460,13 +461,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> { derive_invocations.reserve(derives.len()); derives .into_iter() - .map(|(path, item, _exts)| { + .map(|(path, item, _exts, is_const)| { // FIXME: Consider using the derive resolutions (`_exts`) // instead of enqueuing the derives to be resolved again later. let expn_id = LocalExpnId::fresh_empty(); derive_invocations.push(( Invocation { - kind: InvocationKind::Derive { path, item }, + kind: InvocationKind::Derive { path, item, is_const }, fragment_kind, expansion_data: ExpansionData { id: expn_id, @@ -699,7 +700,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { SyntaxExtensionKind::LegacyAttr(expander) => { match validate_attr::parse_meta(&self.cx.sess.parse_sess, &attr) { Ok(meta) => { - let items = match expander.expand(self.cx, span, &meta, item) { + let items = match expander.expand(self.cx, span, &meta, item, false) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { // Reassemble the original invocation for retrying. @@ -731,19 +732,19 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } _ => unreachable!(), }, - InvocationKind::Derive { path, item } => match ext { + InvocationKind::Derive { path, item, is_const } => match ext { SyntaxExtensionKind::Derive(expander) | SyntaxExtensionKind::LegacyDerive(expander) => { if let SyntaxExtensionKind::Derive(..) = ext { self.gate_proc_macro_input(&item); } let meta = ast::MetaItem { kind: MetaItemKind::Word, span, path }; - let items = match expander.expand(self.cx, span, &meta, item) { + let items = match expander.expand(self.cx, span, &meta, item, is_const) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { // Reassemble the original invocation for retrying. return ExpandResult::Retry(Invocation { - kind: InvocationKind::Derive { path: meta.path, item }, + kind: InvocationKind::Derive { path: meta.path, item, is_const }, ..invoc }); } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 1a2ab9d190eb..e9a691920689 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -112,6 +112,7 @@ impl MultiItemModifier for DeriveProcMacro { span: Span, _meta_item: &ast::MetaItem, item: Annotatable, + _is_derive_const: bool, ) -> ExpandResult, Annotatable> { // We need special handling for statement items // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`) diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index dafa10e9e002..8ef99e5ef968 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -356,7 +356,7 @@ impl<'a> ResolverExpand for Resolver<'a> { has_derive_copy: false, }); let parent_scope = self.invocation_parent_scopes[&expn_id]; - for (i, (path, _, opt_ext)) in entry.resolutions.iter_mut().enumerate() { + for (i, (path, _, opt_ext, _)) in entry.resolutions.iter_mut().enumerate() { if opt_ext.is_none() { *opt_ext = Some( match self.resolve_macro_path( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 562360130e92..0a9570979d5a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -614,6 +614,7 @@ symbols! { deref_mut, deref_target, derive, + derive_const, derive_default_enum, destruct, destructuring_assignment, diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index fd96e1ff77d5..e504db814344 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1464,6 +1464,19 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Attribute macro used to apply derive macros for implementing traits + /// in a const context. + /// + /// See [the reference] for more info. + /// + /// [the reference]: ../../../reference/attributes/derive.html + #[unstable(feature = "derive_const", issue = "none")] + #[rustc_builtin_macro] + #[cfg(not(bootstrap))] + pub macro derive_const($item:item) { + /* compiler built-in */ + } + /// Attribute macro applied to a function to turn it into a unit test. /// /// See [the reference] for more info. diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index b566e211cd89..f52c06d7eadc 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -78,6 +78,10 @@ pub use crate::macros::builtin::{RustcDecodable, RustcEncodable}; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] pub use crate::macros::builtin::{bench, derive, global_allocator, test, test_case}; +#[unstable(feature = "derive_const", issue = "none")] +#[cfg(not(bootstrap))] +pub use crate::macros::builtin::derive_const; + #[unstable( feature = "cfg_accessible", issue = "64797", diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index 0226c4d7a258..93f4a17bbfca 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -62,6 +62,10 @@ pub use core::prelude::v1::{RustcDecodable, RustcEncodable}; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] pub use core::prelude::v1::{bench, derive, global_allocator, test, test_case}; +#[unstable(feature = "derive_const", issue = "none")] +#[cfg(not(bootstrap))] +pub use core::prelude::v1::derive_const; + // Do not `doc(no_inline)` either. #[unstable( feature = "cfg_accessible", diff --git a/src/test/ui/issues/issue-32655.stderr b/src/test/ui/issues/issue-32655.stderr index 2d9ce430a462..5a758c7002b9 100644 --- a/src/test/ui/issues/issue-32655.stderr +++ b/src/test/ui/issues/issue-32655.stderr @@ -2,18 +2,28 @@ error: cannot find attribute `derive_Clone` in this scope --> $DIR/issue-32655.rs:3:11 | LL | #[derive_Clone] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ help: an attribute macro with a similar name exists: `derive_const` ... LL | foo!(); | ------ in this macro invocation | + ::: $SRC_DIR/core/src/macros/mod.rs:LL:COL + | +LL | pub macro derive_const($item:item) { + | ---------------------- similarly named attribute macro `derive_const` defined here + | = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find attribute `derive_Clone` in this scope --> $DIR/issue-32655.rs:15:7 | LL | #[derive_Clone] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ help: an attribute macro with a similar name exists: `derive_const` + | + ::: $SRC_DIR/core/src/macros/mod.rs:LL:COL + | +LL | pub macro derive_const($item:item) { + | ---------------------- similarly named attribute macro `derive_const` defined here error: aborting due to 2 previous errors diff --git a/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs new file mode 100644 index 000000000000..348ca0ab1906 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs @@ -0,0 +1,4 @@ +#[derive_const(Default)] //~ ERROR use of unstable library feature +pub struct S; + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr new file mode 100644 index 000000000000..cc9bdd2715f7 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr @@ -0,0 +1,11 @@ +error[E0658]: use of unstable library feature 'derive_const' + --> $DIR/derive-const-gate.rs:1:3 + | +LL | #[derive_const(Default)] + | ^^^^^^^^^^^^ + | + = help: add `#![feature(derive_const)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs new file mode 100644 index 000000000000..92843a8a2da4 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs @@ -0,0 +1,13 @@ +#![feature(derive_const)] + +pub struct A; + +impl Default for A { + fn default() -> A { A } +} + +#[derive_const(Default)] +pub struct S(A); +//~^ cannot call non-const fn + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr new file mode 100644 index 000000000000..d463c774e289 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr @@ -0,0 +1,14 @@ +error[E0015]: cannot call non-const fn `::default` in constant functions + --> $DIR/derive-const-non-const-type.rs:10:14 + | +LL | #[derive_const(Default)] + | ------- in this derive macro expansion +LL | pub struct S(A); + | ^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0015`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs new file mode 100644 index 000000000000..d1fbeac8598e --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs @@ -0,0 +1,19 @@ +// check-pass +#![feature(const_trait_impl, const_cmp, const_default_impls, derive_const)] + +pub struct A; + +impl const Default for A { + fn default() -> A { A } +} + +impl const PartialEq for A { + fn eq(&self, _: &A) -> bool { true } +} + +#[derive_const(Default, PartialEq)] +pub struct S((), A); + +const _: () = assert!(S((), A) == S::default()); + +fn main() {} From 1e264abf795540a46fb14aa1f0ed717e4f22ecff Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Sun, 11 Sep 2022 18:02:54 -0400 Subject: [PATCH 0009/1126] Add QEMU test for x86_64-unknown-uefi The UEFI targets don't have std support yet, so the normal tests don't work. However, we can compile a simple no-std program and run it under QEMU to at least check that the target compiles, links, and runs. Tested locally with: src/ci/docker/run.sh test-various --- .../host-x86_64/test-various/Dockerfile | 11 ++- .../test-various/uefi_qemu_test/Cargo.toml | 9 ++ .../test-various/uefi_qemu_test/run.py | 96 +++++++++++++++++++ .../test-various/uefi_qemu_test/src/main.rs | 45 +++++++++ 4 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml create mode 100644 src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py create mode 100644 src/ci/docker/host-x86_64/test-various/uefi_qemu_test/src/main.rs diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index b75e2f085cd3..b0f35bcb9ccf 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -16,7 +16,9 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins pkg-config \ xz-utils \ wget \ - patch + patch \ + ovmf \ + qemu-system-x86 RUN curl -sL https://nodejs.org/dist/v15.14.0/node-v15.14.0-linux-x64.tar.xz | \ tar -xJ @@ -64,4 +66,9 @@ ENV MUSL_TARGETS=x86_64-unknown-linux-musl \ CXX_x86_64_unknown_linux_musl=x86_64-linux-musl-g++ ENV MUSL_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $MUSL_TARGETS -ENV SCRIPT $WASM_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT +COPY host-x86_64/test-various/uefi_qemu_test /uefi_qemu_test +ENV UEFI_TARGETS=x86_64-unknown-uefi +ENV UEFI_SCRIPT python3 /checkout/x.py --stage 2 build --host='' --target $UEFI_TARGETS && \ + python3 -u /uefi_qemu_test/run.py + +ENV SCRIPT $WASM_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT && $UEFI_SCRIPT diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml new file mode 100644 index 000000000000..fa8e5b3d0806 --- /dev/null +++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "uefi_qemu_test" +version = "0.0.0" +edition = "2021" + +[workspace] + +[dependencies] +r-efi = "4.1.0" diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py new file mode 100644 index 000000000000..46793ce3afa1 --- /dev/null +++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +import os +import shutil +import subprocess +import sys +import tempfile + +from pathlib import Path + + +def run(*cmd, capture=False, check=True, env=None): + """Print and run a command, optionally capturing the output.""" + cmd = [str(p) for p in cmd] + print(' '.join(cmd)) + return subprocess.run(cmd, + capture_output=capture, + check=check, + env=env, + text=True) + + +def build_and_run(tmp_dir): + host_artifacts = Path('/checkout/obj/build/x86_64-unknown-linux-gnu') + stage0 = host_artifacts / 'stage0/bin' + stage2 = host_artifacts / 'stage2/bin' + + env = dict(os.environ) + env['PATH'] = '{}:{}:{}'.format(stage2, stage0, env['PATH']) + + # Copy the test create into `tmp_dir`. + test_crate = Path(tmp_dir) / 'uefi_qemu_test' + shutil.copytree('/uefi_qemu_test', test_crate) + + # Build the UEFI executable. + target = 'x86_64-unknown-uefi' + run('cargo', + 'build', + '--manifest-path', + test_crate / 'Cargo.toml', + '--target', + target, + env=env) + + # Create a mock EFI System Partition in a subdirectory. + esp = test_crate / 'esp' + boot = esp / 'efi/boot' + os.makedirs(boot, exist_ok=True) + + # Copy the executable into the ESP. + src_exe_path = test_crate / 'target' / target / 'debug/uefi_qemu_test.efi' + shutil.copy(src_exe_path, boot / 'bootx64.efi') + + # Run the executable in QEMU and capture the output. + qemu = 'qemu-system-x86_64' + ovmf_dir = Path('/usr/share/OVMF') + ovmf_code = ovmf_dir / 'OVMF_CODE.fd' + ovmf_vars = ovmf_dir / 'OVMF_VARS.fd' + output = run(qemu, + '-display', + 'none', + '-serial', + 'stdio', + '-drive', + f'if=pflash,format=raw,readonly=on,file={ovmf_code}', + '-drive', + f'if=pflash,format=raw,readonly=on,file={ovmf_vars}', + '-drive', + f'format=raw,file=fat:rw:{esp}', + capture=True, + # Ubuntu 20.04 (which is what the Dockerfile currently + # uses) provides QEMU 4.2.1, which segfaults on + # shutdown under some circumstances. That has been + # fixed in newer versions of QEMU, but for now just + # don't check the exit status. + check=False).stdout + + if 'Hello World!' in output: + print('VM produced expected output') + else: + print('unexpected VM output:') + print('---start---') + print(output) + print('---end---') + sys.exit(1) + + +def main(): + # Create a temporary directory so that we have a writeable + # workspace. + with tempfile.TemporaryDirectory() as tmp_dir: + build_and_run(tmp_dir) + + +if __name__ == "__main__": + main() diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/src/main.rs b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/src/main.rs new file mode 100644 index 000000000000..2ec554c140b5 --- /dev/null +++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/src/main.rs @@ -0,0 +1,45 @@ +// Code is adapted from this hello world example: +// https://doc.rust-lang.org/nightly/rustc/platform-support/unknown-uefi.html + +#![no_main] +#![no_std] + +use core::{panic, ptr}; +use r_efi::efi::{Char16, Handle, Status, SystemTable, RESET_SHUTDOWN}; + +#[panic_handler] +fn panic_handler(_info: &panic::PanicInfo) -> ! { + loop {} +} + +#[export_name = "efi_main"] +pub extern "C" fn main(_h: Handle, st: *mut SystemTable) -> Status { + let s = [ + 0x0048u16, 0x0065u16, 0x006cu16, 0x006cu16, 0x006fu16, // "Hello" + 0x0020u16, // " " + 0x0057u16, 0x006fu16, 0x0072u16, 0x006cu16, 0x0064u16, // "World" + 0x0021u16, // "!" + 0x000au16, // "\n" + 0x0000u16, // NUL + ]; + + // Print "Hello World!". + let r = unsafe { ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut Char16) }; + if r.is_error() { + return r; + } + + // Shut down. + unsafe { + ((*((*st).runtime_services)).reset_system)( + RESET_SHUTDOWN, + Status::SUCCESS, + 0, + ptr::null_mut(), + ); + } + + // This should never be reached because `reset_system` should never + // return, so fail with an error if we get here. + Status::UNSUPPORTED +} From 6630c146f213088dff0d4ebd8dad90591544c169 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 23 Sep 2022 17:06:28 -0700 Subject: [PATCH 0010/1126] Implement the `+whole-archive` modifier for `wasm-ld` This implements the `Linker::{link_whole_staticlib,link_whole_rlib}` methods for the `WasmLd` linker used on wasm targets. Previously these methods were noops since I think historically `wasm-ld` did not have support for `--whole-archive` but nowadays it does, so the flags are passed through. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e0bd7a33f737..6e59a4f64db2 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1265,11 +1265,11 @@ impl<'a> Linker for WasmLd<'a> { } fn link_whole_staticlib(&mut self, lib: &str, _verbatim: bool, _search_path: &[PathBuf]) { - self.cmd.arg("-l").arg(lib); + self.cmd.arg("--whole-archive").arg("-l").arg(lib).arg("--no-whole-archive"); } fn link_whole_rlib(&mut self, lib: &Path) { - self.cmd.arg(lib); + self.cmd.arg("--whole-archive").arg(lib).arg("--no-whole-archive"); } fn gc_sections(&mut self, _keep_metadata: bool) { From f310d4cbb4ac792ecfe2bb680c869bb732e9e7af Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 27 Sep 2022 18:22:06 +0200 Subject: [PATCH 0011/1126] Don't auto-publish lib crates --- .github/workflows/publish.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a4497f49e3c2..73e62ab32c6c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,8 +2,8 @@ name: publish on: workflow_dispatch: # We can add version input when 1.0 is released and scheduled releases are removed -# schedule: -# - cron: "0 0 * * *" # midnight UTC + # schedule: + # - cron: "0 0 * * *" # midnight UTC push: branches: @@ -50,5 +50,7 @@ jobs: cargo workspaces rename --from test-utils test_utils cargo workspaces rename --from text-edit text_edit cargo workspaces rename ra_ap_%n + # Remove library crates from the workspaces so we don't auto-publish them as well + sed -i 's/ "lib\/\*",//' ./Cargo.toml find crates/rust-analyzer -type f -name '*.rs' -exec sed -i 's/rust_analyzer/ra_ap_rust_analyzer/g' {} + cargo workspaces publish --yes --force '*' --exact --no-git-commit --allow-dirty --skip-published custom 0.0.$PATCH From 7874976762bc531fa6330854e35ec4db2182164d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 29 Sep 2022 17:27:03 +0900 Subject: [PATCH 0012/1126] Stabilize the `instruction_set` feature Signed-off-by: Yuki Okushi --- compiler/rustc_feature/src/accepted.rs | 2 ++ compiler/rustc_feature/src/active.rs | 2 -- compiler/rustc_feature/src/builtin_attrs.rs | 6 +---- src/test/ui/asm/issue-92378.rs | 2 +- src/test/ui/error-codes/E0778.rs | 8 ++---- src/test/ui/error-codes/E0778.stderr | 2 +- src/test/ui/error-codes/E0779.rs | 6 +---- src/test/ui/error-codes/E0779.stderr | 2 +- .../feature-gate-isa_attribute.rs | 6 ----- .../feature-gate-isa_attribute.stderr | 25 ------------------- 10 files changed, 9 insertions(+), 52 deletions(-) delete mode 100644 src/test/ui/feature-gates/feature-gate-isa_attribute.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-isa_attribute.stderr diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 8efb7ccc1c71..4f58e42b6ace 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -185,6 +185,8 @@ declare_features! ( (accepted, infer_outlives_requirements, "1.30.0", Some(44493), None), /// Allows irrefutable patterns in `if let` and `while let` statements (RFC 2086). (accepted, irrefutable_let_patterns, "1.33.0", Some(44495), None), + /// Allows `#[instruction_set(_)]` attribute. + (accepted, isa_attribute, "CURRENT_RUSTC_VERSION", Some(74727), None), /// Allows some increased flexibility in the name resolution rules, /// especially around globs and shadowing (RFC 1560). (accepted, item_like_imports, "1.15.0", Some(35120), None), diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 71ad54291b28..e9b948bfe4da 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -426,8 +426,6 @@ declare_features! ( (incomplete, inline_const_pat, "1.58.0", Some(76001), None), /// Allows using `pointer` and `reference` in intra-doc links (active, intra_doc_pointers, "1.51.0", Some(80896), None), - /// Allows `#[instruction_set(_)]` attribute - (active, isa_attribute, "1.48.0", Some(74727), None), // Allows setting the threshold for the `large_assignments` lint. (active, large_assignments, "1.52.0", Some(83518), None), /// Allows `if/while p && let q = r && ...` chains. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 5ee6c9f23877..3c62bdcaadbd 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -384,6 +384,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing), ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk), ungated!(track_caller, Normal, template!(Word), WarnFollowing), + ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding), gated!( no_sanitize, Normal, template!(List: "address, memory, thread"), DuplicatesOk, @@ -445,11 +446,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ optimize, Normal, template!(List: "size|speed"), ErrorPreceding, optimize_attribute, experimental!(optimize), ), - // RFC 2867 - gated!( - instruction_set, Normal, template!(List: "set"), ErrorPreceding, - isa_attribute, experimental!(instruction_set) - ), gated!( ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice) diff --git a/src/test/ui/asm/issue-92378.rs b/src/test/ui/asm/issue-92378.rs index 6e3c26e98c3f..809b0d1555ae 100644 --- a/src/test/ui/asm/issue-92378.rs +++ b/src/test/ui/asm/issue-92378.rs @@ -3,7 +3,7 @@ // needs-asm-support // build-pass -#![feature(no_core, lang_items, rustc_attrs, isa_attribute)] +#![feature(no_core, lang_items, rustc_attrs)] #![no_core] #![crate_type = "rlib"] diff --git a/src/test/ui/error-codes/E0778.rs b/src/test/ui/error-codes/E0778.rs index 60e5c2598f1e..74653886d415 100644 --- a/src/test/ui/error-codes/E0778.rs +++ b/src/test/ui/error-codes/E0778.rs @@ -1,8 +1,4 @@ -#![feature(isa_attribute)] - #[instruction_set()] //~ ERROR -fn no_isa_defined() { -} +fn no_isa_defined() {} -fn main() { -} +fn main() {} diff --git a/src/test/ui/error-codes/E0778.stderr b/src/test/ui/error-codes/E0778.stderr index 6ecae7924237..42647e5c6a1b 100644 --- a/src/test/ui/error-codes/E0778.stderr +++ b/src/test/ui/error-codes/E0778.stderr @@ -1,5 +1,5 @@ error[E0778]: `#[instruction_set]` requires an argument - --> $DIR/E0778.rs:3:1 + --> $DIR/E0778.rs:1:1 | LL | #[instruction_set()] | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/error-codes/E0779.rs b/src/test/ui/error-codes/E0779.rs index 1b4dbce20360..c32dae12c9cb 100644 --- a/src/test/ui/error-codes/E0779.rs +++ b/src/test/ui/error-codes/E0779.rs @@ -1,6 +1,2 @@ -#![feature(isa_attribute)] - #[instruction_set(arm::magic)] //~ ERROR -fn main() { - -} +fn main() {} diff --git a/src/test/ui/error-codes/E0779.stderr b/src/test/ui/error-codes/E0779.stderr index da787260d4f6..7c6a119a0961 100644 --- a/src/test/ui/error-codes/E0779.stderr +++ b/src/test/ui/error-codes/E0779.stderr @@ -1,5 +1,5 @@ error[E0779]: invalid instruction set specified - --> $DIR/E0779.rs:3:1 + --> $DIR/E0779.rs:1:1 | LL | #[instruction_set(arm::magic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/feature-gates/feature-gate-isa_attribute.rs b/src/test/ui/feature-gates/feature-gate-isa_attribute.rs deleted file mode 100644 index cb02a0955e91..000000000000 --- a/src/test/ui/feature-gates/feature-gate-isa_attribute.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[instruction_set] -//~^ ERROR the `#[instruction_set]` attribute is an experimental feature [E0658] -//~| ERROR malformed `instruction_set` attribute input -//~| ERROR must specify an instruction set [E0778] -fn main() { -} diff --git a/src/test/ui/feature-gates/feature-gate-isa_attribute.stderr b/src/test/ui/feature-gates/feature-gate-isa_attribute.stderr deleted file mode 100644 index 2a95a80ca617..000000000000 --- a/src/test/ui/feature-gates/feature-gate-isa_attribute.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error: malformed `instruction_set` attribute input - --> $DIR/feature-gate-isa_attribute.rs:1:1 - | -LL | #[instruction_set] - | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[instruction_set(set)]` - -error[E0658]: the `#[instruction_set]` attribute is an experimental feature - --> $DIR/feature-gate-isa_attribute.rs:1:1 - | -LL | #[instruction_set] - | ^^^^^^^^^^^^^^^^^^ - | - = note: see issue #74727 for more information - = help: add `#![feature(isa_attribute)]` to the crate attributes to enable - -error[E0778]: must specify an instruction set - --> $DIR/feature-gate-isa_attribute.rs:1:1 - | -LL | #[instruction_set] - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0658, E0778. -For more information about an error, try `rustc --explain E0658`. From 12c15a2bfe9f4144003366bc72e10387fbef9aab Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 29 Sep 2022 14:23:47 +0200 Subject: [PATCH 0013/1126] Split out from_u32_unchecked from const_char_convert It relies on the Option::unwrap function which is not const-stable (yet). --- library/core/src/char/convert.rs | 1 - library/core/src/char/methods.rs | 2 +- library/core/src/char/mod.rs | 2 +- library/core/src/lib.rs | 1 + 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 7c5f82f5ea49..f1a51a550f57 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -18,7 +18,6 @@ pub(super) const fn from_u32(i: u32) -> Option { } /// Converts a `u32` to a `char`, ignoring validity. See [`char::from_u32_unchecked`]. -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] #[inline] #[must_use] pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index b7a63b7c6756..4416602c0bda 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -183,7 +183,7 @@ impl char { /// assert_eq!('❤', c); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_unstable(feature = "const_char_from_u32_unchecked", issue = "89259")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index b34a7121631c..bb3a96a19fb2 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -120,7 +120,7 @@ pub const fn from_u32(i: u32) -> Option { /// Converts a `u32` to a `char`, ignoring validity. Use [`char::from_u32_unchecked`]. /// instead. #[stable(feature = "char_from_unchecked", since = "1.5.0")] -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] +#[rustc_const_unstable(feature = "const_char_from_u32_unchecked", issue = "89259")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index c3df0d4f9b91..f98ea6985b02 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -105,6 +105,7 @@ #![feature(const_caller_location)] #![feature(const_cell_into_inner)] #![feature(const_char_convert)] +#![feature(const_char_from_u32_unchecked)] #![feature(const_clone)] #![feature(const_cmp)] #![feature(const_discriminant)] From 176c44c08ed797b5ddea893a0a292a7bee0d8f8c Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 29 Sep 2022 14:24:29 +0200 Subject: [PATCH 0014/1126] Stabilize const_char_convert --- library/core/src/char/methods.rs | 6 +++--- library/core/src/char/mod.rs | 4 ++-- library/core/src/lib.rs | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 4416602c0bda..275e5cff4193 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -140,7 +140,7 @@ impl char { /// assert_eq!(None, c); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_u32(i: u32) -> Option { @@ -241,7 +241,7 @@ impl char { /// let _c = char::from_digit(1, 37); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_digit(num: u32, radix: u32) -> Option { @@ -338,7 +338,7 @@ impl char { /// let _ = '1'.to_digit(37); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] + #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index bb3a96a19fb2..55552376280a 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -110,7 +110,7 @@ pub fn decode_utf16>(iter: I) -> DecodeUtf16 Option { @@ -130,7 +130,7 @@ pub const unsafe fn from_u32_unchecked(i: u32) -> char { /// Converts a digit in the given radix to a `char`. Use [`char::from_digit`] instead. #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] +#[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_digit(num: u32, radix: u32) -> Option { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index f98ea6985b02..bb162b6d0f64 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -104,7 +104,6 @@ #![feature(const_black_box)] #![feature(const_caller_location)] #![feature(const_cell_into_inner)] -#![feature(const_char_convert)] #![feature(const_char_from_u32_unchecked)] #![feature(const_clone)] #![feature(const_cmp)] From 3694429d09a2586cea5c769cddee10cd70f39d84 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 29 Jul 2022 23:06:13 +0400 Subject: [PATCH 0015/1126] recover wrong-cased `use`s (`Use`, `USE`, etc) --- compiler/rustc_parse/src/parser/item.rs | 25 ++++++++++++++--- compiler/rustc_parse/src/parser/mod.rs | 27 +++++++++++++++++++ .../ui/parser/item-kw-case-mismatch.fixed | 7 +++++ src/test/ui/parser/item-kw-case-mismatch.rs | 7 +++++ .../ui/parser/item-kw-case-mismatch.stderr | 14 ++++++++++ 5 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/parser/item-kw-case-mismatch.fixed create mode 100644 src/test/ui/parser/item-kw-case-mismatch.rs create mode 100644 src/test/ui/parser/item-kw-case-mismatch.stderr diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 25425fbb2c6a..34c7d4ec4815 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -143,8 +143,15 @@ impl<'a> Parser<'a> { let lo = self.token.span; let vis = self.parse_visibility(FollowedByType::No)?; let mut def = self.parse_defaultness(); - let kind = - self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, fn_parse_mode)?; + let kind = self.parse_item_kind( + &mut attrs, + mac_allowed, + lo, + &vis, + &mut def, + fn_parse_mode, + false, + )?; if let Some((ident, kind)) = kind { self.error_on_unconsumed_default(def, &kind); let span = lo.to(self.prev_token.span); @@ -205,11 +212,12 @@ impl<'a> Parser<'a> { vis: &Visibility, def: &mut Defaultness, fn_parse_mode: FnParseMode, + kw_case_insensitive: bool, ) -> PResult<'a, Option> { let def_final = def == &Defaultness::Final; let mut def = || mem::replace(def, Defaultness::Final); - let info = if self.eat_keyword(kw::Use) { + let info = if self.eat_keyword_case(kw::Use, kw_case_insensitive) { self.parse_use_item()? } else if self.check_fn_front_matter(def_final) { // FUNCTION ITEM @@ -286,6 +294,17 @@ impl<'a> Parser<'a> { } else if self.isnt_macro_invocation() && vis.kind.is_pub() { self.recover_missing_kw_before_item()?; return Ok(None); + } else if self.isnt_macro_invocation() && !kw_case_insensitive { + // Recover wrong cased keywords + return self.parse_item_kind( + attrs, + macros_allowed, + lo, + vis, + &mut def(), + fn_parse_mode, + true, + ); } else if macros_allowed && self.check_path() { // MACRO INVOCATION ITEM (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?))) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 2aebaf7c3af2..b82ce90129fe 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -616,6 +616,33 @@ impl<'a> Parser<'a> { } } + /// Eats a keyword, optionally ignoring the case. + /// If the case differs (and is ignored) an error is issued. + /// This is useful for recovery. + fn eat_keyword_case(&mut self, kw: Symbol, case_insensitive: bool) -> bool { + if self.eat_keyword(kw) { + return true; + } + + if case_insensitive + && let Some((ident, /* is_raw */ false)) = self.token.ident() + && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { + self + .struct_span_err(ident.span, format!("keyword `{kw}` is written in a wrong case")) + .span_suggestion( + ident.span, + "write it in the correct case", + kw, + Applicability::MachineApplicable + ).emit(); + + self.bump(); + return true; + } + + false + } + fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool { if self.token.is_keyword(kw) { self.bump(); diff --git a/src/test/ui/parser/item-kw-case-mismatch.fixed b/src/test/ui/parser/item-kw-case-mismatch.fixed new file mode 100644 index 000000000000..d99f89ccef5b --- /dev/null +++ b/src/test/ui/parser/item-kw-case-mismatch.fixed @@ -0,0 +1,7 @@ +// run-rustfix +#![allow(unused_imports)] + +fn main() {} + +use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case +use std::ptr::write; //~ ERROR keyword `use` is written in a wrong case diff --git a/src/test/ui/parser/item-kw-case-mismatch.rs b/src/test/ui/parser/item-kw-case-mismatch.rs new file mode 100644 index 000000000000..605552b5f147 --- /dev/null +++ b/src/test/ui/parser/item-kw-case-mismatch.rs @@ -0,0 +1,7 @@ +// run-rustfix +#![allow(unused_imports)] + +fn main() {} + +Use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case +USE std::ptr::write; //~ ERROR keyword `use` is written in a wrong case diff --git a/src/test/ui/parser/item-kw-case-mismatch.stderr b/src/test/ui/parser/item-kw-case-mismatch.stderr new file mode 100644 index 000000000000..aebbc9d558f2 --- /dev/null +++ b/src/test/ui/parser/item-kw-case-mismatch.stderr @@ -0,0 +1,14 @@ +error: keyword `use` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:6:1 + | +LL | Use std::ptr::read; + | ^^^ help: write it in the correct case (notice the capitalization): `use` + +error: keyword `use` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:7:1 + | +LL | USE std::ptr::write; + | ^^^ help: write it in the correct case: `use` + +error: aborting due to 2 previous errors + From 38b086524875c1ed9904f94eca64a162f7572dae Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 13 Sep 2022 22:48:29 +0400 Subject: [PATCH 0016/1126] Recover wrong cased keywords starting functions --- compiler/rustc_ast/src/token.rs | 9 +++ compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/item.rs | 79 ++++++++++--------- compiler/rustc_parse/src/parser/mod.rs | 30 +++++-- compiler/rustc_parse/src/parser/ty.rs | 4 +- .../ui/parser/item-kw-case-mismatch.fixed | 27 +++++++ src/test/ui/parser/item-kw-case-mismatch.rs | 27 +++++++ .../ui/parser/item-kw-case-mismatch.stderr | 78 +++++++++++++++++- 8 files changed, 205 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 99034799b3c3..3f7e32f4fae6 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -618,6 +618,15 @@ impl Token { self.is_non_raw_ident_where(|id| id.name == kw) } + /// Returns `true` if the token is a given keyword, `kw` or if `case_insensitive` is true and this token is an identifier equal to `kw` ignoring the case. + pub fn is_keyword_case(&self, kw: Symbol, case_insensitive: bool) -> bool { + self.is_keyword(kw) + || (case_insensitive + && self.is_non_raw_ident_where(|id| { + id.name.as_str().to_lowercase() == kw.as_str().to_lowercase() + })) + } + pub fn is_path_segment_keyword(&self) -> bool { self.is_non_raw_ident_where(Ident::is_path_segment_keyword) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8b328e593ae8..e08d09a4d9f3 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2024,7 +2024,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; let asyncness = if self.token.uninterpolated_span().rust_2018() { - self.parse_asyncness() + self.parse_asyncness(false) } else { Async::No }; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 34c7d4ec4815..4e2fc513ac50 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -34,7 +34,7 @@ impl<'a> Parser<'a> { /// Parses a `mod { ... }` or `mod ;` item. fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(false); self.expect_keyword(kw::Mod)?; let id = self.parse_ident()?; let mod_kind = if self.eat(&token::Semi) { @@ -215,14 +215,14 @@ impl<'a> Parser<'a> { kw_case_insensitive: bool, ) -> PResult<'a, Option> { let def_final = def == &Defaultness::Final; - let mut def = || mem::replace(def, Defaultness::Final); + let mut def_ = || mem::replace(def, Defaultness::Final); let info = if self.eat_keyword_case(kw::Use, kw_case_insensitive) { self.parse_use_item()? - } else if self.check_fn_front_matter(def_final) { + } else if self.check_fn_front_matter(def_final, kw_case_insensitive) { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?; - (ident, ItemKind::Fn(Box::new(Fn { defaultness: def(), sig, generics, body }))) + (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body }))) } else if self.eat_keyword(kw::Extern) { if self.eat_keyword(kw::Crate) { // EXTERN CRATE @@ -233,7 +233,7 @@ impl<'a> Parser<'a> { } } else if self.is_unsafe_foreign_mod() { // EXTERN BLOCK - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(false); self.expect_keyword(kw::Extern)?; self.parse_item_foreign_mod(attrs, unsafety)? } else if self.is_static_global() { @@ -242,15 +242,15 @@ impl<'a> Parser<'a> { let m = self.parse_mutability(); let (ident, ty, expr) = self.parse_item_global(Some(m))?; (ident, ItemKind::Static(ty, m, expr)) - } else if let Const::Yes(const_span) = self.parse_constness() { + } else if let Const::Yes(const_span) = self.parse_constness(false) { // CONST ITEM if self.token.is_keyword(kw::Impl) { // recover from `const impl`, suggest `impl const` - self.recover_const_impl(const_span, attrs, def())? + self.recover_const_impl(const_span, attrs, def_())? } else { self.recover_const_mut(const_span); let (ident, ty, expr) = self.parse_item_global(None)?; - (ident, ItemKind::Const(def(), ty, expr)) + (ident, ItemKind::Const(def_(), ty, expr)) } } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() { // TRAIT ITEM @@ -259,7 +259,7 @@ impl<'a> Parser<'a> { || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Impl]) { // IMPL ITEM - self.parse_item_impl(attrs, def())? + self.parse_item_impl(attrs, def_())? } else if self.check_keyword(kw::Mod) || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod]) { @@ -267,7 +267,7 @@ impl<'a> Parser<'a> { self.parse_item_mod(attrs)? } else if self.eat_keyword(kw::Type) { // TYPE ITEM - self.parse_type_alias(def())? + self.parse_type_alias(def_())? } else if self.eat_keyword(kw::Enum) { // ENUM ITEM self.parse_item_enum()? @@ -295,16 +295,10 @@ impl<'a> Parser<'a> { self.recover_missing_kw_before_item()?; return Ok(None); } else if self.isnt_macro_invocation() && !kw_case_insensitive { + _ = def_; + // Recover wrong cased keywords - return self.parse_item_kind( - attrs, - macros_allowed, - lo, - vis, - &mut def(), - fn_parse_mode, - true, - ); + return self.parse_item_kind(attrs, macros_allowed, lo, vis, def, fn_parse_mode, true); } else if macros_allowed && self.check_path() { // MACRO INVOCATION ITEM (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?))) @@ -557,7 +551,7 @@ impl<'a> Parser<'a> { attrs: &mut AttrVec, defaultness: Defaultness, ) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(false); self.expect_keyword(kw::Impl)?; // First, parse generic parameters if necessary. @@ -571,7 +565,7 @@ impl<'a> Parser<'a> { generics }; - let constness = self.parse_constness(); + let constness = self.parse_constness(false); if let Const::Yes(span) = constness { self.sess.gated_spans.gate(sym::const_trait_impl, span); } @@ -815,7 +809,7 @@ impl<'a> Parser<'a> { /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(false); // Parse optional `auto` prefix. let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No }; @@ -1764,7 +1758,7 @@ impl<'a> Parser<'a> { let (ident, is_raw) = self.ident_or_err()?; if !is_raw && ident.is_reserved() { let snapshot = self.create_snapshot_for_diagnostic(); - let err = if self.check_fn_front_matter(false) { + let err = if self.check_fn_front_matter(false, false) { let inherited_vis = Visibility { span: rustc_span::DUMMY_SP, kind: VisibilityKind::Inherited, @@ -2153,7 +2147,11 @@ impl<'a> Parser<'a> { /// /// `check_pub` adds additional `pub` to the checks in case users place it /// wrongly, can be used to ensure `pub` never comes after `default`. - pub(super) fn check_fn_front_matter(&mut self, check_pub: bool) -> bool { + pub(super) fn check_fn_front_matter( + &mut self, + check_pub: bool, + kw_case_insensitive: bool, + ) -> bool { // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. // `pub` is added in case users got confused with the ordering like `async pub fn`, @@ -2163,23 +2161,30 @@ impl<'a> Parser<'a> { } else { &[kw::Const, kw::Async, kw::Unsafe, kw::Extern] }; - self.check_keyword(kw::Fn) // Definitely an `fn`. + self.check_keyword_case(kw::Fn, kw_case_insensitive) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: - || quals.iter().any(|&kw| self.check_keyword(kw)) + || quals.iter().any(|&kw| self.check_keyword_case(kw, kw_case_insensitive)) && self.look_ahead(1, |t| { // `$qual fn`, e.g. `const fn` or `async fn`. - t.is_keyword(kw::Fn) + t.is_keyword_case(kw::Fn, kw_case_insensitive) // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. - || t.is_non_raw_ident_where(|i| quals.contains(&i.name) - // Rule out 2015 `const async: T = val`. - && i.is_reserved() + || ( + ( + t.is_non_raw_ident_where(|i| + quals.contains(&i.name) + // Rule out 2015 `const async: T = val`. + && i.is_reserved() + ) + || kw_case_insensitive + && t.is_non_raw_ident_where(|i| quals.iter().any(|qual| qual.as_str() == i.name.as_str().to_lowercase())) + ) // Rule out unsafe extern block. && !self.is_unsafe_foreign_mod()) }) // `extern ABI fn` - || self.check_keyword(kw::Extern) + || self.check_keyword_case(kw::Extern, kw_case_insensitive) && self.look_ahead(1, |t| t.can_begin_literal_maybe_minus()) - && self.look_ahead(2, |t| t.is_keyword(kw::Fn)) + && self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, kw_case_insensitive)) } /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration, @@ -2195,22 +2200,22 @@ impl<'a> Parser<'a> { /// `Visibility::Inherited` when no visibility is known. pub(super) fn parse_fn_front_matter(&mut self, orig_vis: &Visibility) -> PResult<'a, FnHeader> { let sp_start = self.token.span; - let constness = self.parse_constness(); + let constness = self.parse_constness(true); let async_start_sp = self.token.span; - let asyncness = self.parse_asyncness(); + let asyncness = self.parse_asyncness(true); let unsafe_start_sp = self.token.span; - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(true); let ext_start_sp = self.token.span; - let ext = self.parse_extern(); + let ext = self.parse_extern(true); if let Async::Yes { span, .. } = asyncness { self.ban_async_in_2015(span); } - if !self.eat_keyword(kw::Fn) { + if !self.eat_keyword_case(kw::Fn, true) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't // account for this. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index b82ce90129fe..073c51ac120f 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -604,6 +604,20 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw) } + fn check_keyword_case(&mut self, kw: Symbol, case_insensitive: bool) -> bool { + if self.check_keyword(kw) { + return true; + } + + if case_insensitive + && let Some((ident, /* is_raw */ false)) = self.token.ident() + && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { + true + } else { + false + } + } + /// If the next token is the given keyword, eats it and returns `true`. /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes. // Public for rustfmt usage. @@ -1122,8 +1136,8 @@ impl<'a> Parser<'a> { } /// Parses asyncness: `async` or nothing. - fn parse_asyncness(&mut self) -> Async { - if self.eat_keyword(kw::Async) { + fn parse_asyncness(&mut self, case_insensitive: bool) -> Async { + if self.eat_keyword_case(kw::Async, case_insensitive) { let span = self.prev_token.uninterpolated_span(); Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } } else { @@ -1132,8 +1146,8 @@ impl<'a> Parser<'a> { } /// Parses unsafety: `unsafe` or nothing. - fn parse_unsafety(&mut self) -> Unsafe { - if self.eat_keyword(kw::Unsafe) { + fn parse_unsafety(&mut self, case_insensitive: bool) -> Unsafe { + if self.eat_keyword_case(kw::Unsafe, case_insensitive) { Unsafe::Yes(self.prev_token.uninterpolated_span()) } else { Unsafe::No @@ -1141,10 +1155,10 @@ impl<'a> Parser<'a> { } /// Parses constness: `const` or nothing. - fn parse_constness(&mut self) -> Const { + fn parse_constness(&mut self, case_insensitive: bool) -> Const { // Avoid const blocks to be parsed as const items if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace)) - && self.eat_keyword(kw::Const) + && self.eat_keyword_case(kw::Const, case_insensitive) { Const::Yes(self.prev_token.uninterpolated_span()) } else { @@ -1399,8 +1413,8 @@ impl<'a> Parser<'a> { } /// Parses `extern string_literal?`. - fn parse_extern(&mut self) -> Extern { - if self.eat_keyword(kw::Extern) { + fn parse_extern(&mut self, case_insensitive: bool) -> Extern { + if self.eat_keyword_case(kw::Extern, case_insensitive) { let mut extern_span = self.prev_token.span; let abi = self.parse_abi(); if let Some(abi) = abi { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 2a8512acf8cf..984e303e577e 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -267,7 +267,7 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Underscore) { // A type to be inferred `_` TyKind::Infer - } else if self.check_fn_front_matter(false) { + } else if self.check_fn_front_matter(false, false) { // Function pointer type self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? } else if self.check_keyword(kw::For) { @@ -275,7 +275,7 @@ impl<'a> Parser<'a> { // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - if self.check_fn_front_matter(false) { + if self.check_fn_front_matter(false, false) { self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? } else { let path = self.parse_path(PathStyle::Type)?; diff --git a/src/test/ui/parser/item-kw-case-mismatch.fixed b/src/test/ui/parser/item-kw-case-mismatch.fixed index d99f89ccef5b..1794268f260e 100644 --- a/src/test/ui/parser/item-kw-case-mismatch.fixed +++ b/src/test/ui/parser/item-kw-case-mismatch.fixed @@ -1,7 +1,34 @@ // run-rustfix +// edition:2018 #![allow(unused_imports)] fn main() {} use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case use std::ptr::write; //~ ERROR keyword `use` is written in a wrong case + +async fn _a() {} +//~^ ERROR keyword `fn` is written in a wrong case + +fn _b() {} +//~^ ERROR keyword `fn` is written in a wrong case + +async fn _c() {} +//~^ ERROR keyword `async` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case + +async fn _d() {} +//~^ ERROR keyword `async` is written in a wrong case + +const unsafe fn _e() {} +//~^ ERROR keyword `const` is written in a wrong case +//~| ERROR keyword `unsafe` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case + +unsafe extern fn _f() {} +//~^ ERROR keyword `unsafe` is written in a wrong case +//~| ERROR keyword `extern` is written in a wrong case + +extern "C" fn _g() {} +//~^ ERROR keyword `extern` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case diff --git a/src/test/ui/parser/item-kw-case-mismatch.rs b/src/test/ui/parser/item-kw-case-mismatch.rs index 605552b5f147..ac8390efdb9a 100644 --- a/src/test/ui/parser/item-kw-case-mismatch.rs +++ b/src/test/ui/parser/item-kw-case-mismatch.rs @@ -1,7 +1,34 @@ // run-rustfix +// edition:2018 #![allow(unused_imports)] fn main() {} Use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case USE std::ptr::write; //~ ERROR keyword `use` is written in a wrong case + +async Fn _a() {} +//~^ ERROR keyword `fn` is written in a wrong case + +Fn _b() {} +//~^ ERROR keyword `fn` is written in a wrong case + +aSYNC fN _c() {} +//~^ ERROR keyword `async` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case + +Async fn _d() {} +//~^ ERROR keyword `async` is written in a wrong case + +CONST UNSAFE FN _e() {} +//~^ ERROR keyword `const` is written in a wrong case +//~| ERROR keyword `unsafe` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case + +unSAFE EXTern fn _f() {} +//~^ ERROR keyword `unsafe` is written in a wrong case +//~| ERROR keyword `extern` is written in a wrong case + +EXTERN "C" FN _g() {} +//~^ ERROR keyword `extern` is written in a wrong case +//~| ERROR keyword `fn` is written in a wrong case diff --git a/src/test/ui/parser/item-kw-case-mismatch.stderr b/src/test/ui/parser/item-kw-case-mismatch.stderr index aebbc9d558f2..e66dae825f9c 100644 --- a/src/test/ui/parser/item-kw-case-mismatch.stderr +++ b/src/test/ui/parser/item-kw-case-mismatch.stderr @@ -1,14 +1,86 @@ error: keyword `use` is written in a wrong case - --> $DIR/item-kw-case-mismatch.rs:6:1 + --> $DIR/item-kw-case-mismatch.rs:7:1 | LL | Use std::ptr::read; | ^^^ help: write it in the correct case (notice the capitalization): `use` error: keyword `use` is written in a wrong case - --> $DIR/item-kw-case-mismatch.rs:7:1 + --> $DIR/item-kw-case-mismatch.rs:8:1 | LL | USE std::ptr::write; | ^^^ help: write it in the correct case: `use` -error: aborting due to 2 previous errors +error: keyword `fn` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:10:7 + | +LL | async Fn _a() {} + | ^^ help: write it in the correct case (notice the capitalization): `fn` + +error: keyword `fn` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:13:1 + | +LL | Fn _b() {} + | ^^ help: write it in the correct case (notice the capitalization): `fn` + +error: keyword `async` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:16:1 + | +LL | aSYNC fN _c() {} + | ^^^^^ help: write it in the correct case: `async` + +error: keyword `fn` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:16:7 + | +LL | aSYNC fN _c() {} + | ^^ help: write it in the correct case: `fn` + +error: keyword `async` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:20:1 + | +LL | Async fn _d() {} + | ^^^^^ help: write it in the correct case: `async` + +error: keyword `const` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:23:1 + | +LL | CONST UNSAFE FN _e() {} + | ^^^^^ help: write it in the correct case: `const` + +error: keyword `unsafe` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:23:7 + | +LL | CONST UNSAFE FN _e() {} + | ^^^^^^ help: write it in the correct case: `unsafe` + +error: keyword `fn` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:23:14 + | +LL | CONST UNSAFE FN _e() {} + | ^^ help: write it in the correct case: `fn` + +error: keyword `unsafe` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:28:1 + | +LL | unSAFE EXTern fn _f() {} + | ^^^^^^ help: write it in the correct case: `unsafe` + +error: keyword `extern` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:28:8 + | +LL | unSAFE EXTern fn _f() {} + | ^^^^^^ help: write it in the correct case: `extern` + +error: keyword `extern` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:32:1 + | +LL | EXTERN "C" FN _g() {} + | ^^^^^^ help: write it in the correct case: `extern` + +error: keyword `fn` is written in a wrong case + --> $DIR/item-kw-case-mismatch.rs:32:12 + | +LL | EXTERN "C" FN _g() {} + | ^^ help: write it in the correct case: `fn` + +error: aborting due to 14 previous errors From d86f9cd4640c9ad81ad4aec45c358d1931d40f30 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 15 Sep 2022 20:27:23 +0400 Subject: [PATCH 0017/1126] Replace some `bool` params with an enum --- compiler/rustc_ast/src/lib.rs | 1 + compiler/rustc_ast/src/token.rs | 7 +-- compiler/rustc_ast/src/util/case.rs | 6 +++ compiler/rustc_parse/src/parser/expr.rs | 3 +- compiler/rustc_parse/src/parser/item.rs | 63 +++++++++++++------------ compiler/rustc_parse/src/parser/mod.rs | 25 +++++----- compiler/rustc_parse/src/parser/ty.rs | 5 +- 7 files changed, 63 insertions(+), 47 deletions(-) create mode 100644 compiler/rustc_ast/src/util/case.rs diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index eeb7e56e2b12..9c1dfeb1a614 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -29,6 +29,7 @@ extern crate rustc_macros; extern crate tracing; pub mod util { + pub mod case; pub mod classify; pub mod comments; pub mod literal; diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 3f7e32f4fae6..5cdd0bf60a9d 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -5,6 +5,7 @@ pub use TokenKind::*; use crate::ast; use crate::ptr::P; +use crate::util::case::Case; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; @@ -618,10 +619,10 @@ impl Token { self.is_non_raw_ident_where(|id| id.name == kw) } - /// Returns `true` if the token is a given keyword, `kw` or if `case_insensitive` is true and this token is an identifier equal to `kw` ignoring the case. - pub fn is_keyword_case(&self, kw: Symbol, case_insensitive: bool) -> bool { + /// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this token is an identifier equal to `kw` ignoring the case. + pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool { self.is_keyword(kw) - || (case_insensitive + || (case == Case::Insensitive && self.is_non_raw_ident_where(|id| { id.name.as_str().to_lowercase() == kw.as_str().to_lowercase() })) diff --git a/compiler/rustc_ast/src/util/case.rs b/compiler/rustc_ast/src/util/case.rs new file mode 100644 index 000000000000..1afd7dea7408 --- /dev/null +++ b/compiler/rustc_ast/src/util/case.rs @@ -0,0 +1,6 @@ +/// Whatever to ignore case (`fn` vs `Fn` vs `FN`) or not. Used for recovering. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Case { + Sensitive, + Insensitive, +} diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e08d09a4d9f3..6fbcc3fe5a1b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -33,6 +33,7 @@ use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::Spacing; +use rustc_ast::util::case::Case; use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; @@ -2024,7 +2025,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; let asyncness = if self.token.uninterpolated_span().rust_2018() { - self.parse_asyncness(false) + self.parse_asyncness(Case::Sensitive) } else { Async::No }; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 4e2fc513ac50..83a5704b6680 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -8,6 +8,7 @@ use rustc_ast::ast::*; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; +use rustc_ast::util::case::Case; use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID}; use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind}; @@ -34,7 +35,7 @@ impl<'a> Parser<'a> { /// Parses a `mod { ... }` or `mod ;` item. fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(false); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Mod)?; let id = self.parse_ident()?; let mod_kind = if self.eat(&token::Semi) { @@ -150,7 +151,7 @@ impl<'a> Parser<'a> { &vis, &mut def, fn_parse_mode, - false, + Case::Sensitive, )?; if let Some((ident, kind)) = kind { self.error_on_unconsumed_default(def, &kind); @@ -212,14 +213,14 @@ impl<'a> Parser<'a> { vis: &Visibility, def: &mut Defaultness, fn_parse_mode: FnParseMode, - kw_case_insensitive: bool, + case: Case, ) -> PResult<'a, Option> { let def_final = def == &Defaultness::Final; let mut def_ = || mem::replace(def, Defaultness::Final); - let info = if self.eat_keyword_case(kw::Use, kw_case_insensitive) { + let info = if self.eat_keyword_case(kw::Use, case) { self.parse_use_item()? - } else if self.check_fn_front_matter(def_final, kw_case_insensitive) { + } else if self.check_fn_front_matter(def_final, case) { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?; (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body }))) @@ -233,7 +234,7 @@ impl<'a> Parser<'a> { } } else if self.is_unsafe_foreign_mod() { // EXTERN BLOCK - let unsafety = self.parse_unsafety(false); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Extern)?; self.parse_item_foreign_mod(attrs, unsafety)? } else if self.is_static_global() { @@ -242,7 +243,7 @@ impl<'a> Parser<'a> { let m = self.parse_mutability(); let (ident, ty, expr) = self.parse_item_global(Some(m))?; (ident, ItemKind::Static(ty, m, expr)) - } else if let Const::Yes(const_span) = self.parse_constness(false) { + } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM if self.token.is_keyword(kw::Impl) { // recover from `const impl`, suggest `impl const` @@ -294,11 +295,19 @@ impl<'a> Parser<'a> { } else if self.isnt_macro_invocation() && vis.kind.is_pub() { self.recover_missing_kw_before_item()?; return Ok(None); - } else if self.isnt_macro_invocation() && !kw_case_insensitive { + } else if self.isnt_macro_invocation() && case == Case::Sensitive { _ = def_; // Recover wrong cased keywords - return self.parse_item_kind(attrs, macros_allowed, lo, vis, def, fn_parse_mode, true); + return self.parse_item_kind( + attrs, + macros_allowed, + lo, + vis, + def, + fn_parse_mode, + Case::Insensitive, + ); } else if macros_allowed && self.check_path() { // MACRO INVOCATION ITEM (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?))) @@ -551,7 +560,7 @@ impl<'a> Parser<'a> { attrs: &mut AttrVec, defaultness: Defaultness, ) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(false); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Impl)?; // First, parse generic parameters if necessary. @@ -565,7 +574,7 @@ impl<'a> Parser<'a> { generics }; - let constness = self.parse_constness(false); + let constness = self.parse_constness(Case::Sensitive); if let Const::Yes(span) = constness { self.sess.gated_spans.gate(sym::const_trait_impl, span); } @@ -809,7 +818,7 @@ impl<'a> Parser<'a> { /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(false); + let unsafety = self.parse_unsafety(Case::Sensitive); // Parse optional `auto` prefix. let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No }; @@ -1758,7 +1767,7 @@ impl<'a> Parser<'a> { let (ident, is_raw) = self.ident_or_err()?; if !is_raw && ident.is_reserved() { let snapshot = self.create_snapshot_for_diagnostic(); - let err = if self.check_fn_front_matter(false, false) { + let err = if self.check_fn_front_matter(false, Case::Sensitive) { let inherited_vis = Visibility { span: rustc_span::DUMMY_SP, kind: VisibilityKind::Inherited, @@ -2147,11 +2156,7 @@ impl<'a> Parser<'a> { /// /// `check_pub` adds additional `pub` to the checks in case users place it /// wrongly, can be used to ensure `pub` never comes after `default`. - pub(super) fn check_fn_front_matter( - &mut self, - check_pub: bool, - kw_case_insensitive: bool, - ) -> bool { + pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool { // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. // `pub` is added in case users got confused with the ordering like `async pub fn`, @@ -2161,12 +2166,12 @@ impl<'a> Parser<'a> { } else { &[kw::Const, kw::Async, kw::Unsafe, kw::Extern] }; - self.check_keyword_case(kw::Fn, kw_case_insensitive) // Definitely an `fn`. + self.check_keyword_case(kw::Fn, case) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: - || quals.iter().any(|&kw| self.check_keyword_case(kw, kw_case_insensitive)) + || quals.iter().any(|&kw| self.check_keyword_case(kw, case)) && self.look_ahead(1, |t| { // `$qual fn`, e.g. `const fn` or `async fn`. - t.is_keyword_case(kw::Fn, kw_case_insensitive) + t.is_keyword_case(kw::Fn, case) // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. || ( ( @@ -2175,16 +2180,16 @@ impl<'a> Parser<'a> { // Rule out 2015 `const async: T = val`. && i.is_reserved() ) - || kw_case_insensitive + || case == Case::Insensitive && t.is_non_raw_ident_where(|i| quals.iter().any(|qual| qual.as_str() == i.name.as_str().to_lowercase())) ) // Rule out unsafe extern block. && !self.is_unsafe_foreign_mod()) }) // `extern ABI fn` - || self.check_keyword_case(kw::Extern, kw_case_insensitive) + || self.check_keyword_case(kw::Extern, case) && self.look_ahead(1, |t| t.can_begin_literal_maybe_minus()) - && self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, kw_case_insensitive)) + && self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) } /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration, @@ -2200,22 +2205,22 @@ impl<'a> Parser<'a> { /// `Visibility::Inherited` when no visibility is known. pub(super) fn parse_fn_front_matter(&mut self, orig_vis: &Visibility) -> PResult<'a, FnHeader> { let sp_start = self.token.span; - let constness = self.parse_constness(true); + let constness = self.parse_constness(Case::Insensitive); let async_start_sp = self.token.span; - let asyncness = self.parse_asyncness(true); + let asyncness = self.parse_asyncness(Case::Insensitive); let unsafe_start_sp = self.token.span; - let unsafety = self.parse_unsafety(true); + let unsafety = self.parse_unsafety(Case::Insensitive); let ext_start_sp = self.token.span; - let ext = self.parse_extern(true); + let ext = self.parse_extern(Case::Insensitive); if let Async::Yes { span, .. } = asyncness { self.ban_async_in_2015(span); } - if !self.eat_keyword_case(kw::Fn, true) { + if !self.eat_keyword_case(kw::Fn, Case::Insensitive) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't // account for this. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 073c51ac120f..11753af7039e 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -22,6 +22,7 @@ use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::AttributesData; use rustc_ast::tokenstream::{self, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::util::case::Case; use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, Extern}; @@ -604,12 +605,12 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw) } - fn check_keyword_case(&mut self, kw: Symbol, case_insensitive: bool) -> bool { + fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.check_keyword(kw) { return true; } - if case_insensitive + if case == Case::Insensitive && let Some((ident, /* is_raw */ false)) = self.token.ident() && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { true @@ -633,12 +634,12 @@ impl<'a> Parser<'a> { /// Eats a keyword, optionally ignoring the case. /// If the case differs (and is ignored) an error is issued. /// This is useful for recovery. - fn eat_keyword_case(&mut self, kw: Symbol, case_insensitive: bool) -> bool { + fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.eat_keyword(kw) { return true; } - if case_insensitive + if case == Case::Insensitive && let Some((ident, /* is_raw */ false)) = self.token.ident() && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { self @@ -1136,8 +1137,8 @@ impl<'a> Parser<'a> { } /// Parses asyncness: `async` or nothing. - fn parse_asyncness(&mut self, case_insensitive: bool) -> Async { - if self.eat_keyword_case(kw::Async, case_insensitive) { + fn parse_asyncness(&mut self, case: Case) -> Async { + if self.eat_keyword_case(kw::Async, case) { let span = self.prev_token.uninterpolated_span(); Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } } else { @@ -1146,8 +1147,8 @@ impl<'a> Parser<'a> { } /// Parses unsafety: `unsafe` or nothing. - fn parse_unsafety(&mut self, case_insensitive: bool) -> Unsafe { - if self.eat_keyword_case(kw::Unsafe, case_insensitive) { + fn parse_unsafety(&mut self, case: Case) -> Unsafe { + if self.eat_keyword_case(kw::Unsafe, case) { Unsafe::Yes(self.prev_token.uninterpolated_span()) } else { Unsafe::No @@ -1155,10 +1156,10 @@ impl<'a> Parser<'a> { } /// Parses constness: `const` or nothing. - fn parse_constness(&mut self, case_insensitive: bool) -> Const { + fn parse_constness(&mut self, case: Case) -> Const { // Avoid const blocks to be parsed as const items if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace)) - && self.eat_keyword_case(kw::Const, case_insensitive) + && self.eat_keyword_case(kw::Const, case) { Const::Yes(self.prev_token.uninterpolated_span()) } else { @@ -1413,8 +1414,8 @@ impl<'a> Parser<'a> { } /// Parses `extern string_literal?`. - fn parse_extern(&mut self, case_insensitive: bool) -> Extern { - if self.eat_keyword_case(kw::Extern, case_insensitive) { + fn parse_extern(&mut self, case: Case) -> Extern { + if self.eat_keyword_case(kw::Extern, case) { let mut extern_span = self.prev_token.span; let abi = self.parse_abi(); if let Some(abi) = abi { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 984e303e577e..0f3281e52856 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -4,6 +4,7 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, @@ -267,7 +268,7 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Underscore) { // A type to be inferred `_` TyKind::Infer - } else if self.check_fn_front_matter(false, false) { + } else if self.check_fn_front_matter(false, Case::Sensitive) { // Function pointer type self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? } else if self.check_keyword(kw::For) { @@ -275,7 +276,7 @@ impl<'a> Parser<'a> { // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - if self.check_fn_front_matter(false, false) { + if self.check_fn_front_matter(false, Case::Sensitive) { self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? } else { let path = self.parse_path(PathStyle::Type)?; From b91dc035106f4c203d67d2a0db0077c7a887224b Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 1 Oct 2022 18:59:17 +0200 Subject: [PATCH 0018/1126] Add `as_ptr_cast_mut` lint This lint detects calls to a `&self`-taking `as_ptr` method, where the result is then immediately cast to a `*mut T`. Code like this is probably invalid, as that pointer will not have write permissions, and `*mut T` is usually used to write through. --- CHANGELOG.md | 1 + clippy_lints/src/casts/as_ptr_cast_mut.rs | 38 +++++++++++++++++++++++ clippy_lints/src/casts/mod.rs | 31 +++++++++++++++++- clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_nursery.rs | 1 + src/docs.rs | 1 + src/docs/as_ptr_cast_mut.txt | 19 ++++++++++++ tests/ui/as_ptr_cast_mut.rs | 37 ++++++++++++++++++++++ tests/ui/as_ptr_cast_mut.stderr | 16 ++++++++++ 9 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/casts/as_ptr_cast_mut.rs create mode 100644 src/docs/as_ptr_cast_mut.txt create mode 100644 tests/ui/as_ptr_cast_mut.rs create mode 100644 tests/ui/as_ptr_cast_mut.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ef6140152ffc..754d5f506d4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3735,6 +3735,7 @@ Released 2018-09-13 [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions +[`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut [`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states diff --git a/clippy_lints/src/casts/as_ptr_cast_mut.rs b/clippy_lints/src/casts/as_ptr_cast_mut.rs new file mode 100644 index 000000000000..9409f4844f54 --- /dev/null +++ b/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::{ + mir::Mutability, + ty::{self, Ty, TypeAndMut}, +}; + +use super::AS_PTR_CAST_MUT; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) { + if let ty::RawPtr(ptrty @ TypeAndMut { mutbl: Mutability::Mut, .. }) = cast_to.kind() + && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) = + cx.typeck_results().node_type(cast_expr.hir_id).kind() + && let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind + && method_name.ident.name == rustc_span::sym::as_ptr + && let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id) + && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did) + && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next() + && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() + && let Some(recv) = snippet_opt(cx, receiver.span) + { + // `as_mut_ptr` might not exist + let applicability = Applicability::MaybeIncorrect; + + span_lint_and_sugg( + cx, + AS_PTR_CAST_MUT, + expr.span, + &format!("casting the result of `as_ptr` to *{ptrty}"), + "replace with", + format!("{recv}.as_mut_ptr()"), + applicability + ); + } +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index cc5d346b954e..fb0bd4d30f76 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -1,3 +1,4 @@ +mod as_ptr_cast_mut; mod as_underscore; mod borrow_as_ptr; mod cast_abs_to_unsigned; @@ -596,6 +597,32 @@ declare_clippy_lint! { "casting a slice created from a pointer and length to a slice pointer" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer + /// + /// ### Why is this bad? + /// Since `as_ptr` took a `&self`, the pointer won't have write permissions, making it + /// unlikely that having it as a mutable pointer is correct. + /// + /// ### Example + /// ```rust + /// let string = String::with_capacity(1); + /// let ptr = string.as_ptr() as *mut _; + /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR + /// ``` + /// Use instead: + /// ```rust + /// let mut string = String::with_capacity(1); + /// let string = string.as_mut_ptr(); + /// unsafe { ptr.write(4) }; + /// ``` + #[clippy::version = "1.66.0"] + pub AS_PTR_CAST_MUT, + nursery, + "casting the result of the `&self`-taking as_ptr to a mutabe point" +} + pub struct Casts { msrv: Option, } @@ -627,7 +654,8 @@ impl_lint_pass!(Casts => [ CAST_ABS_TO_UNSIGNED, AS_UNDERSCORE, BORROW_AS_PTR, - CAST_SLICE_FROM_RAW_PARTS + CAST_SLICE_FROM_RAW_PARTS, + AS_PTR_CAST_MUT, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -653,6 +681,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv); + as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index ee08d802ccfb..2c8497013655 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -66,6 +66,7 @@ store.register_lints(&[ cargo::NEGATIVE_FEATURE_NAMES, cargo::REDUNDANT_FEATURE_NAMES, cargo::WILDCARD_DEPENDENCIES, + casts::AS_PTR_CAST_MUT, casts::AS_UNDERSCORE, casts::BORROW_AS_PTR, casts::CAST_ABS_TO_UNSIGNED, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 87be0052028f..a75bc81b2222 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -4,6 +4,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), + LintId::of(casts::AS_PTR_CAST_MUT), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), diff --git a/src/docs.rs b/src/docs.rs index 166be0618ffd..c3cbd2942d86 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -28,6 +28,7 @@ docs! { "approx_constant", "arithmetic_side_effects", "as_conversions", + "as_ptr_cast_mut", "as_underscore", "assertions_on_constants", "assertions_on_result_states", diff --git a/src/docs/as_ptr_cast_mut.txt b/src/docs/as_ptr_cast_mut.txt new file mode 100644 index 000000000000..0192a0b686fb --- /dev/null +++ b/src/docs/as_ptr_cast_mut.txt @@ -0,0 +1,19 @@ +### What it does +Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer + +### Why is this bad? +Since `as_ptr` took a `&self`, the pointer won't have write permissions, making it +unlikely that having it as a mutable pointer is correct. + +### Example +``` +let string = String::with_capacity(1); +let ptr = string.as_ptr() as *mut _; +unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR +``` +Use instead: +``` +let mut string = String::with_capacity(1); +let string = string.as_mut_ptr(); +unsafe { ptr.write(4) }; +``` \ No newline at end of file diff --git a/tests/ui/as_ptr_cast_mut.rs b/tests/ui/as_ptr_cast_mut.rs new file mode 100644 index 000000000000..0d1d9258433b --- /dev/null +++ b/tests/ui/as_ptr_cast_mut.rs @@ -0,0 +1,37 @@ +#![allow(unused)] +#![warn(clippy::as_ptr_cast_mut)] +#![allow(clippy::wrong_self_convention)] + +struct MutPtrWrapper(Vec); +impl MutPtrWrapper { + fn as_ptr(&mut self) -> *const u8 { + self.0.as_mut_ptr() as *const u8 + } +} + +struct Covariant(*const T); +impl Covariant { + fn as_ptr(self) -> *const T { + self.0 + } +} + +fn main() { + let mut string = String::new(); + let _ = string.as_ptr() as *mut u8; + let _: *mut i8 = string.as_ptr() as *mut _; + let _ = string.as_ptr() as *const i8; + let _ = string.as_mut_ptr(); + let _ = string.as_mut_ptr() as *mut u8; + let _ = string.as_mut_ptr() as *const u8; + + let nn = std::ptr::NonNull::new(4 as *mut u8).unwrap(); + let _ = nn.as_ptr() as *mut u8; + + let mut wrap = MutPtrWrapper(Vec::new()); + let _ = wrap.as_ptr() as *mut u8; + + let mut local = 4; + let ref_with_write_perm = Covariant(std::ptr::addr_of_mut!(local) as *const _); + let _ = ref_with_write_perm.as_ptr() as *mut u8; +} diff --git a/tests/ui/as_ptr_cast_mut.stderr b/tests/ui/as_ptr_cast_mut.stderr new file mode 100644 index 000000000000..2189c3d2f855 --- /dev/null +++ b/tests/ui/as_ptr_cast_mut.stderr @@ -0,0 +1,16 @@ +error: casting the result of `as_ptr` to *mut u8 + --> $DIR/as_ptr_cast_mut.rs:21:13 + | +LL | let _ = string.as_ptr() as *mut u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()` + | + = note: `-D clippy::as-ptr-cast-mut` implied by `-D warnings` + +error: casting the result of `as_ptr` to *mut i8 + --> $DIR/as_ptr_cast_mut.rs:22:22 + | +LL | let _: *mut i8 = string.as_ptr() as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()` + +error: aborting due to 2 previous errors + From 169ef781f9ddc9954a2e1aa58351cd14983c9ae6 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 1 Oct 2022 20:10:43 +0200 Subject: [PATCH 0019/1126] Improve wording --- clippy_lints/src/casts/mod.rs | 6 +++--- src/docs/as_ptr_cast_mut.txt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index fb0bd4d30f76..9a48e8d064a5 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -602,8 +602,8 @@ declare_clippy_lint! { /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer /// /// ### Why is this bad? - /// Since `as_ptr` took a `&self`, the pointer won't have write permissions, making it - /// unlikely that having it as a mutable pointer is correct. + /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior + /// mutability is used, making it unlikely that having it as a mutable pointer is correct. /// /// ### Example /// ```rust @@ -620,7 +620,7 @@ declare_clippy_lint! { #[clippy::version = "1.66.0"] pub AS_PTR_CAST_MUT, nursery, - "casting the result of the `&self`-taking as_ptr to a mutabe point" + "casting the result of the `&self`-taking `as_ptr` to a mutabe pointer" } pub struct Casts { diff --git a/src/docs/as_ptr_cast_mut.txt b/src/docs/as_ptr_cast_mut.txt index 0192a0b686fb..83332a2559d1 100644 --- a/src/docs/as_ptr_cast_mut.txt +++ b/src/docs/as_ptr_cast_mut.txt @@ -2,8 +2,8 @@ Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer ### Why is this bad? -Since `as_ptr` took a `&self`, the pointer won't have write permissions, making it -unlikely that having it as a mutable pointer is correct. +Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior +mutability is used, making it unlikely that having it as a mutable pointer is correct. ### Example ``` From 2b944d0c38ebfb99252ccfcb6200db28a217ee07 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 1 Oct 2022 20:31:54 +0200 Subject: [PATCH 0020/1126] Fix example --- clippy_lints/src/casts/mod.rs | 4 ++-- src/docs/as_ptr_cast_mut.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 9a48e8d064a5..b8f0dedf5ed9 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -608,13 +608,13 @@ declare_clippy_lint! { /// ### Example /// ```rust /// let string = String::with_capacity(1); - /// let ptr = string.as_ptr() as *mut _; + /// let ptr = string.as_ptr() as *mut u8; /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR /// ``` /// Use instead: /// ```rust /// let mut string = String::with_capacity(1); - /// let string = string.as_mut_ptr(); + /// let ptr = string.as_mut_ptr(); /// unsafe { ptr.write(4) }; /// ``` #[clippy::version = "1.66.0"] diff --git a/src/docs/as_ptr_cast_mut.txt b/src/docs/as_ptr_cast_mut.txt index 83332a2559d1..228dde996bb2 100644 --- a/src/docs/as_ptr_cast_mut.txt +++ b/src/docs/as_ptr_cast_mut.txt @@ -8,12 +8,12 @@ mutability is used, making it unlikely that having it as a mutable pointer is co ### Example ``` let string = String::with_capacity(1); -let ptr = string.as_ptr() as *mut _; +let ptr = string.as_ptr() as *mut u8; unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR ``` Use instead: ``` let mut string = String::with_capacity(1); -let string = string.as_mut_ptr(); +let ptr = string.as_mut_ptr(); unsafe { ptr.write(4) }; ``` \ No newline at end of file From b89ac0cefcc19b99a5af902ced053344a8054568 Mon Sep 17 00:00:00 2001 From: kraktus Date: Mon, 3 Oct 2022 14:11:54 +0200 Subject: [PATCH 0021/1126] refactor `manual_filter` Move common functions to `manual_utils.rs`, better arm matching, use clippy utils `contains_unsafe_block` --- clippy_lints/src/matches/manual_filter.rs | 76 ++---- clippy_lints/src/matches/manual_map.rs | 287 +--------------------- clippy_lints/src/matches/manual_utils.rs | 278 +++++++++++++++++++++ clippy_lints/src/matches/mod.rs | 1 + clippy_utils/src/sugg.rs | 2 +- 5 files changed, 311 insertions(+), 333 deletions(-) create mode 100644 clippy_lints/src/matches/manual_utils.rs diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 9931f1268ab3..66ba1f6f9c55 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -1,39 +1,20 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; -use rustc_hir::intravisit::{walk_expr, Visitor}; + use rustc_hir::LangItem::OptionSome; -use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, HirId, Pat, PatKind, UnsafeSource}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::{sym, SyntaxContext}; -use super::manual_map::{check_with, SomeExpr}; +use super::manual_utils::{check_with, SomeExpr}; use super::MANUAL_FILTER; -#[derive(Default)] -struct NeedsUnsafeBlock(pub bool); - -impl<'tcx> Visitor<'tcx> for NeedsUnsafeBlock { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - match expr.kind { - ExprKind::Block( - Block { - rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), - .. - }, - _, - ) => { - self.0 = true; - }, - _ => walk_expr(self, expr), - } - } -} - -// Function called on the `expr` of `[&+]Some((ref | ref mut) x) => ` -// Need to check if it's of the `if {} else {}` +// Function called on the of `[&+]Some((ref | ref mut) x) => ` +// Need to check if it's of the form `=if {} else {}` // AND that only one `then/else_expr` resolves to `Some(x)` while the other resolves to `None` -// return `cond` if +// return the `cond` expression if so. fn get_cond_expr<'tcx>( cx: &LateContext<'tcx>, pat: &Pat<'_>, @@ -45,15 +26,13 @@ fn get_cond_expr<'tcx>( if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; if let PatKind::Binding(_,target, ..) = pat.kind; if let (then_visitor, else_visitor) - = (handle_if_or_else_expr(cx, target, ctxt, then_expr), - handle_if_or_else_expr(cx, target, ctxt, else_expr)); + = (is_some_expr(cx, target, ctxt, then_expr), + is_some_expr(cx, target, ctxt, else_expr)); if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None` then { - let mut needs_unsafe_block = NeedsUnsafeBlock::default(); - needs_unsafe_block.visit_expr(expr); return Some(SomeExpr { expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), - needs_unsafe_block: needs_unsafe_block.0, + needs_unsafe_block: contains_unsafe_block(cx, expr), needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond }) } @@ -63,7 +42,7 @@ fn get_cond_expr<'tcx>( fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's - // checked by `NeedsUnsafeBlock` + // checked by `contains_unsafe_block` if let ExprKind::Block(block, None) = expr.kind { if block.stmts.is_empty() { return block.expr; @@ -76,23 +55,18 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr) } -// function called for each expression: +// function called for each expression: // Some(x) => if { -// +// // } else { -// +// // } -// Returns true if resolves to `Some(x)`, `false` otherwise -fn handle_if_or_else_expr<'tcx>( - cx: &LateContext<'_>, - target: HirId, - ctxt: SyntaxContext, - if_or_else_expr: &'tcx Expr<'_>, -) -> bool { - if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(if_or_else_expr) { +// Returns true if resolves to `Some(x)`, `false` otherwise +fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &'tcx Expr<'_>) -> bool { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { // there can be not statements in the block as they would be removed when switching to `.filter` if let ExprKind::Call(callee, [arg]) = inner_expr.kind { - return ctxt == if_or_else_expr.span.ctxt() + return ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) && path_to_local_id(arg, target); } @@ -119,15 +93,13 @@ pub(super) fn check_match<'tcx>( expr: &'tcx Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr); - if_chain! { - if is_type_diagnostic_item(cx, ty, sym::Option); - if arms.len() == 2; - if arms[0].guard.is_none(); - if arms[1].guard.is_none(); - then { - check(cx, expr, scrutinee, arms[0].pat, arms[0].body, Some(arms[1].pat), arms[1].body) + if is_type_diagnostic_item(cx, ty, sym::Option) + && let [first_arm, second_arm] = arms + && first_arm.guard.is_none() + && second_arm.guard.is_none() + { + check(cx, expr, scrutinee, first_arm.pat, first_arm.body, Some(second_arm.pat), second_arm.body); } - } } pub(super) fn check_if_let<'tcx>( diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index 2b6a07c5d744..aaba239677ff 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -1,21 +1,13 @@ +use super::manual_utils::{check_with, SomeExpr}; use super::MANUAL_MAP; -use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; -use clippy_utils::{ - can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, sugg::Sugg, CaptureKind, -}; -use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{ - def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, - QPath, UnsafeSource, -}; + +use clippy_utils::{is_res_lang_ctor, path_res}; + +use rustc_hir::LangItem::OptionSome; +use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource}; use rustc_lint::LateContext; -use rustc_span::{sym, SyntaxContext}; +use rustc_span::SyntaxContext; pub(super) fn check_match<'tcx>( cx: &LateContext<'tcx>, @@ -83,266 +75,6 @@ fn check<'tcx>( } } -#[expect(clippy::too_many_arguments)] -#[expect(clippy::too_many_lines)] -pub(super) fn check_with<'tcx, F>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - scrutinee: &'tcx Expr<'_>, - then_pat: &'tcx Pat<'_>, - then_body: &'tcx Expr<'_>, - else_pat: Option<&'tcx Pat<'_>>, - else_body: &'tcx Expr<'_>, - get_some_expr_fn: F, -) -> Option> -where - F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, -{ - let (scrutinee_ty, ty_ref_count, ty_mutability) = - peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) - { - return None; - } - - let expr_ctxt = expr.span.ctxt(); - let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( - try_parse_pattern(cx, then_pat, expr_ctxt), - else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), - ) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, false) - }, - _ => return None, - }; - - // Top level or patterns aren't allowed in closures. - if matches!(some_pat.kind, PatKind::Or(_)) { - return None; - } - - let some_expr = match get_some_expr_fn(cx, some_pat, some_expr, expr_ctxt) { - Some(expr) => expr, - None => return None, - }; - - // These two lints will go back and forth with each other. - if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit - && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) - { - return None; - } - - // `map` won't perform any adjustments. - if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { - return None; - } - - // Determine which binding mode to use. - let explicit_ref = some_pat.contains_explicit_ref_binding(); - let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability)); - - let as_ref_str = match binding_ref { - Some(Mutability::Mut) => ".as_mut()", - Some(Mutability::Not) => ".as_ref()", - None => "", - }; - - match can_move_expr_to_closure(cx, some_expr.expr) { - Some(captures) => { - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `[.]*` is checked for. - if let Some(binding_ref_mutability) = binding_ref { - let e = peel_hir_expr_while(scrutinee, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { - match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, - Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return None; - }, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), - } - } - } - }, - None => return None, - }; - - let mut app = Applicability::MachineApplicable; - - // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or - // it's being passed by value. - let scrutinee = peel_hir_expr_refs(scrutinee).0; - let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({scrutinee_str})") - } else { - scrutinee_str.into() - }; - - let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); - let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - if_chain! { - if !some_expr.needs_unsafe_block; - if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); - if func.span.ctxt() == some_expr.expr.span.ctxt(); - then { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() - } else { - if path_to_local_id(some_expr.expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return None; - } - - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::MUT) { - "mut " - } else { - "" - }; - - if some_expr.needs_unsafe_block { - format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") - } else { - format!("|{annotation}{some_binding}| {closure_expr_snip}") - } - } - } - } else if !is_wild_none && explicit_ref.is_none() { - // TODO: handle explicit reference annotations. - let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}") - } else { - format!("|{pat_snip}| {closure_expr_snip}") - } - } else { - // Refutable bindings and mixed reference annotations can't be handled by `map`. - return None; - }; - - // relies on the fact that Option: Copy where T: copy - let scrutinee_impl_copy = is_copy(cx, scrutinee_ty); - - Some(SuggInfo { - needs_brackets: else_pat.is_none() && is_else_clause(cx.tcx, expr), - scrutinee_impl_copy, - scrutinee_str, - as_ref_str, - body_str, - app, - }) -} - -pub struct SuggInfo<'a> { - pub needs_brackets: bool, - pub scrutinee_impl_copy: bool, - pub scrutinee_str: String, - pub as_ref_str: &'a str, - pub body_str: String, - pub app: Applicability, -} - -// Checks whether the expression could be passed as a function, or whether a closure is needed. -// Returns the function to be passed to `map` if it exists. -fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - match expr.kind { - ExprKind::Call(func, [arg]) - if path_to_local_id(arg, binding) - && cx.typeck_results().expr_adjustments(arg).is_empty() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => - { - Some(func) - }, - _ => None, - } -} - -#[derive(Debug)] -pub(super) enum OptionPat<'a> { - Wild, - None, - Some { - // The pattern contained in the `Some` tuple. - pattern: &'a Pat<'a>, - // The number of references before the `Some` tuple. - // e.g. `&&Some(_)` has a ref count of 2. - ref_count: usize, - }, -} - -pub(super) struct SomeExpr<'tcx> { - pub expr: &'tcx Expr<'tcx>, - pub needs_unsafe_block: bool, - pub needs_negated: bool, // for `manual_filter` lint -} - -impl<'tcx> SomeExpr<'tcx> { - pub fn new_no_negated(expr: &'tcx Expr<'tcx>, needs_unsafe_block: bool) -> Self { - Self { - expr, - needs_unsafe_block, - needs_negated: false, - } - } - - pub fn to_snippet_with_context( - &self, - cx: &LateContext<'tcx>, - ctxt: SyntaxContext, - app: &mut Applicability, - ) -> Sugg<'tcx> { - let sugg = Sugg::hir_with_context(cx, self.expr, ctxt, "..", app); - if self.needs_negated { !sugg } else { sugg } - } -} - -// Try to parse into a recognized `Option` pattern. -// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -pub(super) fn try_parse_pattern<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - ctxt: SyntaxContext, -) -> Option> { - fn f<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - ref_count: usize, - ctxt: SyntaxContext, - ) -> Option> { - match pat.kind { - PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { - Some(OptionPat::None) - }, - PatKind::TupleStruct(ref qpath, [pattern], _) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => - { - Some(OptionPat::Some { pattern, ref_count }) - }, - _ => None, - } - } - f(cx, pat, 0, ctxt) -} - // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. fn get_some_expr<'tcx>( cx: &LateContext<'tcx>, @@ -382,8 +114,3 @@ fn get_some_expr<'tcx>( } get_some_expr_internal(cx, expr, false, ctxt) } - -// Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) -} diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs new file mode 100644 index 000000000000..792908aa7dfc --- /dev/null +++ b/clippy_lints/src/matches/manual_utils.rs @@ -0,0 +1,278 @@ +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::{ + can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, sugg::Sugg, CaptureKind, +}; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_lines)] +pub(super) fn check_with<'tcx, F>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, + get_some_expr_fn: F, +) -> Option> +where + F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, +{ + let (scrutinee_ty, ty_ref_count, ty_mutability) = + peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + { + return None; + } + + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, then_pat, expr_ctxt), + else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, false) + }, + _ => return None, + }; + + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return None; + } + + let some_expr = match get_some_expr_fn(cx, some_pat, some_expr, expr_ctxt) { + Some(expr) => expr, + None => return None, + }; + + // These two lints will go back and forth with each other. + if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit + && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) + { + return None; + } + + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { + return None; + } + + // Determine which binding mode to use. + let explicit_ref = some_pat.contains_explicit_ref_binding(); + let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability)); + + let as_ref_str = match binding_ref { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; + + match can_move_expr_to_closure(cx, some_expr.expr) { + Some(captures) => { + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { + return None; + }, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } + }, + None => return None, + }; + + let mut app = Applicability::MachineApplicable; + + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // it's being passed by value. + let scrutinee = peel_hir_expr_refs(scrutinee).0; + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({scrutinee_str})") + } else { + scrutinee_str.into() + }; + + let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + if_chain! { + if !some_expr.needs_unsafe_block; + if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); + if func.span.ctxt() == some_expr.expr.span.ctxt(); + then { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + if path_to_local_id(some_expr.expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return None; + } + + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::MUT) { + "mut " + } else { + "" + }; + + if some_expr.needs_unsafe_block { + format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{annotation}{some_binding}| {closure_expr_snip}") + } + } + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { + format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{pat_snip}| {closure_expr_snip}") + } + } else { + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return None; + }; + + // relies on the fact that Option: Copy where T: copy + let scrutinee_impl_copy = is_copy(cx, scrutinee_ty); + + Some(SuggInfo { + needs_brackets: else_pat.is_none() && is_else_clause(cx.tcx, expr), + scrutinee_impl_copy, + scrutinee_str, + as_ref_str, + body_str, + app, + }) +} + +pub struct SuggInfo<'a> { + pub needs_brackets: bool, + pub scrutinee_impl_copy: bool, + pub scrutinee_str: String, + pub as_ref_str: &'a str, + pub body_str: String, + pub app: Applicability, +} + +// Checks whether the expression could be passed as a function, or whether a closure is needed. +// Returns the function to be passed to `map` if it exists. +fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + match expr.kind { + ExprKind::Call(func, [arg]) + if path_to_local_id(arg, binding) + && cx.typeck_results().expr_adjustments(arg).is_empty() + && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + { + Some(func) + }, + _ => None, + } +} + +#[derive(Debug)] +pub(super) enum OptionPat<'a> { + Wild, + None, + Some { + // The pattern contained in the `Some` tuple. + pattern: &'a Pat<'a>, + // The number of references before the `Some` tuple. + // e.g. `&&Some(_)` has a ref count of 2. + ref_count: usize, + }, +} + +pub(super) struct SomeExpr<'tcx> { + pub expr: &'tcx Expr<'tcx>, + pub needs_unsafe_block: bool, + pub needs_negated: bool, // for `manual_filter` lint +} + +impl<'tcx> SomeExpr<'tcx> { + pub fn new_no_negated(expr: &'tcx Expr<'tcx>, needs_unsafe_block: bool) -> Self { + Self { + expr, + needs_unsafe_block, + needs_negated: false, + } + } + + pub fn to_snippet_with_context( + &self, + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + app: &mut Applicability, + ) -> Sugg<'tcx> { + let sugg = Sugg::hir_with_context(cx, self.expr, ctxt, "..", app); + if self.needs_negated { !sugg } else { sugg } + } +} + +// Try to parse into a recognized `Option` pattern. +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. +pub(super) fn try_parse_pattern<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ctxt: SyntaxContext, +) -> Option> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option> { + match pat.kind { + PatKind::Wild => Some(OptionPat::Wild), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), + PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { + Some(OptionPat::None) + }, + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => + { + Some(OptionPat::Some { pattern, ref_count }) + }, + _ => None, + } + } + f(cx, pat, 0, ctxt) +} + +// Checks for the `None` value. +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) +} diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index cf1bd7a12ade..c472d6280985 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -3,6 +3,7 @@ mod infallible_destructuring_match; mod manual_filter; mod manual_map; mod manual_unwrap_or; +mod manual_utils; mod match_as_ref; mod match_bool; mod match_like_matches; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index e88542b77a67..f25bced0c2b3 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -112,7 +112,7 @@ impl<'a> Sugg<'a> { if expr.span.ctxt() == ctxt { Self::hir_from_snippet(expr, |span| snippet(cx, span, default)) } else { - let snip = snippet_with_context(cx, expr.span, ctxt, default, applicability).0; + let (snip, _) = snippet_with_context(cx, expr.span, ctxt, default, applicability); Sugg::NonParen(snip) } } From 830fdf2b56d2a2f0f8e8135e05ec30b08e54ad3a Mon Sep 17 00:00:00 2001 From: kraktus Date: Sun, 2 Oct 2022 17:45:44 +0200 Subject: [PATCH 0022/1126] update rust version introduction --- clippy_lints/src/matches/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index c472d6280985..7d8171ead89e 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -922,7 +922,7 @@ declare_clippy_lint! { /// ```rust /// Some(0).filter(|&x| x % 2 == 0); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub MANUAL_FILTER, complexity, "reimplentation of `filter`" From 093b075d327d1c7c7e0730e46f1d5c2d4aa47f5a Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sun, 2 Oct 2022 17:31:43 +0200 Subject: [PATCH 0023/1126] rustc: Use `unix_sigpipe` instead of `rustc_driver::set_sigpipe_handler` This is the first (known) step towards starting to use `unix_sigpipe` in the wild. Eventually, `rustc_driver::set_sigpipe_handler` can be removed and all clients can use `unix_sigpipe` instead. For now we just start using `unix_sigpipe` in once place: `rustc` itself. It is easy to manually verify this change. If you remove `#[unix_sigpipe = "sig_dfl"]` and run `./x.py build` you will get an ICE when you do `./build/x86_64-unknown-linux-gnu/stage1/bin/rustc --help | false`. Add back `#[unix_sigpipe = "sig_dfl"]` and the ICE disappears again. --- compiler/rustc/src/main.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs index 0de1a7819135..e21c9b660444 100644 --- a/compiler/rustc/src/main.rs +++ b/compiler/rustc/src/main.rs @@ -1,3 +1,5 @@ +#![feature(unix_sigpipe)] + // A note about jemalloc: rustc uses jemalloc when built for CI and // distribution. The obvious way to do this is with the `#[global_allocator]` // mechanism. However, for complicated reasons (see @@ -23,6 +25,7 @@ // libraries. So we must reference jemalloc symbols one way or another, because // this file is the only object code in the rustc executable. +#[unix_sigpipe = "sig_dfl"] fn main() { // See the comment at the top of this file for an explanation of this. #[cfg(feature = "jemalloc-sys")] @@ -58,6 +61,5 @@ fn main() { } } - rustc_driver::set_sigpipe_handler(); rustc_driver::main() } From afe29e48bc4d57c1bb73228ba8d70dc8f42436fc Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Wed, 5 Oct 2022 11:23:57 +0200 Subject: [PATCH 0024/1126] split steps for generating the standalone docs and the shared assets Before this commit, the step to generate the standalone docs (which included the index page and other HTML files at the root of the documentation) was bundled with the code copying files needed by multiple pieces of documentation. This means it wasn't possible to avoid generating the standalone docs. This commit splits the step into two, allowing the standalone docs generation to be excluded while still building the rest of the docs. --- src/bootstrap/doc.rs | 61 ++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 819af6587484..b286c79a3030 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -227,7 +227,7 @@ impl Step for TheBook { } // build the version info page and CSS - builder.ensure(Standalone { compiler, target }); + let shared_assets = builder.ensure(SharedAssets { target }); // build the redirect pages builder.info(&format!("Documenting book redirect pages ({})", target)); @@ -236,7 +236,7 @@ impl Step for TheBook { let path = file.path(); let path = path.to_str().unwrap(); - invoke_rustdoc(builder, compiler, target, path); + invoke_rustdoc(builder, compiler, &shared_assets, target, path); } if builder.was_invoked_explicitly::(Kind::Doc) { @@ -250,6 +250,7 @@ impl Step for TheBook { fn invoke_rustdoc( builder: &Builder<'_>, compiler: Compiler, + shared_assets: &SharedAssetsPaths, target: TargetSelection, markdown: &str, ) { @@ -259,7 +260,6 @@ fn invoke_rustdoc( let header = builder.src.join("src/doc/redirect.inc"); let footer = builder.src.join("src/doc/footer.inc"); - let version_info = out.join("version_info.html"); let mut cmd = builder.rustdoc_cmd(compiler); @@ -268,7 +268,7 @@ fn invoke_rustdoc( cmd.arg("--html-after-content") .arg(&footer) .arg("--html-before-content") - .arg(&version_info) + .arg(&shared_assets.version_info) .arg("--html-in-header") .arg(&header) .arg("--markdown-no-toc") @@ -324,21 +324,11 @@ impl Step for Standalone { let out = builder.doc_out(target); t!(fs::create_dir_all(&out)); + let version_info = builder.ensure(SharedAssets { target: self.target }).version_info; + let favicon = builder.src.join("src/doc/favicon.inc"); let footer = builder.src.join("src/doc/footer.inc"); let full_toc = builder.src.join("src/doc/full-toc.inc"); - t!(fs::copy(builder.src.join("src/doc/rust.css"), out.join("rust.css"))); - - let version_input = builder.src.join("src/doc/version_info.html.template"); - let version_info = out.join("version_info.html"); - - if !builder.config.dry_run && !up_to_date(&version_input, &version_info) { - let info = t!(fs::read_to_string(&version_input)) - .replace("VERSION", &builder.rust_release()) - .replace("SHORT_HASH", builder.rust_info.sha_short().unwrap_or("")) - .replace("STAMP", builder.rust_info.sha().unwrap_or("")); - t!(fs::write(&version_info, &info)); - } for file in t!(fs::read_dir(builder.src.join("src/doc"))) { let file = t!(file); @@ -400,6 +390,45 @@ impl Step for Standalone { } } +#[derive(Debug, Clone)] +pub struct SharedAssetsPaths { + pub version_info: PathBuf, +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct SharedAssets { + target: TargetSelection, +} + +impl Step for SharedAssets { + type Output = SharedAssetsPaths; + const DEFAULT: bool = false; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + // Other tasks depend on this, no need to execute it on its own + run.never() + } + + // Generate shared resources used by other pieces of documentation. + fn run(self, builder: &Builder<'_>) -> Self::Output { + let out = builder.doc_out(self.target); + + let version_input = builder.src.join("src").join("doc").join("version_info.html.template"); + let version_info = out.join("version_info.html"); + if !builder.config.dry_run && !up_to_date(&version_input, &version_info) { + let info = t!(fs::read_to_string(&version_input)) + .replace("VERSION", &builder.rust_release()) + .replace("SHORT_HASH", builder.rust_info.sha_short().unwrap_or("")) + .replace("STAMP", builder.rust_info.sha().unwrap_or("")); + t!(fs::write(&version_info, &info)); + } + + builder.copy(&builder.src.join("src").join("doc").join("rust.css"), &out.join("rust.css")); + + SharedAssetsPaths { version_info } + } +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Std { pub stage: u32, From 004b8b98d626a89b5d4cdc630ff337c0f4ea919f Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Wed, 5 Oct 2022 12:45:52 +0200 Subject: [PATCH 0025/1126] add a "standalone" path for doc::Standalone to be able to exclude it Before this commit, the path for the doc::Standalone step was "src/doc", which is accurate as the standalone docs source files live at the root of the "src/doc" directory tree. Unfortunately, that caused bad interactions when trying to exclude it with `--exclude src/doc`. When an exclusion is passed to bootstrap, it will exclude all steps whose path *ends with* the exclusion, which results in the Cargo book (src/tools/cargo/src/doc) to also be excluded. To work around this problem, this commit adds the "standalone" path as an alternate path for doc::Standalone, allowing `--exclude standalone` to work without side effects. --- src/bootstrap/doc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index b286c79a3030..a8524fea1b2a 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -299,7 +299,7 @@ impl Step for Standalone { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; - run.path("src/doc").default_condition(builder.config.docs) + run.path("src/doc").path("standalone").default_condition(builder.config.docs) } fn make_run(run: RunConfig<'_>) { From 9e70a0ff12547f0764041c9cff8adf617d7762c5 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 5 Oct 2022 17:17:57 +0000 Subject: [PATCH 0026/1126] Replace if_chain with let chains in `clippy::author` output --- clippy_lints/src/utils/author.rs | 117 +++++++++-------- tests/ui/author.stdout | 24 ++-- tests/ui/author/blocks.stdout | 116 ++++++++--------- tests/ui/author/call.stdout | 28 ++--- tests/ui/author/if.stdout | 92 +++++++------- tests/ui/author/issue_3849.stdout | 24 ++-- tests/ui/author/loop.stdout | 202 ++++++++++++++---------------- tests/ui/author/matches.stdout | 72 ++++++----- tests/ui/author/repeat.stdout | 20 ++- tests/ui/author/struct.stdout | 112 ++++++++--------- 10 files changed, 387 insertions(+), 420 deletions(-) diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index e069de8cb5c7..0c052d86eda4 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -12,6 +12,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::{Ident, Symbol}; +use std::cell::Cell; use std::fmt::{Display, Formatter, Write as _}; declare_clippy_lint! { @@ -37,15 +38,13 @@ declare_clippy_lint! { /// /// ```rust,ignore /// // ./tests/ui/new_lint.stdout - /// if_chain! { - /// if let ExprKind::If(ref cond, ref then, None) = item.kind, - /// if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind, - /// if let ExprKind::Path(ref path) = left.kind, - /// if let ExprKind::Lit(ref lit) = right.kind, - /// if let LitKind::Int(42, _) = lit.node, - /// then { - /// // report your lint here - /// } + /// if ExprKind::If(ref cond, ref then, None) = item.kind + /// && let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind + /// && let ExprKind::Path(ref path) = left.kind + /// && let ExprKind::Lit(ref lit) = right.kind + /// && let LitKind::Int(42, _) = lit.node + /// { + /// // report your lint here /// } /// ``` pub LINT_AUTHOR, @@ -91,15 +90,16 @@ macro_rules! field { }; } -fn prelude() { - println!("if_chain! {{"); -} - -fn done() { - println!(" then {{"); - println!(" // report your lint here"); - println!(" }}"); - println!("}}"); +/// Print a condition of a let chain, `chain!(self, "let Some(x) = y")` will print +/// `if let Some(x) = y` on the first call and ` && let Some(x) = y` thereafter +macro_rules! chain { + ($self:ident, $($t:tt)*) => { + if $self.first.take() { + println!("if {}", format_args!($($t)*)); + } else { + println!(" && {}", format_args!($($t)*)); + } + } } impl<'tcx> LateLintPass<'tcx> for Author { @@ -149,9 +149,10 @@ fn check_item(cx: &LateContext<'_>, hir_id: HirId) { fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { if has_attr(cx, hir_id) { - prelude(); f(&PrintVisitor::new(cx)); - done(); + println!("{{"); + println!(" // report your lint here"); + println!("}}"); } } @@ -195,7 +196,9 @@ struct PrintVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, /// Fields are the current index that needs to be appended to pattern /// binding names - ids: std::cell::Cell>, + ids: Cell>, + /// Currently at the first condition in the if chain + first: Cell, } #[allow(clippy::unused_self)] @@ -203,7 +206,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - ids: std::cell::Cell::default(), + ids: Cell::default(), + first: Cell::new(true), } } @@ -226,10 +230,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn option(&self, option: &Binding>, name: &'static str, f: impl Fn(&Binding)) { match option.value { - None => out!("if {option}.is_none();"), + None => chain!(self, "{option}.is_none()"), Some(value) => { let value = &self.bind(name, value); - out!("if let Some({value}) = {option};"); + chain!(self, "let Some({value}) = {option}"); f(value); }, } @@ -237,9 +241,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn slice(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) { if slice.value.is_empty() { - out!("if {slice}.is_empty();"); + chain!(self, "{slice}.is_empty()"); } else { - out!("if {slice}.len() == {};", slice.value.len()); + chain!(self, "{slice}.len() == {}", slice.value.len()); for (i, value) in slice.value.iter().enumerate() { let name = format!("{slice}[{i}]"); f(&Binding { name, value }); @@ -254,23 +258,23 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn ident(&self, ident: &Binding) { - out!("if {ident}.as_str() == {:?};", ident.value.as_str()); + chain!(self, "{ident}.as_str() == {:?}", ident.value.as_str()); } fn symbol(&self, symbol: &Binding) { - out!("if {symbol}.as_str() == {:?};", symbol.value.as_str()); + chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } fn qpath(&self, qpath: &Binding<&QPath<'_>>) { if let QPath::LangItem(lang_item, ..) = *qpath.value { - out!("if matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _));"); + chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); } else { - out!("if match_qpath({qpath}, &[{}]);", path_to_string(qpath.value)); + chain!(self, "match_qpath({qpath}, &[{}])", path_to_string(qpath.value)); } } fn lit(&self, lit: &Binding<&Lit>) { - let kind = |kind| out!("if let LitKind::{kind} = {lit}.node;"); + let kind = |kind| chain!(self, "let LitKind::{kind} = {lit}.node"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -298,7 +302,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { LitKind::ByteStr(ref vec) => { bind!(self, vec); kind!("ByteStr(ref {vec})"); - out!("if let [{:?}] = **{vec};", vec.value); + chain!(self, "let [{:?}] = **{vec}", vec.value); }, LitKind::Str(s, _) => { bind!(self, s); @@ -311,15 +315,15 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn arm(&self, arm: &Binding<&hir::Arm<'_>>) { self.pat(field!(arm.pat)); match arm.value.guard { - None => out!("if {arm}.guard.is_none();"), + None => chain!(self, "{arm}.guard.is_none()"), Some(hir::Guard::If(expr)) => { bind!(self, expr); - out!("if let Some(Guard::If({expr})) = {arm}.guard;"); + chain!(self, "let Some(Guard::If({expr})) = {arm}.guard"); self.expr(expr); }, Some(hir::Guard::IfLet(let_expr)) => { bind!(self, let_expr); - out!("if let Some(Guard::IfLet({let_expr}) = {arm}.guard;"); + chain!(self, "let Some(Guard::IfLet({let_expr}) = {arm}.guard"); self.pat(field!(let_expr.pat)); self.expr(field!(let_expr.init)); }, @@ -331,9 +335,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) { bind!(self, condition, body); - out!( - "if let Some(higher::While {{ condition: {condition}, body: {body} }}) \ - = higher::While::hir({expr});" + chain!( + self, + "let Some(higher::While {{ condition: {condition}, body: {body} }}) \ + = higher::While::hir({expr})" ); self.expr(condition); self.expr(body); @@ -347,9 +352,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }) = higher::WhileLet::hir(expr.value) { bind!(self, let_pat, let_expr, if_then); - out!( - "if let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ - = higher::WhileLet::hir({expr});" + chain!( + self, + "let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ + = higher::WhileLet::hir({expr})" ); self.pat(let_pat); self.expr(let_expr); @@ -359,9 +365,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) { bind!(self, pat, arg, body); - out!( - "if let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ - = higher::ForLoop::hir({expr});" + chain!( + self, + "let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ + = higher::ForLoop::hir({expr})" ); self.pat(pat); self.expr(arg); @@ -369,7 +376,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { return; } - let kind = |kind| out!("if let ExprKind::{kind} = {expr}.kind;"); + let kind = |kind| chain!(self, "let ExprKind::{kind} = {expr}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -383,7 +390,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { // if it's a path if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { bind!(self, qpath); - out!("if let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind;"); + chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); self.qpath(qpath); } self.expr(field!(let_expr.init)); @@ -419,7 +426,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::Binary(op, left, right) => { bind!(self, op, left, right); kind!("Binary({op}, {left}, {right})"); - out!("if BinOpKind::{:?} == {op}.node;", op.value.node); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); self.expr(left); self.expr(right); }, @@ -438,7 +445,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Cast({expr}, {cast_ty})"); if let TyKind::Path(ref qpath) = cast_ty.value.kind { bind!(self, qpath); - out!("if let TyKind::Path(ref {qpath}) = {cast_ty}.kind;"); + chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); self.qpath(qpath); } self.expr(expr); @@ -485,7 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, fn_decl, body_id); kind!("Closure(CaptureBy::{capture_clause:?}, {fn_decl}, {body_id}, _, {movability})"); - out!("if let {ret_ty} = {fn_decl}.output;"); + chain!(self, "let {ret_ty} = {fn_decl}.output"); self.body(body_id); }, ExprKind::Yield(sub, source) => { @@ -509,7 +516,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::AssignOp(op, target, value) => { bind!(self, op, target, value); kind!("AssignOp({op}, {target}, {value})"); - out!("if BinOpKind::{:?} == {op}.node;", op.value.node); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); self.expr(target); self.expr(value); }, @@ -573,10 +580,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Repeat({value}, {length})"); self.expr(value); match length.value { - ArrayLen::Infer(..) => out!("if let ArrayLen::Infer(..) = length;"), + ArrayLen::Infer(..) => chain!(self, "let ArrayLen::Infer(..) = length"), ArrayLen::Body(anon_const) => { bind!(self, anon_const); - out!("if let ArrayLen::Body({anon_const}) = {length};"); + chain!(self, "let ArrayLen::Body({anon_const}) = {length}"); self.body(field!(anon_const.body)); }, } @@ -600,12 +607,12 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn body(&self, body_id: &Binding) { let expr = self.cx.tcx.hir().body(body_id.value).value; bind!(self, expr); - out!("let {expr} = &cx.tcx.hir().body({body_id}).value;"); + chain!(self, "{expr} = &cx.tcx.hir().body({body_id}).value"); self.expr(expr); } fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { - let kind = |kind| out!("if let PatKind::{kind} = {pat}.kind;"); + let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -688,7 +695,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) { - let kind = |kind| out!("if let StmtKind::{kind} = {stmt}.kind;"); + let kind = |kind| chain!(self, "let StmtKind::{kind} = {stmt}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } diff --git a/tests/ui/author.stdout b/tests/ui/author.stdout index 597318a556b8..27ad538f24d8 100644 --- a/tests/ui/author.stdout +++ b/tests/ui/author.stdout @@ -1,14 +1,12 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Cast(expr, cast_ty) = init.kind; - if let TyKind::Path(ref qpath) = cast_ty.kind; - if match_qpath(qpath, &["char"]); - if let ExprKind::Lit(ref lit) = expr.kind; - if let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "x"; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Cast(expr, cast_ty) = init.kind + && let TyKind::Path(ref qpath) = cast_ty.kind + && match_qpath(qpath, &["char"]) + && let ExprKind::Lit(ref lit) = expr.kind + && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" +{ + // report your lint here } diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index a529981e2e68..9de0550d81d0 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -1,64 +1,58 @@ -if_chain! { - if let ExprKind::Block(block, None) = expr.kind; - if block.stmts.len() == 3; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Lit(ref lit) = init.kind; - if let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "x"; - if let StmtKind::Local(local1) = block.stmts[1].kind; - if let Some(init1) = local1.init; - if let ExprKind::Lit(ref lit1) = init1.kind; - if let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind; - if name1.as_str() == "_t"; - if let StmtKind::Semi(e) = block.stmts[2].kind; - if let ExprKind::Unary(UnOp::Neg, inner) = e.kind; - if let ExprKind::Path(ref qpath) = inner.kind; - if match_qpath(qpath, &["x"]); - if block.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 3 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Lit(ref lit) = init.kind + && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" + && let StmtKind::Local(local1) = block.stmts[1].kind + && let Some(init1) = local1.init + && let ExprKind::Lit(ref lit1) = init1.kind + && let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind + && name1.as_str() == "_t" + && let StmtKind::Semi(e) = block.stmts[2].kind + && let ExprKind::Unary(UnOp::Neg, inner) = e.kind + && let ExprKind::Path(ref qpath) = inner.kind + && match_qpath(qpath, &["x"]) + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::Block(block, None) = expr.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["String", "new"]); - if args.is_empty(); - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "expr"; - if let Some(trailing_expr) = block.expr; - if let ExprKind::Call(func1, args1) = trailing_expr.kind; - if let ExprKind::Path(ref qpath1) = func1.kind; - if match_qpath(qpath1, &["drop"]); - if args1.len() == 1; - if let ExprKind::Path(ref qpath2) = args1[0].kind; - if match_qpath(qpath2, &["expr"]); - then { - // report your lint here - } +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["String", "new"]) + && args.is_empty() + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "expr" + && let Some(trailing_expr) = block.expr + && let ExprKind::Call(func1, args1) = trailing_expr.kind + && let ExprKind::Path(ref qpath1) = func1.kind + && match_qpath(qpath1, &["drop"]) + && args1.len() == 1 + && let ExprKind::Path(ref qpath2) = args1[0].kind + && match_qpath(qpath2, &["expr"]) +{ + // report your lint here } -if_chain! { - if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind; - if let FnRetTy::DefaultReturn(_) = fn_decl.output; - let expr1 = &cx.tcx.hir().body(body_id).value; - if let ExprKind::Call(func, args) = expr1.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)); - if args.len() == 1; - if let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind; - if let FnRetTy::DefaultReturn(_) = fn_decl1.output; - let expr2 = &cx.tcx.hir().body(body_id1).value; - if let ExprKind::Block(block, None) = expr2.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind + && let FnRetTy::DefaultReturn(_) = fn_decl.output + && expr1 = &cx.tcx.hir().body(body_id).value + && let ExprKind::Call(func, args) = expr1.kind + && let ExprKind::Path(ref qpath) = func.kind + && matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)) + && args.len() == 1 + && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind + && let FnRetTy::DefaultReturn(_) = fn_decl1.output + && expr2 = &cx.tcx.hir().body(body_id1).value + && let ExprKind::Block(block, None) = expr2.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout index 266312d63e50..f040f6330a64 100644 --- a/tests/ui/author/call.stdout +++ b/tests/ui/author/call.stdout @@ -1,16 +1,14 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]); - if args.len() == 2; - if let ExprKind::Lit(ref lit) = args[0].kind; - if let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node; - if let ExprKind::Lit(ref lit1) = args[1].kind; - if let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node; - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]) + && args.len() == 2 + && let ExprKind::Lit(ref lit) = args[0].kind + && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node + && let ExprKind::Lit(ref lit1) = args[1].kind + && let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout index 8d92849b3668..5d79618820d8 100644 --- a/tests/ui/author/if.stdout +++ b/tests/ui/author/if.stdout @@ -1,50 +1,46 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::If(cond, then, Some(else_expr)) = init.kind; - if let ExprKind::DropTemps(expr) = cond.kind; - if let ExprKind::Lit(ref lit) = expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Binary(op, left, right) = e.kind; - if BinOpKind::Eq == op.node; - if let ExprKind::Lit(ref lit1) = left.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Lit(ref lit2) = right.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node; - if block.expr.is_none(); - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.len() == 1; - if let StmtKind::Semi(e1) = block1.stmts[0].kind; - if let ExprKind::Binary(op1, left1, right1) = e1.kind; - if BinOpKind::Eq == op1.node; - if let ExprKind::Lit(ref lit3) = left1.kind; - if let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node; - if let ExprKind::Lit(ref lit4) = right1.kind; - if let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node; - if block1.expr.is_none(); - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::If(cond, then, Some(else_expr)) = init.kind + && let ExprKind::DropTemps(expr) = cond.kind + && let ExprKind::Lit(ref lit) = expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Block(block, None) = then.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Binary(op, left, right) = e.kind + && BinOpKind::Eq == op.node + && let ExprKind::Lit(ref lit1) = left.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Lit(ref lit2) = right.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node + && block.expr.is_none() + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.len() == 1 + && let StmtKind::Semi(e1) = block1.stmts[0].kind + && let ExprKind::Binary(op1, left1, right1) = e1.kind + && BinOpKind::Eq == op1.node + && let ExprKind::Lit(ref lit3) = left1.kind + && let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node + && let ExprKind::Lit(ref lit4) = right1.kind + && let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node + && block1.expr.is_none() + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } -if_chain! { - if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind; - if let ExprKind::Let(let_expr) = cond.kind; - if let PatKind::Lit(lit_expr) = let_expr.pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Path(ref qpath) = let_expr.init.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.is_empty(); - if block1.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind + && let ExprKind::Let(let_expr) = cond.kind + && let PatKind::Lit(lit_expr) = let_expr.pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Path(ref qpath) = let_expr.init.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = then.kind + && block.stmts.is_empty() + && block.expr.is_none() + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.is_empty() + && block1.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/issue_3849.stdout b/tests/ui/author/issue_3849.stdout index bce4bc702733..32a3127b85a3 100644 --- a/tests/ui/author/issue_3849.stdout +++ b/tests/ui/author/issue_3849.stdout @@ -1,14 +1,12 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["std", "mem", "transmute"]); - if args.len() == 1; - if let ExprKind::Path(ref qpath1) = args[0].kind; - if match_qpath(qpath1, &["ZPTR"]); - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["std", "mem", "transmute"]) + && args.len() == 1 + && let ExprKind::Path(ref qpath1) = args[0].kind + && match_qpath(qpath1, &["ZPTR"]) + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout index ceb53fcd4963..94a6436ed547 100644 --- a/tests/ui/author/loop.stdout +++ b/tests/ui/author/loop.stdout @@ -1,113 +1,101 @@ -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind; - if name.as_str() == "y"; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Path(ref qpath1) = init.kind; - if match_qpath(qpath1, &["y"]); - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind; - if name1.as_str() == "z"; - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind + && name.as_str() == "y" + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Path(ref qpath1) = init.kind + && match_qpath(qpath1, &["y"]) + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind + && name1.as_str() == "z" + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Wild = pat.kind; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Wild = pat.kind + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Wild = pat.kind; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if let Some(label) = destination.label; - if label.ident.as_str() == "'label"; - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Wild = pat.kind + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && let Some(label) = destination.label + && label.ident.as_str() == "'label" + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr); - if let ExprKind::Path(ref qpath) = condition.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr) + && let ExprKind::Path(ref qpath) = condition.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr); - if let PatKind::Lit(lit_expr) = let_pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Path(ref qpath) = let_expr.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = if_then.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) + && let PatKind::Lit(lit_expr) = let_pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Path(ref qpath) = let_expr.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = if_then.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind; - if body.stmts.len() == 1; - if let StmtKind::Semi(e) = body.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if body.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind + && body.stmts.len() == 1 + && let StmtKind::Semi(e) = body.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && body.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/matches.stdout b/tests/ui/author/matches.stdout index 2cf69a035b4c..88e2ca656a4f 100644 --- a/tests/ui/author/matches.stdout +++ b/tests/ui/author/matches.stdout @@ -1,38 +1,36 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind; - if let ExprKind::Lit(ref lit) = scrutinee.kind; - if let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node; - if arms.len() == 3; - if let PatKind::Lit(lit_expr) = arms[0].pat.kind; - if let ExprKind::Lit(ref lit1) = lit_expr.kind; - if let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node; - if arms[0].guard.is_none(); - if let ExprKind::Lit(ref lit2) = arms[0].body.kind; - if let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node; - if let PatKind::Lit(lit_expr1) = arms[1].pat.kind; - if let ExprKind::Lit(ref lit3) = lit_expr1.kind; - if let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node; - if arms[1].guard.is_none(); - if let ExprKind::Block(block, None) = arms[1].body.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local1) = block.stmts[0].kind; - if let Some(init1) = local1.init; - if let ExprKind::Lit(ref lit4) = init1.kind; - if let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind; - if name.as_str() == "x"; - if let Some(trailing_expr) = block.expr; - if let ExprKind::Path(ref qpath) = trailing_expr.kind; - if match_qpath(qpath, &["x"]); - if let PatKind::Wild = arms[2].pat.kind; - if arms[2].guard.is_none(); - if let ExprKind::Lit(ref lit5) = arms[2].body.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind; - if name1.as_str() == "a"; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind + && let ExprKind::Lit(ref lit) = scrutinee.kind + && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node + && arms.len() == 3 + && let PatKind::Lit(lit_expr) = arms[0].pat.kind + && let ExprKind::Lit(ref lit1) = lit_expr.kind + && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node + && arms[0].guard.is_none() + && let ExprKind::Lit(ref lit2) = arms[0].body.kind + && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node + && let PatKind::Lit(lit_expr1) = arms[1].pat.kind + && let ExprKind::Lit(ref lit3) = lit_expr1.kind + && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node + && arms[1].guard.is_none() + && let ExprKind::Block(block, None) = arms[1].body.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local1) = block.stmts[0].kind + && let Some(init1) = local1.init + && let ExprKind::Lit(ref lit4) = init1.kind + && let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind + && name.as_str() == "x" + && let Some(trailing_expr) = block.expr + && let ExprKind::Path(ref qpath) = trailing_expr.kind + && match_qpath(qpath, &["x"]) + && let PatKind::Wild = arms[2].pat.kind + && arms[2].guard.is_none() + && let ExprKind::Lit(ref lit5) = arms[2].body.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind + && name1.as_str() == "a" +{ + // report your lint here } diff --git a/tests/ui/author/repeat.stdout b/tests/ui/author/repeat.stdout index 471bbce4f418..c2a369610cc1 100644 --- a/tests/ui/author/repeat.stdout +++ b/tests/ui/author/repeat.stdout @@ -1,12 +1,10 @@ -if_chain! { - if let ExprKind::Repeat(value, length) = expr.kind; - if let ExprKind::Lit(ref lit) = value.kind; - if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node; - if let ArrayLen::Body(anon_const) = length; - let expr1 = &cx.tcx.hir().body(anon_const.body).value; - if let ExprKind::Lit(ref lit1) = expr1.kind; - if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node; - then { - // report your lint here - } +if let ExprKind::Repeat(value, length) = expr.kind + && let ExprKind::Lit(ref lit) = value.kind + && let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node + && let ArrayLen::Body(anon_const) = length + && expr1 = &cx.tcx.hir().body(anon_const.body).value + && let ExprKind::Lit(ref lit1) = expr1.kind + && let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node +{ + // report your lint here } diff --git a/tests/ui/author/struct.stdout b/tests/ui/author/struct.stdout index b5bbc9e213c6..0b332d5e7d0e 100644 --- a/tests/ui/author/struct.stdout +++ b/tests/ui/author/struct.stdout @@ -1,64 +1,56 @@ -if_chain! { - if let ExprKind::Struct(qpath, fields, None) = expr.kind; - if match_qpath(qpath, &["Test"]); - if fields.len() == 1; - if fields[0].ident.as_str() == "field"; - if let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind; - if let ExprKind::DropTemps(expr1) = cond.kind; - if let ExprKind::Lit(ref lit) = expr1.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.is_empty(); - if let Some(trailing_expr) = block.expr; - if let ExprKind::Lit(ref lit1) = trailing_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.is_empty(); - if let Some(trailing_expr1) = block1.expr; - if let ExprKind::Lit(ref lit2) = trailing_expr1.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node; - then { - // report your lint here - } +if let ExprKind::Struct(qpath, fields, None) = expr.kind + && match_qpath(qpath, &["Test"]) + && fields.len() == 1 + && fields[0].ident.as_str() == "field" + && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind + && let ExprKind::DropTemps(expr1) = cond.kind + && let ExprKind::Lit(ref lit) = expr1.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Block(block, None) = then.kind + && block.stmts.is_empty() + && let Some(trailing_expr) = block.expr + && let ExprKind::Lit(ref lit1) = trailing_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.is_empty() + && let Some(trailing_expr1) = block1.expr + && let ExprKind::Lit(ref lit2) = trailing_expr1.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node +{ + // report your lint here } -if_chain! { - if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind; - if match_qpath(qpath, &["Test"]); - if fields.len() == 1; - if fields[0].ident.as_str() == "field"; - if let PatKind::Lit(lit_expr) = fields[0].pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if arm.guard.is_none(); - if let ExprKind::Block(block, None) = arm.body.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind + && match_qpath(qpath, &["Test"]) + && fields.len() == 1 + && fields[0].ident.as_str() == "field" + && let PatKind::Lit(lit_expr) = fields[0].pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && arm.guard.is_none() + && let ExprKind::Block(block, None) = arm.body.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind; - if match_qpath(qpath, &["TestTuple"]); - if fields.len() == 1; - if let PatKind::Lit(lit_expr) = fields[0].kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if arm.guard.is_none(); - if let ExprKind::Block(block, None) = arm.body.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind + && match_qpath(qpath, &["TestTuple"]) + && fields.len() == 1 + && let PatKind::Lit(lit_expr) = fields[0].kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && arm.guard.is_none() + && let ExprKind::Block(block, None) = arm.body.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind; - if method_name.ident.as_str() == "test"; - if let ExprKind::Path(ref qpath) = receiver.kind; - if match_qpath(qpath, &["test_method_call"]); - if args.is_empty(); - then { - // report your lint here - } +if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind + && method_name.ident.as_str() == "test" + && let ExprKind::Path(ref qpath) = receiver.kind + && match_qpath(qpath, &["test_method_call"]) + && args.is_empty() +{ + // report your lint here } From d75b25faabdcf0a22fe37928917c4ab1761fa265 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 6 Oct 2022 09:44:38 +0200 Subject: [PATCH 0027/1126] Merge commit 'ac0e10aa68325235069a842f47499852b2dee79e' into clippyup --- CHANGELOG.md | 159 +++- Cargo.toml | 8 +- README.md | 46 +- clippy_dev/src/fmt.rs | 6 +- clippy_dev/src/main.rs | 2 +- clippy_dev/src/new_lint.rs | 161 ++-- clippy_dev/src/serve.rs | 4 +- clippy_dev/src/setup/git_hook.rs | 7 +- clippy_dev/src/setup/intellij.rs | 25 +- clippy_dev/src/setup/vscode.rs | 19 +- clippy_dev/src/update_lints.rs | 103 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/approx_const.rs | 4 +- clippy_lints/src/asm_syntax.rs | 6 +- clippy_lints/src/assertions_on_constants.rs | 4 +- .../src/assertions_on_result_states.rs | 10 +- clippy_lints/src/attrs.rs | 7 +- clippy_lints/src/await_holding_invalid.rs | 42 +- clippy_lints/src/blocks_in_if_conditions.rs | 68 +- clippy_lints/src/bool_assert_comparison.rs | 4 +- clippy_lints/src/bool_to_int_with_if.rs | 21 +- clippy_lints/src/booleans.rs | 5 +- clippy_lints/src/box_default.rs | 61 ++ clippy_lints/src/cargo/common_metadata.rs | 2 +- clippy_lints/src/cargo/feature_name.rs | 4 +- clippy_lints/src/cargo/mod.rs | 4 +- .../src/cargo/multiple_crate_versions.rs | 2 +- clippy_lints/src/casts/borrow_as_ptr.rs | 2 +- clippy_lints/src/casts/cast_lossless.rs | 12 +- .../src/casts/cast_possible_truncation.rs | 16 +- clippy_lints/src/casts/cast_possible_wrap.rs | 5 +- clippy_lints/src/casts/cast_ptr_alignment.rs | 4 +- clippy_lints/src/casts/cast_sign_loss.rs | 5 +- .../src/casts/cast_slice_different_sizes.rs | 4 +- clippy_lints/src/casts/char_lit_as_u8.rs | 2 +- clippy_lints/src/casts/fn_to_numeric_cast.rs | 4 +- .../src/casts/fn_to_numeric_cast_any.rs | 4 +- .../fn_to_numeric_cast_with_truncation.rs | 7 +- clippy_lints/src/casts/ptr_as_ptr.rs | 4 +- clippy_lints/src/casts/unnecessary_cast.rs | 81 +- clippy_lints/src/checked_conversions.rs | 16 +- clippy_lints/src/cognitive_complexity.rs | 64 +- clippy_lints/src/default.rs | 17 +- .../src/default_instead_of_iter_empty.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 4 +- .../src/default_union_representation.rs | 2 +- clippy_lints/src/dereference.rs | 53 +- clippy_lints/src/derive.rs | 2 +- clippy_lints/src/disallowed_macros.rs | 151 +++ clippy_lints/src/disallowed_methods.rs | 17 +- clippy_lints/src/disallowed_script_idents.rs | 3 +- clippy_lints/src/disallowed_types.rs | 24 +- clippy_lints/src/doc.rs | 92 +- clippy_lints/src/doc_link_with_quotes.rs | 60 -- clippy_lints/src/drop_forget_ref.rs | 30 +- clippy_lints/src/entry.rs | 28 +- clippy_lints/src/enum_variants.rs | 7 +- clippy_lints/src/equatable_if_let.rs | 7 +- clippy_lints/src/escape.rs | 10 +- clippy_lints/src/eta_reduction.rs | 19 +- clippy_lints/src/exhaustive_items.rs | 2 +- clippy_lints/src/explicit_write.rs | 8 +- clippy_lints/src/float_literal.rs | 6 +- clippy_lints/src/floating_point_arithmetic.rs | 55 +- clippy_lints/src/format.rs | 2 +- clippy_lints/src/format_args.rs | 154 ++- clippy_lints/src/format_impl.rs | 6 +- clippy_lints/src/formatting.rs | 25 +- clippy_lints/src/from_str_radix_10.rs | 6 +- clippy_lints/src/functions/must_use.rs | 122 ++- .../src/functions/not_unsafe_ptr_arg_deref.rs | 91 +- .../src/functions/too_many_arguments.rs | 5 +- clippy_lints/src/functions/too_many_lines.rs | 5 +- clippy_lints/src/if_then_some_else_none.rs | 19 +- clippy_lints/src/implicit_hasher.rs | 9 +- clippy_lints/src/implicit_return.rs | 16 +- clippy_lints/src/implicit_saturating_add.rs | 114 +++ clippy_lints/src/implicit_saturating_sub.rs | 20 +- .../src/inconsistent_struct_constructor.rs | 6 +- clippy_lints/src/index_refutable_slice.rs | 4 +- clippy_lints/src/infinite_iter.rs | 15 +- clippy_lints/src/inherent_to_string.rs | 19 +- clippy_lints/src/inline_fn_without_body.rs | 2 +- clippy_lints/src/int_plus_one.rs | 4 +- .../src/iter_not_returning_iterator.rs | 5 +- clippy_lints/src/large_const_arrays.rs | 2 +- clippy_lints/src/len_zero.rs | 38 +- clippy_lints/src/let_if_seq.rs | 3 +- clippy_lints/src/lib.register_all.rs | 5 +- clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_internal.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 11 +- clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 3 +- clippy_lints/src/lib.register_perf.rs | 1 + clippy_lints/src/lib.register_style.rs | 3 +- clippy_lints/src/lib.rs | 56 +- clippy_lints/src/lifetimes.rs | 6 +- clippy_lints/src/literal_representation.rs | 2 +- .../src/loops/explicit_counter_loop.rs | 14 +- clippy_lints/src/loops/explicit_iter_loop.rs | 2 +- clippy_lints/src/loops/for_kv_map.rs | 4 +- clippy_lints/src/loops/manual_find.rs | 9 +- clippy_lints/src/loops/manual_flatten.rs | 14 +- clippy_lints/src/loops/manual_memcpy.rs | 13 +- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 10 +- clippy_lints/src/loops/needless_collect.rs | 6 +- clippy_lints/src/loops/needless_range_loop.rs | 10 +- clippy_lints/src/loops/never_loop.rs | 37 +- clippy_lints/src/loops/same_item_push.rs | 5 +- clippy_lints/src/loops/utils.rs | 5 +- .../src/loops/while_let_on_iterator.rs | 12 +- clippy_lints/src/macro_use.rs | 6 +- clippy_lints/src/manual_assert.rs | 29 +- clippy_lints/src/manual_async_fn.rs | 6 +- clippy_lints/src/manual_clamp.rs | 713 ++++++++++++++ clippy_lints/src/manual_non_exhaustive.rs | 4 +- clippy_lints/src/manual_rem_euclid.rs | 2 +- clippy_lints/src/manual_retain.rs | 18 +- clippy_lints/src/manual_strip.rs | 9 +- clippy_lints/src/map_unit_fn.rs | 9 +- clippy_lints/src/match_result_ok.rs | 6 +- clippy_lints/src/matches/collapsible_match.rs | 6 +- clippy_lints/src/matches/manual_map.rs | 44 +- clippy_lints/src/matches/manual_unwrap_or.rs | 22 +- clippy_lints/src/matches/match_as_ref.rs | 18 +- .../src/matches/match_like_matches.rs | 5 +- clippy_lints/src/matches/match_same_arms.rs | 2 +- .../src/matches/match_single_binding.rs | 28 +- .../src/matches/match_str_case_mismatch.rs | 4 +- .../src/matches/match_wild_err_arm.rs | 2 +- clippy_lints/src/matches/needless_match.rs | 9 +- .../src/matches/redundant_pattern_match.rs | 65 +- .../matches/significant_drop_in_scrutinee.rs | 6 +- clippy_lints/src/matches/single_match.rs | 6 +- clippy_lints/src/matches/try_err.rs | 9 +- clippy_lints/src/mem_replace.rs | 72 +- .../src/methods/bind_instead_of_map.rs | 2 +- clippy_lints/src/methods/bytes_nth.rs | 2 +- clippy_lints/src/methods/chars_cmp.rs | 5 +- .../src/methods/chars_cmp_with_unwrap.rs | 5 +- clippy_lints/src/methods/clone_on_copy.rs | 13 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 11 +- clippy_lints/src/methods/filetype_is_file.rs | 11 +- clippy_lints/src/methods/filter_map_next.rs | 2 +- clippy_lints/src/methods/filter_next.rs | 2 +- .../methods/from_iter_instead_of_collect.rs | 6 +- clippy_lints/src/methods/get_first.rs | 4 +- clippy_lints/src/methods/get_last_with_len.rs | 6 +- clippy_lints/src/methods/get_unwrap.rs | 11 +- clippy_lints/src/methods/implicit_clone.rs | 6 +- .../src/methods/inefficient_to_string.rs | 9 +- clippy_lints/src/methods/into_iter_on_ref.rs | 3 +- .../src/methods/is_digit_ascii_radix.rs | 7 +- .../src/methods/iter_cloned_collect.rs | 4 +- clippy_lints/src/methods/iter_count.rs | 2 +- clippy_lints/src/methods/iter_kv_map.rs | 8 +- clippy_lints/src/methods/iter_next_slice.rs | 2 +- clippy_lints/src/methods/iter_nth.rs | 4 +- .../iter_on_single_or_empty_collections.rs | 27 +- clippy_lints/src/methods/iter_with_drain.rs | 2 +- clippy_lints/src/methods/manual_ok_or.rs | 38 +- .../methods/manual_saturating_arithmetic.rs | 5 +- clippy_lints/src/methods/manual_str_repeat.rs | 10 +- clippy_lints/src/methods/map_clone.rs | 5 +- clippy_lints/src/methods/map_flatten.rs | 9 +- clippy_lints/src/methods/map_identity.rs | 2 +- clippy_lints/src/methods/map_unwrap_or.rs | 2 +- clippy_lints/src/methods/mod.rs | 87 +- .../src/methods/option_as_ref_deref.rs | 9 +- .../src/methods/option_map_or_none.rs | 22 +- .../src/methods/option_map_unwrap_or.rs | 9 +- clippy_lints/src/methods/or_fun_call.rs | 10 +- clippy_lints/src/methods/or_then_unwrap.rs | 5 +- clippy_lints/src/methods/search_is_some.rs | 14 +- .../src/methods/single_char_insert_string.rs | 2 +- .../src/methods/single_char_push_string.rs | 2 +- .../src/methods/stable_sort_primitive.rs | 4 +- clippy_lints/src/methods/str_splitn.rs | 20 +- .../src/methods/string_extend_chars.rs | 3 +- clippy_lints/src/methods/suspicious_splitn.rs | 4 +- .../src/methods/suspicious_to_owned.rs | 4 +- .../src/methods/unnecessary_filter_map.rs | 73 +- clippy_lints/src/methods/unnecessary_fold.rs | 7 +- .../src/methods/unnecessary_iter_cloned.rs | 2 +- .../src/methods/unnecessary_lazy_eval.rs | 10 +- .../src/methods/unnecessary_to_owned.rs | 23 +- clippy_lints/src/methods/useless_asref.rs | 7 +- .../src/methods/wrong_self_convention.rs | 15 +- clippy_lints/src/minmax.rs | 4 +- clippy_lints/src/misc.rs | 24 +- clippy_lints/src/misc_early/literal_suffix.rs | 8 +- clippy_lints/src/misc_early/mod.rs | 5 +- .../src/misc_early/unneeded_field_pattern.rs | 4 +- .../src/mismatching_type_param_order.rs | 7 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/missing_doc.rs | 2 +- .../src/missing_enforced_import_rename.rs | 7 +- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/module_style.rs | 8 +- clippy_lints/src/mut_reference.rs | 2 +- clippy_lints/src/mutable_debug_assertion.rs | 5 +- clippy_lints/src/mutex_atomic.rs | 5 +- clippy_lints/src/needless_borrowed_ref.rs | 123 ++- clippy_lints/src/needless_continue.rs | 14 +- clippy_lints/src/needless_late_init.rs | 44 +- clippy_lints/src/needless_pass_by_value.rs | 17 +- clippy_lints/src/needless_question_mark.rs | 16 +- clippy_lints/src/neg_multiply.rs | 4 +- clippy_lints/src/new_without_default.rs | 7 +- clippy_lints/src/non_copy_const.rs | 5 +- clippy_lints/src/non_expressive_names.rs | 5 +- .../src/non_octal_unix_permissions.rs | 5 +- clippy_lints/src/nonstandard_macro_braces.rs | 79 +- clippy_lints/src/octal_escapes.rs | 2 +- .../operators/absurd_extreme_comparisons.rs | 5 +- .../src/operators/arithmetic_side_effects.rs | 120 ++- .../src/operators/assign_op_pattern.rs | 43 +- clippy_lints/src/operators/bit_mask.rs | 40 +- clippy_lints/src/operators/cmp_owned.rs | 10 +- clippy_lints/src/operators/duration_subsec.rs | 7 +- clippy_lints/src/operators/eq_op.rs | 2 +- .../src/operators/misrefactored_assign_op.rs | 12 +- clippy_lints/src/operators/mod.rs | 2 +- .../src/operators/needless_bitwise_bool.rs | 2 +- .../src/operators/numeric_arithmetic.rs | 9 +- clippy_lints/src/operators/ptr_eq.rs | 2 +- clippy_lints/src/operators/self_assignment.rs | 2 +- .../src/operators/verbose_bit_mask.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 30 +- clippy_lints/src/panic_in_result_fn.rs | 19 +- clippy_lints/src/partialeq_to_none.rs | 5 +- clippy_lints/src/pass_by_ref_or_value.rs | 4 +- clippy_lints/src/ptr.rs | 4 +- clippy_lints/src/ptr_offset_with_cast.rs | 4 +- clippy_lints/src/question_mark.rs | 39 +- clippy_lints/src/ranges.rs | 16 +- clippy_lints/src/read_zero_byte_vec.rs | 45 +- clippy_lints/src/redundant_pub_crate.rs | 2 +- clippy_lints/src/redundant_slicing.rs | 8 +- .../src/redundant_static_lifetimes.rs | 2 +- clippy_lints/src/regex.rs | 4 +- clippy_lints/src/returns.rs | 212 ++--- clippy_lints/src/same_name_method.rs | 4 +- .../src/semicolon_if_nothing_returned.rs | 2 +- .../src/slow_vector_initialization.rs | 16 +- clippy_lints/src/std_instead_of_core.rs | 22 + clippy_lints/src/strings.rs | 6 +- clippy_lints/src/strlen_on_c_strings.rs | 2 +- .../src/suspicious_operation_groupings.rs | 9 +- clippy_lints/src/suspicious_trait_impl.rs | 33 +- clippy_lints/src/swap.rs | 19 +- clippy_lints/src/swap_ptr_to_ref.rs | 2 +- clippy_lints/src/to_digit_is_some.rs | 4 +- clippy_lints/src/trait_bounds.rs | 3 +- .../src/transmute/crosspointer_transmute.rs | 10 +- .../src/transmute/transmute_float_to_int.rs | 4 +- .../src/transmute/transmute_int_to_bool.rs | 2 +- .../src/transmute/transmute_int_to_char.rs | 4 +- .../src/transmute/transmute_int_to_float.rs | 4 +- .../src/transmute/transmute_num_to_bytes.rs | 4 +- .../src/transmute/transmute_ptr_to_ref.rs | 18 +- .../src/transmute/transmute_ref_to_ref.rs | 2 +- .../src/transmute/transmute_undefined_repr.rs | 23 +- .../transmutes_expressible_as_ptr_casts.rs | 5 +- .../src/transmute/transmuting_null.rs | 41 +- .../transmute/unsound_collection_transmute.rs | 5 +- .../src/transmute/useless_transmute.rs | 2 +- clippy_lints/src/transmute/utils.rs | 5 +- clippy_lints/src/transmute/wrong_transmute.rs | 2 +- clippy_lints/src/types/borrowed_box.rs | 6 +- clippy_lints/src/types/box_collection.rs | 2 +- clippy_lints/src/types/mod.rs | 8 +- clippy_lints/src/types/rc_buffer.rs | 4 +- .../src/types/redundant_allocation.rs | 31 +- clippy_lints/src/types/vec_box.rs | 2 +- clippy_lints/src/uninit_vec.rs | 9 +- clippy_lints/src/unit_return_expecting_ord.rs | 6 +- clippy_lints/src/unit_types/unit_arg.rs | 7 +- clippy_lints/src/unit_types/unit_cmp.rs | 7 +- .../src/unnecessary_owned_empty_strings.rs | 4 +- clippy_lints/src/unnecessary_self_imports.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 13 +- clippy_lints/src/unsafe_removed_from_name.rs | 5 +- clippy_lints/src/unused_io_amount.rs | 7 +- clippy_lints/src/unused_rounding.rs | 4 +- clippy_lints/src/unwrap.rs | 7 +- clippy_lints/src/unwrap_in_result.rs | 74 +- clippy_lints/src/upper_case_acronyms.rs | 3 +- clippy_lints/src/use_self.rs | 2 +- clippy_lints/src/useless_conversion.rs | 14 +- clippy_lints/src/utils/author.rs | 2 +- clippy_lints/src/utils/conf.rs | 41 +- clippy_lints/src/utils/internal_lints.rs | 317 +++++-- .../internal_lints/metadata_collector.rs | 97 +- clippy_lints/src/wildcard_imports.rs | 2 +- clippy_lints/src/write.rs | 17 +- clippy_lints/src/zero_div_zero.rs | 3 +- clippy_lints/src/zero_sized_map_values.rs | 7 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/src/attrs.rs | 4 +- clippy_utils/src/diagnostics.rs | 5 +- clippy_utils/src/eager_or_lazy.rs | 12 +- clippy_utils/src/hir_utils.rs | 4 +- clippy_utils/src/lib.rs | 228 +++-- clippy_utils/src/macros.rs | 261 +++-- clippy_utils/src/msrvs.rs | 3 +- clippy_utils/src/paths.rs | 3 - clippy_utils/src/ptr.rs | 37 +- clippy_utils/src/qualify_min_const_fn.rs | 21 +- clippy_utils/src/source.rs | 18 +- clippy_utils/src/sugg.rs | 62 +- clippy_utils/src/ty.rs | 2 +- clippy_utils/src/usage.rs | 59 +- clippy_utils/src/visitors.rs | 164 ++-- lintcheck/Cargo.toml | 2 + lintcheck/README.md | 20 +- lintcheck/lintcheck_crates.toml | 8 + lintcheck/src/config.rs | 10 +- lintcheck/src/driver.rs | 67 ++ lintcheck/src/main.rs | 164 ++-- lintcheck/src/recursive.rs | 123 +++ rust-toolchain | 2 +- rustc_tools_util/Cargo.toml | 2 +- rustc_tools_util/README.md | 8 +- rustc_tools_util/src/lib.rs | 12 +- src/docs.rs | 5 + src/docs/arithmetic_side_effects.txt | 2 +- src/docs/box_default.txt | 23 + src/docs/disallowed_macros.txt | 36 + src/docs/implicit_saturating_add.txt | 20 + src/docs/manual_clamp.txt | 46 + src/docs/needless_borrowed_reference.txt | 18 +- src/docs/similar_names.txt | 4 + src/docs/uninlined_format_args.txt | 36 + src/driver.rs | 6 +- src/main.rs | 6 +- tests/compile-test.rs | 23 +- tests/integration.rs | 23 +- tests/lint_message_convention.rs | 2 +- tests/missing-test-files.rs | 2 +- .../duplicate_mod/fail/src/main.stderr | 2 +- .../feature_name/fail/src/main.stderr | 4 +- .../module_style/fail_mod/src/main.stderr | 2 +- .../fail_mod_remap/src/main.stderr | 2 +- .../module_style/fail_no_mod/src/main.stderr | 2 +- tests/ui-internal/auxiliary/paths.rs | 2 + .../check_clippy_version_attribute.stderr | 4 +- tests/ui-internal/if_chain_style.stderr | 2 +- tests/ui-internal/match_type_on_diag_item.rs | 39 - .../match_type_on_diag_item.stderr | 27 - tests/ui-internal/unnecessary_def_path.fixed | 62 ++ tests/ui-internal/unnecessary_def_path.rs | 62 ++ tests/ui-internal/unnecessary_def_path.stderr | 101 ++ .../conf_deprecated_key.rs | 2 + .../conf_deprecated_key.stderr | 2 +- .../disallowed_macros/auxiliary/macros.rs | 32 + tests/ui-toml/disallowed_macros/clippy.toml | 11 + .../disallowed_macros/disallowed_macros.rs | 39 + .../disallowed_macros.stderr | 84 ++ .../conf_nonstandard_macro_braces.fixed | 62 ++ .../conf_nonstandard_macro_braces.rs | 1 + .../conf_nonstandard_macro_braces.stderr | 81 +- .../toml_disallowed_methods/clippy.toml | 1 + .../conf_disallowed_methods.rs | 6 + .../conf_disallowed_methods.stderr | 24 +- .../toml_unknown_key/conf_unknown_key.stderr | 1 + tests/ui/arithmetic_side_effects.rs | 95 +- tests/ui/arithmetic_side_effects.stderr | 298 +++++- tests/ui/assign_ops2.rs | 2 + tests/ui/assign_ops2.stderr | 20 +- tests/ui/auxiliary/proc_macro_attr.rs | 2 +- tests/ui/bind_instead_of_map.fixed | 1 + tests/ui/bind_instead_of_map.rs | 1 + tests/ui/bind_instead_of_map.stderr | 6 +- tests/ui/borrow_box.rs | 5 +- tests/ui/borrow_box.stderr | 20 +- tests/ui/box_collection.rs | 4 +- tests/ui/box_default.rs | 31 + tests/ui/box_default.stderr | 59 ++ .../branches_sharing_code/shared_at_bottom.rs | 3 +- .../shared_at_bottom.stderr | 20 +- .../ui/branches_sharing_code/shared_at_top.rs | 5 +- .../shared_at_top.stderr | 28 +- .../shared_at_top_and_bottom.rs | 3 +- .../shared_at_top_and_bottom.stderr | 26 +- .../branches_sharing_code/valid_if_blocks.rs | 5 +- .../valid_if_blocks.stderr | 26 +- tests/ui/cast_abs_to_unsigned.fixed | 1 + tests/ui/cast_abs_to_unsigned.rs | 1 + tests/ui/cast_abs_to_unsigned.stderr | 34 +- tests/ui/collapsible_match.rs | 3 +- tests/ui/collapsible_match.stderr | 40 +- tests/ui/crashes/ice-4775.rs | 2 + tests/ui/crashes/ice-9445.rs | 3 + tests/ui/crashes/ice-9459.rs | 5 + tests/ui/crashes/regressions.rs | 2 +- tests/ui/default_trait_access.fixed | 4 +- tests/ui/default_trait_access.rs | 4 +- tests/ui/default_trait_access.stderr | 2 +- tests/ui/doc_link_with_quotes.rs | 7 +- tests/ui/doc_link_with_quotes.stderr | 6 +- tests/ui/drop_forget_copy.rs | 20 + tests/ui/drop_forget_copy.stderr | 38 +- tests/ui/eta.fixed | 31 +- tests/ui/eta.rs | 31 +- tests/ui/eta.stderr | 20 +- tests/ui/expect_fun_call.fixed | 9 +- tests/ui/expect_fun_call.rs | 9 +- tests/ui/expect_fun_call.stderr | 40 +- tests/ui/explicit_counter_loop.rs | 1 + tests/ui/explicit_counter_loop.stderr | 18 +- tests/ui/explicit_deref_methods.fixed | 16 +- tests/ui/explicit_deref_methods.rs | 16 +- tests/ui/explicit_write.fixed | 3 +- tests/ui/explicit_write.rs | 3 +- tests/ui/explicit_write.stderr | 26 +- tests/ui/fallible_impl_from.rs | 1 + tests/ui/fallible_impl_from.stderr | 16 +- tests/ui/floating_point_exp.fixed | 1 + tests/ui/floating_point_exp.rs | 1 + tests/ui/floating_point_exp.stderr | 10 +- tests/ui/floating_point_log.fixed | 2 +- tests/ui/floating_point_log.rs | 2 +- tests/ui/floating_point_logbase.fixed | 1 + tests/ui/floating_point_logbase.rs | 1 + tests/ui/floating_point_logbase.stderr | 10 +- tests/ui/floating_point_mul_add.fixed | 2 + tests/ui/floating_point_mul_add.rs | 2 + tests/ui/floating_point_mul_add.stderr | 30 +- tests/ui/floating_point_powf.fixed | 1 + tests/ui/floating_point_powf.rs | 1 + tests/ui/floating_point_powf.stderr | 62 +- tests/ui/floating_point_powi.fixed | 3 + tests/ui/floating_point_powi.rs | 3 + tests/ui/floating_point_powi.stderr | 24 +- tests/ui/for_loop_fixable.fixed | 2 +- tests/ui/for_loop_fixable.rs | 2 +- tests/ui/for_loops_over_fallibles.rs | 1 + tests/ui/for_loops_over_fallibles.stderr | 22 +- tests/ui/format.fixed | 6 +- tests/ui/format.rs | 6 +- tests/ui/format_args.fixed | 12 +- tests/ui/format_args.rs | 12 +- tests/ui/format_args.stderr | 46 +- tests/ui/format_args_unfixable.rs | 6 +- tests/ui/format_args_unfixable.stderr | 36 +- tests/ui/functions.rs | 4 +- tests/ui/identity_op.fixed | 4 +- tests/ui/identity_op.rs | 4 +- tests/ui/implicit_saturating_add.fixed | 106 +++ tests/ui/implicit_saturating_add.rs | 154 +++ tests/ui/implicit_saturating_add.stderr | 197 ++++ .../if_let_slice_binding.rs | 1 + .../if_let_slice_binding.stderr | 20 +- tests/ui/infinite_iter.rs | 2 + tests/ui/infinite_iter.stderr | 32 +- tests/ui/issue_2356.fixed | 1 + tests/ui/issue_2356.rs | 1 + tests/ui/issue_2356.stderr | 2 +- tests/ui/issue_4266.rs | 1 + tests/ui/issue_4266.stderr | 6 +- tests/ui/item_after_statement.rs | 1 + tests/ui/item_after_statement.stderr | 6 +- tests/ui/manual_assert.edition2018.fixed | 14 +- tests/ui/manual_assert.edition2018.stderr | 90 +- tests/ui/manual_assert.edition2021.fixed | 14 +- tests/ui/manual_assert.edition2021.stderr | 90 +- tests/ui/manual_assert.fixed | 45 - tests/ui/manual_assert.rs | 15 +- tests/ui/manual_bits.fixed | 3 +- tests/ui/manual_bits.rs | 3 +- tests/ui/manual_bits.stderr | 58 +- tests/ui/manual_clamp.rs | 304 ++++++ tests/ui/manual_clamp.stderr | 375 ++++++++ tests/ui/manual_find_fixable.fixed | 4 +- tests/ui/manual_find_fixable.rs | 4 +- tests/ui/manual_flatten.rs | 2 +- tests/ui/map_unwrap_or.rs | 2 +- tests/ui/match_ref_pats.fixed | 3 +- tests/ui/match_ref_pats.rs | 3 +- tests/ui/match_ref_pats.stderr | 10 +- tests/ui/match_result_ok.fixed | 3 +- tests/ui/match_result_ok.rs | 3 +- tests/ui/match_result_ok.stderr | 6 +- tests/ui/match_same_arms2.rs | 6 +- tests/ui/match_same_arms2.stderr | 46 +- tests/ui/match_single_binding.fixed | 4 +- tests/ui/match_single_binding.rs | 4 +- tests/ui/match_single_binding2.fixed | 2 +- tests/ui/match_single_binding2.rs | 2 +- tests/ui/min_max.rs | 1 + tests/ui/min_max.stderr | 26 +- tests/ui/min_rust_version_attr.rs | 12 + tests/ui/min_rust_version_attr.stderr | 36 +- tests/ui/mut_mut.rs | 4 +- tests/ui/needless_borrow.fixed | 33 +- tests/ui/needless_borrow.rs | 33 +- tests/ui/needless_borrowed_ref.fixed | 41 +- tests/ui/needless_borrowed_ref.rs | 41 +- tests/ui/needless_borrowed_ref.stderr | 121 ++- tests/ui/needless_collect_indirect.rs | 2 + tests/ui/needless_collect_indirect.stderr | 32 +- tests/ui/needless_continue.rs | 1 + tests/ui/needless_continue.stderr | 16 +- tests/ui/needless_for_each_fixable.fixed | 7 +- tests/ui/needless_for_each_fixable.rs | 7 +- tests/ui/needless_for_each_fixable.stderr | 16 +- tests/ui/needless_for_each_unfixable.rs | 2 +- tests/ui/needless_late_init.fixed | 5 +- tests/ui/needless_late_init.rs | 5 +- tests/ui/needless_late_init.stderr | 32 +- tests/ui/needless_pass_by_value.rs | 9 +- tests/ui/needless_pass_by_value.stderr | 52 +- tests/ui/needless_range_loop.rs | 1 + tests/ui/needless_range_loop.stderr | 28 +- tests/ui/needless_return.fixed | 37 + tests/ui/needless_return.rs | 37 + tests/ui/needless_return.stderr | 205 +++- tests/ui/never_loop.rs | 26 + tests/ui/never_loop.stderr | 15 +- tests/ui/no_effect.rs | 6 +- tests/ui/no_effect.stderr | 60 +- tests/ui/option_map_unit_fn_fixable.fixed | 3 +- tests/ui/option_map_unit_fn_fixable.rs | 3 +- tests/ui/option_map_unit_fn_fixable.stderr | 38 +- tests/ui/option_take_on_temporary.fixed | 15 - tests/ui/or_fun_call.fixed | 3 +- tests/ui/or_fun_call.rs | 3 +- tests/ui/or_fun_call.stderr | 52 +- tests/ui/panic_in_result_fn_assertions.rs | 2 +- .../ui/panic_in_result_fn_debug_assertions.rs | 2 +- tests/ui/patterns.fixed | 3 +- tests/ui/patterns.rs | 3 +- tests/ui/patterns.stderr | 6 +- tests/ui/print_literal.rs | 1 + tests/ui/print_literal.stderr | 24 +- tests/ui/ptr_offset_with_cast.fixed | 1 + tests/ui/ptr_offset_with_cast.rs | 1 + tests/ui/ptr_offset_with_cast.stderr | 4 +- tests/ui/question_mark.fixed | 9 + tests/ui/question_mark.rs | 9 + tests/ui/recursive_format_impl.rs | 5 +- tests/ui/recursive_format_impl.stderr | 20 +- tests/ui/redundant_clone.fixed | 4 +- tests/ui/redundant_clone.rs | 4 +- .../redundant_pattern_matching_ipaddr.fixed | 11 +- tests/ui/redundant_pattern_matching_ipaddr.rs | 11 +- .../redundant_pattern_matching_ipaddr.stderr | 36 +- .../redundant_pattern_matching_result.fixed | 11 +- tests/ui/redundant_pattern_matching_result.rs | 11 +- .../redundant_pattern_matching_result.stderr | 44 +- tests/ui/result_map_unit_fn_fixable.fixed | 2 +- tests/ui/result_map_unit_fn_fixable.rs | 2 +- tests/ui/reversed_empty_ranges_fixable.fixed | 1 + tests/ui/reversed_empty_ranges_fixable.rs | 1 + tests/ui/reversed_empty_ranges_fixable.stderr | 8 +- .../reversed_empty_ranges_loops_fixable.fixed | 1 + .../ui/reversed_empty_ranges_loops_fixable.rs | 1 + ...reversed_empty_ranges_loops_fixable.stderr | 12 +- .../reversed_empty_ranges_loops_unfixable.rs | 1 + ...versed_empty_ranges_loops_unfixable.stderr | 4 +- tests/ui/same_functions_in_if_condition.rs | 12 +- .../ui/same_functions_in_if_condition.stderr | 24 +- tests/ui/semicolon_if_nothing_returned.rs | 2 +- .../ui/should_impl_trait/method_list_1.stderr | 12 +- tests/ui/significant_drop_in_scrutinee.rs | 7 +- tests/ui/significant_drop_in_scrutinee.stderr | 52 +- tests/ui/single_match.rs | 1 + tests/ui/single_match.stderr | 32 +- tests/ui/single_match_else.rs | 4 +- tests/ui/single_match_else.stderr | 10 +- tests/ui/std_instead_of_core.rs | 6 + tests/ui/std_instead_of_core.stderr | 16 +- tests/ui/toplevel_ref_arg.fixed | 2 +- tests/ui/toplevel_ref_arg.rs | 2 +- tests/ui/trivially_copy_pass_by_ref.rs | 7 +- tests/ui/trivially_copy_pass_by_ref.stderr | 38 +- tests/ui/uninit_vec.rs | 6 + tests/ui/uninlined_format_args.fixed | 164 ++++ tests/ui/uninlined_format_args.rs | 169 ++++ tests/ui/uninlined_format_args.stderr | 894 ++++++++++++++++++ tests/ui/unit_arg.rs | 19 +- tests/ui/unit_arg.stderr | 20 +- tests/ui/unit_arg_empty_blocks.fixed | 3 +- tests/ui/unit_arg_empty_blocks.rs | 3 +- tests/ui/unit_arg_empty_blocks.stderr | 8 +- tests/ui/unnecessary_cast.fixed | 14 + tests/ui/unnecessary_cast.rs | 14 + tests/ui/unnecessary_cast.stderr | 20 +- tests/ui/unnecessary_clone.rs | 4 +- tests/ui/unnecessary_join.fixed | 2 +- tests/ui/unnecessary_join.rs | 2 +- tests/ui/unnecessary_lazy_eval.fixed | 21 + tests/ui/unnecessary_lazy_eval.rs | 21 + tests/ui/unnecessary_lazy_eval.stderr | 68 +- tests/ui/upper_case_acronyms.rs | 9 + tests/ui/upper_case_acronyms.stderr | 14 +- tests/ui/used_underscore_binding.rs | 3 +- tests/ui/used_underscore_binding.stderr | 12 +- tests/ui/useless_asref.fixed | 3 +- tests/ui/useless_asref.rs | 3 +- tests/ui/useless_asref.stderr | 24 +- tests/ui/vec.fixed | 2 +- tests/ui/vec.rs | 2 +- tests/ui/while_let_loop.rs | 1 + tests/ui/while_let_loop.stderr | 10 +- tests/ui/while_let_on_iterator.fixed | 10 +- tests/ui/while_let_on_iterator.rs | 10 +- tests/ui/while_let_on_iterator.stderr | 52 +- tests/ui/wildcard_enum_match_arm.fixed | 10 +- tests/ui/wildcard_enum_match_arm.rs | 10 +- tests/ui/wildcard_enum_match_arm.stderr | 14 +- tests/ui/write_literal.rs | 2 +- tests/versioncheck.rs | 6 +- 617 files changed, 10259 insertions(+), 4400 deletions(-) create mode 100644 clippy_lints/src/box_default.rs create mode 100644 clippy_lints/src/disallowed_macros.rs delete mode 100644 clippy_lints/src/doc_link_with_quotes.rs create mode 100644 clippy_lints/src/implicit_saturating_add.rs create mode 100644 clippy_lints/src/manual_clamp.rs create mode 100644 lintcheck/src/driver.rs create mode 100644 lintcheck/src/recursive.rs create mode 100644 src/docs/box_default.txt create mode 100644 src/docs/disallowed_macros.txt create mode 100644 src/docs/implicit_saturating_add.txt create mode 100644 src/docs/manual_clamp.txt create mode 100644 src/docs/uninlined_format_args.txt create mode 100644 tests/ui-internal/auxiliary/paths.rs delete mode 100644 tests/ui-internal/match_type_on_diag_item.rs delete mode 100644 tests/ui-internal/match_type_on_diag_item.stderr create mode 100644 tests/ui-internal/unnecessary_def_path.fixed create mode 100644 tests/ui-internal/unnecessary_def_path.rs create mode 100644 tests/ui-internal/unnecessary_def_path.stderr create mode 100644 tests/ui-toml/disallowed_macros/auxiliary/macros.rs create mode 100644 tests/ui-toml/disallowed_macros/clippy.toml create mode 100644 tests/ui-toml/disallowed_macros/disallowed_macros.rs create mode 100644 tests/ui-toml/disallowed_macros/disallowed_macros.stderr create mode 100644 tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed create mode 100644 tests/ui/box_default.rs create mode 100644 tests/ui/box_default.stderr create mode 100644 tests/ui/crashes/ice-9445.rs create mode 100644 tests/ui/crashes/ice-9459.rs create mode 100644 tests/ui/implicit_saturating_add.fixed create mode 100644 tests/ui/implicit_saturating_add.rs create mode 100644 tests/ui/implicit_saturating_add.stderr delete mode 100644 tests/ui/manual_assert.fixed create mode 100644 tests/ui/manual_clamp.rs create mode 100644 tests/ui/manual_clamp.stderr delete mode 100644 tests/ui/option_take_on_temporary.fixed create mode 100644 tests/ui/uninlined_format_args.fixed create mode 100644 tests/ui/uninlined_format_args.rs create mode 100644 tests/ui/uninlined_format_args.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 044cbff4b78e..42615179f705 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,161 @@ document. ## Unreleased / In Rust Nightly -[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master) +[3c7e7dbc...master](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...master) + +## Rust 1.64 + +Current stable, released 2022-09-22 + +[d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc) + +### New Lints + +* [`arithmetic_side_effects`] + [#9130](https://github.com/rust-lang/rust-clippy/pull/9130) +* [`invalid_utf8_in_unchecked`] + [#9105](https://github.com/rust-lang/rust-clippy/pull/9105) +* [`assertions_on_result_states`] + [#9225](https://github.com/rust-lang/rust-clippy/pull/9225) +* [`manual_find`] + [#8649](https://github.com/rust-lang/rust-clippy/pull/8649) +* [`manual_retain`] + [#8972](https://github.com/rust-lang/rust-clippy/pull/8972) +* [`default_instead_of_iter_empty`] + [#8989](https://github.com/rust-lang/rust-clippy/pull/8989) +* [`manual_rem_euclid`] + [#9031](https://github.com/rust-lang/rust-clippy/pull/9031) +* [`obfuscated_if_else`] + [#9148](https://github.com/rust-lang/rust-clippy/pull/9148) +* [`std_instead_of_core`] + [#9103](https://github.com/rust-lang/rust-clippy/pull/9103) +* [`std_instead_of_alloc`] + [#9103](https://github.com/rust-lang/rust-clippy/pull/9103) +* [`alloc_instead_of_core`] + [#9103](https://github.com/rust-lang/rust-clippy/pull/9103) +* [`explicit_auto_deref`] + [#8355](https://github.com/rust-lang/rust-clippy/pull/8355) + + +### Moves and Deprecations + +* Moved [`format_push_string`] to `restriction` (now allow-by-default) + [#9161](https://github.com/rust-lang/rust-clippy/pull/9161) + +### Enhancements + +* [`significant_drop_in_scrutinee`]: Now gives more context in the lint message + [#8981](https://github.com/rust-lang/rust-clippy/pull/8981) +* [`single_match`], [`single_match_else`]: Now catches more `Option` cases + [#8985](https://github.com/rust-lang/rust-clippy/pull/8985) +* [`unused_async`]: Now works for async methods + [#9025](https://github.com/rust-lang/rust-clippy/pull/9025) +* [`manual_filter_map`], [`manual_find_map`]: Now lint more expressions + [#8958](https://github.com/rust-lang/rust-clippy/pull/8958) +* [`question_mark`]: Now works for simple `if let` expressions + [#8356](https://github.com/rust-lang/rust-clippy/pull/8356) +* [`undocumented_unsafe_blocks`]: Now finds comments before the start of closures + [#9117](https://github.com/rust-lang/rust-clippy/pull/9117) +* [`trait_duplication_in_bounds`]: Now catches duplicate bounds in where clauses + [#8703](https://github.com/rust-lang/rust-clippy/pull/8703) +* [`shadow_reuse`], [`shadow_same`], [`shadow_unrelated`]: Now lint in const blocks + [#9124](https://github.com/rust-lang/rust-clippy/pull/9124) +* [`slow_vector_initialization`]: Now detects cases with `vec.capacity()` + [#8953](https://github.com/rust-lang/rust-clippy/pull/8953) +* [`unused_self`]: Now respects the `avoid-breaking-exported-api` config option + [#9199](https://github.com/rust-lang/rust-clippy/pull/9199) +* [`box_collection`]: Now supports all std collections + [#9170](https://github.com/rust-lang/rust-clippy/pull/9170) + +### False Positive Fixes + +* [`significant_drop_in_scrutinee`]: Now ignores calls to `IntoIterator::into_iter` + [#9140](https://github.com/rust-lang/rust-clippy/pull/9140) +* [`while_let_loop`]: Now ignores cases when the significant drop order would change + [#8981](https://github.com/rust-lang/rust-clippy/pull/8981) +* [`branches_sharing_code`]: Now ignores cases where moved variables have a significant + drop or variable modifications can affect the conditions + [#9138](https://github.com/rust-lang/rust-clippy/pull/9138) +* [`let_underscore_lock`]: Now ignores bindings that aren't locked + [#8990](https://github.com/rust-lang/rust-clippy/pull/8990) +* [`trivially_copy_pass_by_ref`]: Now tracks lifetimes and ignores cases where unsafe + pointers are used + [#8639](https://github.com/rust-lang/rust-clippy/pull/8639) +* [`let_unit_value`]: No longer ignores `#[allow]` attributes on the value + [#9082](https://github.com/rust-lang/rust-clippy/pull/9082) +* [`declare_interior_mutable_const`]: Now ignores the `thread_local!` macro + [#9015](https://github.com/rust-lang/rust-clippy/pull/9015) +* [`if_same_then_else`]: Now ignores branches with `todo!` and `unimplemented!` + [#9006](https://github.com/rust-lang/rust-clippy/pull/9006) +* [`enum_variant_names`]: Now ignores names with `_` prefixes + [#9032](https://github.com/rust-lang/rust-clippy/pull/9032) +* [`let_unit_value`]: Now ignores cases, where the unit type is manually specified + [#9056](https://github.com/rust-lang/rust-clippy/pull/9056) +* [`match_same_arms`]: Now ignores branches with `todo!` + [#9207](https://github.com/rust-lang/rust-clippy/pull/9207) +* [`assign_op_pattern`]: Ignores cases that break borrowing rules + [#9214](https://github.com/rust-lang/rust-clippy/pull/9214) +* [`extra_unused_lifetimes`]: No longer triggers in derive macros + [#9037](https://github.com/rust-lang/rust-clippy/pull/9037) +* [`mismatching_type_param_order`]: Now ignores complicated generic parameters + [#9146](https://github.com/rust-lang/rust-clippy/pull/9146) +* [`equatable_if_let`]: No longer lints in macros + [#9074](https://github.com/rust-lang/rust-clippy/pull/9074) +* [`new_without_default`]: Now ignores generics and lifetime parameters on `fn new` + [#9115](https://github.com/rust-lang/rust-clippy/pull/9115) +* [`needless_borrow`]: Now ignores cases that result in the execution of different traits + [#9096](https://github.com/rust-lang/rust-clippy/pull/9096) +* [`declare_interior_mutable_const`]: No longer triggers in thread-local initializers + [#9246](https://github.com/rust-lang/rust-clippy/pull/9246) + +### Suggestion Fixes/Improvements + +* [`type_repetition_in_bounds`]: The suggestion now works with maybe bounds + [#9132](https://github.com/rust-lang/rust-clippy/pull/9132) +* [`transmute_ptr_to_ref`]: Now suggests `pointer::cast` when possible + [#8939](https://github.com/rust-lang/rust-clippy/pull/8939) +* [`useless_format`]: Now suggests the correct variable name + [#9237](https://github.com/rust-lang/rust-clippy/pull/9237) +* [`or_fun_call`]: The lint emission will now only span over the `unwrap_or` call + [#9144](https://github.com/rust-lang/rust-clippy/pull/9144) +* [`neg_multiply`]: Now suggests adding parentheses around suggestion if needed + [#9026](https://github.com/rust-lang/rust-clippy/pull/9026) +* [`unnecessary_lazy_evaluations`]: Now suggest for `bool::then_some` for lazy evaluation + [#9099](https://github.com/rust-lang/rust-clippy/pull/9099) +* [`manual_flatten`]: Improved message for long code snippets + [#9156](https://github.com/rust-lang/rust-clippy/pull/9156) +* [`explicit_counter_loop`]: The suggestion is now machine applicable + [#9149](https://github.com/rust-lang/rust-clippy/pull/9149) +* [`needless_borrow`]: Now keeps parentheses around fields, when needed + [#9210](https://github.com/rust-lang/rust-clippy/pull/9210) +* [`while_let_on_iterator`]: The suggestion now works in `FnOnce` closures + [#9134](https://github.com/rust-lang/rust-clippy/pull/9134) + +### ICE Fixes + +* Fix ICEs related to `#![feature(generic_const_exprs)]` usage + [#9241](https://github.com/rust-lang/rust-clippy/pull/9241) +* Fix ICEs related to reference lints + [#9093](https://github.com/rust-lang/rust-clippy/pull/9093) +* [`question_mark`]: Fix ICE on zero field tuple structs + [#9244](https://github.com/rust-lang/rust-clippy/pull/9244) + +### Documentation Improvements + +* [`needless_option_take`]: Now includes a "What it does" and "Why is this bad?" section. + [#9022](https://github.com/rust-lang/rust-clippy/pull/9022) + +### Others + +* Using `--cap-lints=allow` and only `--force-warn`ing some will now work with Clippy's driver + [#9036](https://github.com/rust-lang/rust-clippy/pull/9036) +* Clippy now tries to read the `rust-version` from `Cargo.toml` to identify the + minimum supported rust version + [#8774](https://github.com/rust-lang/rust-clippy/pull/8774) ## Rust 1.63 -Current stable, released 2022-08-11 +Released 2022-08-11 [7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0) @@ -3609,6 +3759,7 @@ Released 2018-09-13 [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection +[`box_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_default [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code @@ -3669,6 +3820,7 @@ Released 2018-09-13 [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq +[`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods [`disallowed_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names @@ -3766,6 +3918,7 @@ Released 2018-09-13 [`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return +[`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping @@ -3834,6 +3987,7 @@ Released 2018-09-13 [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits +[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map @@ -4124,6 +4278,7 @@ Released 2018-09-13 [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init [`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec +[`uninlined_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args [`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 diff --git a/Cargo.toml b/Cargo.toml index b7e136ce9b29..60200a88b858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.65" +version = "0.1.66" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -23,12 +23,12 @@ path = "src/driver.rs" [dependencies] clippy_lints = { path = "clippy_lints" } semver = "1.0" -rustc_tools_util = { path = "rustc_tools_util" } +rustc_tools_util = "0.2.1" tempfile = { version = "3.2", optional = true } termize = "0.1" [dev-dependencies] -compiletest_rs = { version = "0.8", features = ["tmp"] } +compiletest_rs = { version = "0.9", features = ["tmp"] } tester = "0.9" regex = "1.5" toml = "0.5" @@ -55,7 +55,7 @@ tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" [build-dependencies] -rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } +rustc_tools_util = "0.2.1" [features] deny-warnings = ["clippy_lints/deny-warnings"] diff --git a/README.md b/README.md index 1193771ff736..a8a6b86d2a15 100644 --- a/README.md +++ b/README.md @@ -139,25 +139,6 @@ line. (You can swap `clippy::all` with the specific lint category you are target ## Configuration -Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable = -value` mapping e.g. - -```toml -avoid-breaking-exported-api = false -disallowed-names = ["toto", "tata", "titi"] -cognitive-complexity-threshold = 30 -``` - -See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which -lints can be configured and the meaning of the variables. - -Note that configuration changes will not apply for code that has already been compiled and cached under `./target/`; -for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure that -any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch. - -To deactivate the “for further information visit *lint-link*” message you can -define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. - ### Allowing/denying lints You can add options to your code to `allow`/`warn`/`deny` Clippy lints: @@ -205,6 +186,33 @@ the lint(s) you are interested in: cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... ``` +### Configure the behavior of some lints + +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable = +value` mapping e.g. + +```toml +avoid-breaking-exported-api = false +disallowed-names = ["toto", "tata", "titi"] +cognitive-complexity-threshold = 30 +``` + +See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which +lints can be configured and the meaning of the variables. + +> **Note** +> +> `clippy.toml` or `.clippy.toml` cannot be used to allow/deny lints. + +> **Note** +> +> Configuration changes will not apply for code that has already been compiled and cached under `./target/`; +> for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure +> that any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch. + +To deactivate the “for further information visit *lint-link*” message you can +define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. + ### Specifying the minimum supported Rust version Projects that intend to support old versions of Rust can disable lints pertaining to newer features by diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 357cf6fc43aa..256231441817 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -82,16 +82,16 @@ pub fn run(check: bool, verbose: bool) { fn output_err(err: CliError) { match err { CliError::CommandFailed(command, stderr) => { - eprintln!("error: A command failed! `{}`\nstderr: {}", command, stderr); + eprintln!("error: A command failed! `{command}`\nstderr: {stderr}"); }, CliError::IoError(err) => { - eprintln!("error: {}", err); + eprintln!("error: {err}"); }, CliError::RustfmtNotInstalled => { eprintln!("error: rustfmt nightly is not installed."); }, CliError::WalkDirError(err) => { - eprintln!("error: {}", err); + eprintln!("error: {err}"); }, CliError::IntellijSetupActive => { eprintln!( diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index a417d3dd8a4e..d3e036692040 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -41,7 +41,7 @@ fn main() { matches.contains_id("msrv"), ) { Ok(_) => update_lints::update(update_lints::UpdateMode::Change), - Err(e) => eprintln!("Unable to create lint: {}", e), + Err(e) => eprintln!("Unable to create lint: {e}"), } }, Some(("setup", sub_command)) => match sub_command.subcommand() { diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 02cb13a1d8af..9e15f1504fa9 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,5 +1,5 @@ use crate::clippy_project_root; -use indoc::{indoc, writedoc}; +use indoc::{formatdoc, writedoc}; use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; @@ -23,7 +23,7 @@ impl Context for io::Result { match self { Ok(t) => Ok(t), Err(e) => { - let message = format!("{}: {}", text.as_ref(), e); + let message = format!("{}: {e}", text.as_ref()); Err(io::Error::new(ErrorKind::Other, message)) }, } @@ -72,7 +72,7 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let lint_contents = get_lint_file_contents(lint, enable_msrv); let lint_path = format!("clippy_lints/src/{}.rs", lint.name); write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?; - println!("Generated lint file: `{}`", lint_path); + println!("Generated lint file: `{lint_path}`"); Ok(()) } @@ -86,7 +86,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> { path.push("src"); fs::create_dir(&path)?; - let header = format!("// compile-flags: --crate-name={}", lint_name); + let header = format!("// compile-flags: --crate-name={lint_name}"); write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?; Ok(()) @@ -106,7 +106,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> { let test_contents = get_test_file_contents(lint.name, None); write_file(lint.project_root.join(&test_path), test_contents)?; - println!("Generated test file: `{}`", test_path); + println!("Generated test file: `{test_path}`"); } Ok(()) @@ -186,38 +186,36 @@ pub(crate) fn get_stabilization_version() -> String { } fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { - let mut contents = format!( - indoc! {" - #![allow(unused)] - #![warn(clippy::{})] + let mut contents = formatdoc!( + r#" + #![allow(unused)] + #![warn(clippy::{lint_name})] - fn main() {{ - // test code goes here - }} - "}, - lint_name + fn main() {{ + // test code goes here + }} + "# ); if let Some(header) = header_commands { - contents = format!("{}\n{}", header, contents); + contents = format!("{header}\n{contents}"); } contents } fn get_manifest_contents(lint_name: &str, hint: &str) -> String { - format!( - indoc! {r#" - # {} + formatdoc!( + r#" + # {hint} - [package] - name = "{}" - version = "0.1.0" - publish = false + [package] + name = "{lint_name}" + version = "0.1.0" + publish = false - [workspace] - "#}, - hint, lint_name + [workspace] + "# ) } @@ -238,76 +236,61 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { let name_upper = lint_name.to_uppercase(); result.push_str(&if enable_msrv { - format!( - indoc! {" - use clippy_utils::msrvs; - {pass_import} - use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; - use rustc_semver::RustcVersion; - use rustc_session::{{declare_tool_lint, impl_lint_pass}}; + formatdoc!( + r#" + use clippy_utils::msrvs; + {pass_import} + use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; + use rustc_semver::RustcVersion; + use rustc_session::{{declare_tool_lint, impl_lint_pass}}; - "}, - pass_type = pass_type, - pass_import = pass_import, - context_import = context_import, + "# ) } else { - format!( - indoc! {" - {pass_import} - use rustc_lint::{{{context_import}, {pass_type}}}; - use rustc_session::{{declare_lint_pass, declare_tool_lint}}; + formatdoc!( + r#" + {pass_import} + use rustc_lint::{{{context_import}, {pass_type}}}; + use rustc_session::{{declare_lint_pass, declare_tool_lint}}; - "}, - pass_import = pass_import, - pass_type = pass_type, - context_import = context_import + "# ) }); let _ = write!(result, "{}", get_lint_declaration(&name_upper, category)); result.push_str(&if enable_msrv { - format!( - indoc! {" - pub struct {name_camel} {{ - msrv: Option, + formatdoc!( + r#" + pub struct {name_camel} {{ + msrv: Option, + }} + + impl {name_camel} {{ + #[must_use] + pub fn new(msrv: Option) -> Self {{ + Self {{ msrv }} }} + }} - impl {name_camel} {{ - #[must_use] - pub fn new(msrv: Option) -> Self {{ - Self {{ msrv }} - }} - }} + impl_lint_pass!({name_camel} => [{name_upper}]); - impl_lint_pass!({name_camel} => [{name_upper}]); + impl {pass_type}{pass_lifetimes} for {name_camel} {{ + extract_msrv_attr!({context_import}); + }} - impl {pass_type}{pass_lifetimes} for {name_camel} {{ - extract_msrv_attr!({context_import}); - }} - - // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. - // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`. - // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` - "}, - pass_type = pass_type, - pass_lifetimes = pass_lifetimes, - name_upper = name_upper, - name_camel = name_camel, - context_import = context_import, + // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. + // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`. + // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` + "# ) } else { - format!( - indoc! {" - declare_lint_pass!({name_camel} => [{name_upper}]); + formatdoc!( + r#" + declare_lint_pass!({name_camel} => [{name_upper}]); - impl {pass_type}{pass_lifetimes} for {name_camel} {{}} - "}, - pass_type = pass_type, - pass_lifetimes = pass_lifetimes, - name_upper = name_upper, - name_camel = name_camel, + impl {pass_type}{pass_lifetimes} for {name_camel} {{}} + "# ) }); @@ -315,8 +298,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { } fn get_lint_declaration(name_upper: &str, category: &str) -> String { - format!( - indoc! {r#" + formatdoc!( + r#" declare_clippy_lint! {{ /// ### What it does /// @@ -330,15 +313,13 @@ fn get_lint_declaration(name_upper: &str, category: &str) -> String { /// ```rust /// // example code which does not raise clippy warning /// ``` - #[clippy::version = "{version}"] + #[clippy::version = "{}"] pub {name_upper}, {category}, "default lint description" }} - "#}, - version = get_stabilization_version(), - name_upper = name_upper, - category = category, + "#, + get_stabilization_version(), ) } @@ -352,7 +333,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R _ => {}, } - let ty_dir = lint.project_root.join(format!("clippy_lints/src/{}", ty)); + let ty_dir = lint.project_root.join(format!("clippy_lints/src/{ty}")); assert!( ty_dir.exists() && ty_dir.is_dir(), "Directory `{}` does not exist!", @@ -412,10 +393,10 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R } write_file(lint_file_path.as_path(), lint_file_contents)?; - println!("Generated lint file: `clippy_lints/src/{}/{}.rs`", ty, lint.name); + println!("Generated lint file: `clippy_lints/src/{ty}/{}.rs`", lint.name); println!( - "Be sure to add a call to `{}::check` in `clippy_lints/src/{}/mod.rs`!", - lint.name, ty + "Be sure to add a call to `{}::check` in `clippy_lints/src/{ty}/mod.rs`!", + lint.name ); Ok(()) @@ -542,7 +523,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> .chain(std::iter::once(&*lint_name_upper)) .filter(|s| !s.is_empty()) { - let _ = write!(new_arr_content, "\n {},", ident); + let _ = write!(new_arr_content, "\n {ident},"); } new_arr_content.push('\n'); diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index f15f24da9467..2e0794f12fa1 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -10,8 +10,8 @@ use std::time::{Duration, SystemTime}; /// Panics if the python commands could not be spawned pub fn run(port: u16, lint: Option<&String>) -> ! { let mut url = Some(match lint { - None => format!("http://localhost:{}", port), - Some(lint) => format!("http://localhost:{}/#{}", port, lint), + None => format!("http://localhost:{port}"), + Some(lint) => format!("http://localhost:{port}/#{lint}"), }); loop { diff --git a/clippy_dev/src/setup/git_hook.rs b/clippy_dev/src/setup/git_hook.rs index 3fbb77d59235..1de5b1940bae 100644 --- a/clippy_dev/src/setup/git_hook.rs +++ b/clippy_dev/src/setup/git_hook.rs @@ -30,10 +30,7 @@ pub fn install_hook(force_override: bool) { println!("info: the hook can be removed with `cargo dev remove git-hook`"); println!("git hook successfully installed"); }, - Err(err) => eprintln!( - "error: unable to copy `{}` to `{}` ({})", - HOOK_SOURCE_FILE, HOOK_TARGET_FILE, err - ), + Err(err) => eprintln!("error: unable to copy `{HOOK_SOURCE_FILE}` to `{HOOK_TARGET_FILE}` ({err})"), } } @@ -77,7 +74,7 @@ pub fn remove_hook() { fn delete_git_hook_file(path: &Path) -> bool { if let Err(err) = fs::remove_file(path) { - eprintln!("error: unable to delete existing pre-commit git hook ({})", err); + eprintln!("error: unable to delete existing pre-commit git hook ({err})"); false } else { true diff --git a/clippy_dev/src/setup/intellij.rs b/clippy_dev/src/setup/intellij.rs index bf741e6d1217..b64e79733eb2 100644 --- a/clippy_dev/src/setup/intellij.rs +++ b/clippy_dev/src/setup/intellij.rs @@ -60,7 +60,7 @@ fn check_and_get_rustc_dir(rustc_path: &str) -> Result { path = absolute_path; }, Err(err) => { - eprintln!("error: unable to get the absolute path of rustc ({})", err); + eprintln!("error: unable to get the absolute path of rustc ({err})"); return Err(()); }, }; @@ -103,14 +103,14 @@ fn inject_deps_into_project(rustc_source_dir: &Path, project: &ClippyProjectInfo fn read_project_file(file_path: &str) -> Result { let path = Path::new(file_path); if !path.exists() { - eprintln!("error: unable to find the file `{}`", file_path); + eprintln!("error: unable to find the file `{file_path}`"); return Err(()); } match fs::read_to_string(path) { Ok(content) => Ok(content), Err(err) => { - eprintln!("error: the file `{}` could not be read ({})", file_path, err); + eprintln!("error: the file `{file_path}` could not be read ({err})"); Err(()) }, } @@ -124,10 +124,7 @@ fn inject_deps_into_manifest( ) -> std::io::Result<()> { // do not inject deps if we have already done so if cargo_toml.contains(RUSTC_PATH_SECTION) { - eprintln!( - "warn: dependencies are already setup inside {}, skipping file", - manifest_path - ); + eprintln!("warn: dependencies are already setup inside {manifest_path}, skipping file"); return Ok(()); } @@ -142,11 +139,7 @@ fn inject_deps_into_manifest( let new_deps = extern_crates.map(|dep| { // format the dependencies that are going to be put inside the Cargo.toml - format!( - "{dep} = {{ path = \"{source_path}/{dep}\" }}\n", - dep = dep, - source_path = rustc_source_dir.display() - ) + format!("{dep} = {{ path = \"{}/{dep}\" }}\n", rustc_source_dir.display()) }); // format a new [dependencies]-block with the new deps we need to inject @@ -163,11 +156,11 @@ fn inject_deps_into_manifest( // etc let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1); - // println!("{}", new_manifest); + // println!("{new_manifest}"); let mut file = File::create(manifest_path)?; file.write_all(new_manifest.as_bytes())?; - println!("info: successfully setup dependencies inside {}", manifest_path); + println!("info: successfully setup dependencies inside {manifest_path}"); Ok(()) } @@ -214,8 +207,8 @@ fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool { }, Err(err) => { eprintln!( - "error: unable to open file `{}` to remove rustc dependencies for {} ({})", - project.cargo_file, project.name, err + "error: unable to open file `{}` to remove rustc dependencies for {} ({err})", + project.cargo_file, project.name ); false }, diff --git a/clippy_dev/src/setup/vscode.rs b/clippy_dev/src/setup/vscode.rs index d59001b2c66a..dbcdc9b59e52 100644 --- a/clippy_dev/src/setup/vscode.rs +++ b/clippy_dev/src/setup/vscode.rs @@ -17,10 +17,7 @@ pub fn install_tasks(force_override: bool) { println!("info: the task file can be removed with `cargo dev remove vscode-tasks`"); println!("vscode tasks successfully installed"); }, - Err(err) => eprintln!( - "error: unable to copy `{}` to `{}` ({})", - TASK_SOURCE_FILE, TASK_TARGET_FILE, err - ), + Err(err) => eprintln!("error: unable to copy `{TASK_SOURCE_FILE}` to `{TASK_TARGET_FILE}` ({err})"), } } @@ -44,23 +41,17 @@ fn check_install_precondition(force_override: bool) -> bool { return delete_vs_task_file(path); } - eprintln!( - "error: there is already a `task.json` file inside the `{}` directory", - VSCODE_DIR - ); + eprintln!("error: there is already a `task.json` file inside the `{VSCODE_DIR}` directory"); println!("info: use the `--force-override` flag to override the existing `task.json` file"); return false; } } else { match fs::create_dir(vs_dir_path) { Ok(_) => { - println!("info: created `{}` directory for clippy", VSCODE_DIR); + println!("info: created `{VSCODE_DIR}` directory for clippy"); }, Err(err) => { - eprintln!( - "error: the task target directory `{}` could not be created ({})", - VSCODE_DIR, err - ); + eprintln!("error: the task target directory `{VSCODE_DIR}` could not be created ({err})"); }, } } @@ -82,7 +73,7 @@ pub fn remove_tasks() { fn delete_vs_task_file(path: &Path) -> bool { if let Err(err) = fs::remove_file(path) { - eprintln!("error: unable to delete the existing `tasks.json` file ({})", err); + eprintln!("error: unable to delete the existing `tasks.json` file ({err})"); return false; } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index b95061bf81a2..0eb443167ecf 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -45,9 +45,8 @@ fn generate_lint_files( renamed_lints: &[RenamedLint], ) { 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 mut usable_lints = Lint::usable_lints(lints); + usable_lints.sort_by_key(|lint| lint.name.clone()); replace_region_in_file( update_mode, @@ -86,7 +85,7 @@ fn generate_lint_files( ) .sorted() { - writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap(); + writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); } }, ); @@ -99,7 +98,7 @@ fn generate_lint_files( "// 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(); + writeln!(res, "mod {lint_mod};").unwrap(); } }, ); @@ -129,7 +128,7 @@ fn generate_lint_files( for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { let content = gen_lint_group_list(&lint_group, lints.iter()); process_file( - &format!("clippy_lints/src/lib.register_{}.rs", lint_group), + &format!("clippy_lints/src/lib.register_{lint_group}.rs"), update_mode, &content, ); @@ -190,9 +189,9 @@ fn print_lint_names(header: &str, lints: &BTreeSet) -> bool { if lints.is_empty() { return false; } - println!("{}", header); + println!("{header}"); for lint in lints.iter().sorted() { - println!(" {}", lint); + println!(" {lint}"); } println!(); true @@ -205,16 +204,16 @@ pub fn print_lints() { let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); for (lint_group, mut lints) in grouped_by_lint_group { - println!("\n## {}", lint_group); + println!("\n## {lint_group}"); lints.sort_by_key(|l| l.name.clone()); for lint in lints { - println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc); + println!("* [{}]({DOCS_LINK}#{}) ({})", lint.name, lint.name, lint.desc); } } - println!("there are {} lints", usable_lint_count); + println!("there are {usable_lint_count} lints"); } /// Runs the `rename_lint` command. @@ -235,10 +234,10 @@ pub fn print_lints() { #[allow(clippy::too_many_lines)] pub fn rename(old_name: &str, new_name: &str, uplift: bool) { if let Some((prefix, _)) = old_name.split_once("::") { - panic!("`{}` should not contain the `{}` prefix", old_name, prefix); + panic!("`{old_name}` should not contain the `{prefix}` prefix"); } if let Some((prefix, _)) = new_name.split_once("::") { - panic!("`{}` should not contain the `{}` prefix", new_name, prefix); + panic!("`{new_name}` should not contain the `{prefix}` prefix"); } let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); @@ -251,14 +250,14 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { found_new_name = true; } } - let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name)); + let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`")); let lint = RenamedLint { - old_name: format!("clippy::{}", old_name), + old_name: format!("clippy::{old_name}"), new_name: if uplift { new_name.into() } else { - format!("clippy::{}", new_name) + format!("clippy::{new_name}") }, }; @@ -266,13 +265,11 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { // case. assert!( !renamed_lints.iter().any(|l| lint.old_name == l.old_name), - "`{}` has already been renamed", - old_name + "`{old_name}` has already been renamed" ); assert!( !deprecated_lints.iter().any(|l| lint.old_name == l.name), - "`{}` has already been deprecated", - old_name + "`{old_name}` has already been deprecated" ); // Update all lint level attributes. (`clippy::lint_name`) @@ -309,14 +306,12 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { if uplift { write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); println!( - "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.", - old_name + "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." ); } else if found_new_name { write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); println!( - "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.", - new_name + "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." ); } else { // Rename the lint struct and source files sharing a name with the lint. @@ -327,16 +322,16 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. if try_rename_file( - Path::new(&format!("tests/ui/{}.rs", old_name)), - Path::new(&format!("tests/ui/{}.rs", new_name)), + Path::new(&format!("tests/ui/{old_name}.rs")), + Path::new(&format!("tests/ui/{new_name}.rs")), ) { try_rename_file( - Path::new(&format!("tests/ui/{}.stderr", old_name)), - Path::new(&format!("tests/ui/{}.stderr", new_name)), + Path::new(&format!("tests/ui/{old_name}.stderr")), + Path::new(&format!("tests/ui/{new_name}.stderr")), ); try_rename_file( - Path::new(&format!("tests/ui/{}.fixed", old_name)), - Path::new(&format!("tests/ui/{}.fixed", new_name)), + Path::new(&format!("tests/ui/{old_name}.fixed")), + Path::new(&format!("tests/ui/{new_name}.fixed")), ); } @@ -344,8 +339,8 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { let replacements; let replacements = if lint.module == old_name && try_rename_file( - Path::new(&format!("clippy_lints/src/{}.rs", old_name)), - Path::new(&format!("clippy_lints/src/{}.rs", new_name)), + Path::new(&format!("clippy_lints/src/{old_name}.rs")), + Path::new(&format!("clippy_lints/src/{new_name}.rs")), ) { // Edit the module name in the lint list. Note there could be multiple lints. for lint in lints.iter_mut().filter(|l| l.module == old_name) { @@ -356,14 +351,14 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { } else if !lint.module.contains("::") // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` && try_rename_file( - Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)), - Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)), + Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)), + Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)), ) { // Edit the module name in the lint list. Note there could be multiple lints, or none. - let renamed_mod = format!("{}::{}", lint.module, old_name); + let renamed_mod = format!("{}::{old_name}", lint.module); for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { - lint.module = format!("{}::{}", lint.module, new_name); + lint.module = format!("{}::{new_name}", lint.module); } replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; replacements.as_slice() @@ -379,7 +374,7 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { } generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("{} has been successfully renamed", old_name); + println!("{old_name} has been successfully renamed"); } println!("note: `cargo uitest` still needs to be run to update the test results"); @@ -408,7 +403,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) { }); generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{}` has successfully been deprecated", name); + println!("info: `{name}` has successfully been deprecated"); if reason == DEFAULT_DEPRECATION_REASON { println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`"); @@ -421,7 +416,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) { let name_upper = name.to_uppercase(); let (mut lints, deprecated_lints, renamed_lints) = gather_all(); - let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{}`", name); return; }; + let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{name}`"); return; }; let mod_path = { let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); @@ -450,7 +445,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io } fn remove_test_assets(name: &str) { - let test_file_stem = format!("tests/ui/{}", name); + let test_file_stem = format!("tests/ui/{name}"); let path = Path::new(&test_file_stem); // Some lints have their own directories, delete them @@ -512,8 +507,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); eprintln!( - "warn: you will have to manually remove any code related to `{}` from `{}`", - name, + "warn: you will have to manually remove any code related to `{name}` from `{}`", path.display() ); @@ -528,7 +522,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io content.replace_range(lint.declaration_range.clone(), ""); // Remove the module declaration (mod xyz;) - let mod_decl = format!("\nmod {};", name); + let mod_decl = format!("\nmod {name};"); content = content.replacen(&mod_decl, "", 1); remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); @@ -621,13 +615,13 @@ fn round_to_fifty(count: usize) -> usize { fn process_file(path: impl AsRef, update_mode: UpdateMode, content: &str) { if update_mode == UpdateMode::Check { let old_content = - fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.as_ref().display(), e)); + fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {e}", path.as_ref().display())); if content != old_content { exit_with_failure(); } } else { fs::write(&path, content.as_bytes()) - .unwrap_or_else(|e| panic!("Cannot write to {}: {}", path.as_ref().display(), e)); + .unwrap_or_else(|e| panic!("Cannot write to {}: {e}", path.as_ref().display())); } } @@ -731,11 +725,10 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator( if !is_public { output.push_str(" #[cfg(feature = \"internal\")]\n"); } - let _ = writeln!(output, " {}::{},", module_name, lint_name); + let _ = writeln!(output, " {module_name}::{lint_name},"); } output.push_str("])\n"); @@ -841,7 +834,7 @@ fn gather_all() -> (Vec, Vec, Vec) { for (rel_path, file) in clippy_lints_src_files() { let path = file.path(); let contents = - fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); + fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); let module = rel_path .components() .map(|c| c.as_os_str().to_str().unwrap()) @@ -1050,7 +1043,7 @@ fn remove_line_splices(s: &str) -> String { .trim_matches('#') .strip_prefix('"') .and_then(|s| s.strip_suffix('"')) - .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s)); + .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, ch| { if ch.is_ok() { @@ -1076,10 +1069,10 @@ fn replace_region_in_file( end: &str, 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 contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); 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()), + Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()), }; match update_mode { @@ -1087,7 +1080,7 @@ fn replace_region_in_file( UpdateMode::Check => (), UpdateMode::Change => { if let Err(e) = fs::write(path, new_contents.as_bytes()) { - panic!("Cannot write to `{}`: {}", path.display(), e); + panic!("Cannot write to `{}`: {e}", path.display()); } }, } @@ -1135,7 +1128,7 @@ fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { #[allow(clippy::needless_pass_by_value)] fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { - panic!("failed to {} file `{}`: {}", action, name.display(), error) + panic!("failed to {action} file `{}`: {error}", name.display()) } fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 738562ef8559..6fbd6401ef3e 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.65" +version = "0.1.66" 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/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 159f3b0cd014..724490fb4959 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -92,7 +92,7 @@ impl ApproxConstant { cx, APPROX_CONSTANT, e.span, - &format!("approximate value of `{}::consts::{}` found", module, &name), + &format!("approximate value of `{module}::consts::{}` found", &name), None, "consider using the constant directly", ); @@ -126,7 +126,7 @@ fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool { // The value is a truncated constant true } else { - let round_const = format!("{:.*}", value.len() - 2, constant); + let round_const = format!("{constant:.*}", value.len() - 2); value == round_const } } diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs index f419781dbc82..9717aa9e981f 100644 --- a/clippy_lints/src/asm_syntax.rs +++ b/clippy_lints/src/asm_syntax.rs @@ -44,7 +44,7 @@ fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr cx, lint, expr.span, - &format!("{} x86 assembly syntax used", style), + &format!("{style} x86 assembly syntax used"), None, &format!("use {} x86 assembly syntax", !style), ); @@ -64,6 +64,7 @@ declare_clippy_lint! { /// /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); @@ -72,6 +73,7 @@ declare_clippy_lint! { /// Use instead: /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); @@ -103,6 +105,7 @@ declare_clippy_lint! { /// /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); @@ -111,6 +114,7 @@ declare_clippy_lint! { /// Use instead: /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 2705ffffdcbf..a36df55d0bda 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -60,9 +60,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { cx, ASSERTIONS_ON_CONSTANTS, macro_call.span, - &format!("`assert!(false{})` should probably be replaced", assert_arg), + &format!("`assert!(false{assert_arg})` should probably be replaced"), None, - &format!("use `panic!({})` or `unreachable!({0})`", panic_arg), + &format!("use `panic!({panic_arg})` or `unreachable!({panic_arg})`"), ); } } diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index 656dc5feeb57..f6d6c23bb6ed 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -69,9 +69,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { "called `assert!` with `Result::is_ok`", "replace with", format!( - "{}.unwrap(){}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0, - semicolon + "{}.unwrap(){semicolon}", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 ), app, ); @@ -84,9 +83,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { "called `assert!` with `Result::is_err`", "replace with", format!( - "{}.unwrap_err(){}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0, - semicolon + "{}.unwrap_err(){semicolon}", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 ), app, ); diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 732dc2b43309..5f45c69d7f98 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -541,10 +541,7 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut cx, INLINE_ALWAYS, attr.span, - &format!( - "you have declared `#[inline(always)]` on `{}`. This is usually a bad idea", - name - ), + &format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"), ); } } @@ -720,7 +717,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { let mut unix_suggested = false; for (os, span) in mismatched { - let sugg = format!("target_os = \"{}\"", os); + let sugg = format!("target_os = \"{os}\""); diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); if !unix_suggested && is_unix(os) { diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 1761360fb281..34717811866d 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,14 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::GeneratorInteriorTypeCause; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; +use rustc_span::{sym, Span}; -use crate::utils::conf::DisallowedType; +use crate::utils::conf::DisallowedPath; declare_clippy_lint! { /// ### What it does @@ -171,12 +172,12 @@ impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, #[derive(Debug)] pub struct AwaitHolding { - conf_invalid_types: Vec, - def_ids: FxHashMap, + conf_invalid_types: Vec, + def_ids: FxHashMap, } impl AwaitHolding { - pub(crate) fn new(conf_invalid_types: Vec) -> Self { + pub(crate) fn new(conf_invalid_types: Vec) -> Self { Self { conf_invalid_types, def_ids: FxHashMap::default(), @@ -187,11 +188,8 @@ impl AwaitHolding { impl LateLintPass<'_> for AwaitHolding { fn check_crate(&mut self, cx: &LateContext<'_>) { for conf in &self.conf_invalid_types { - let path = match conf { - DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path, - }; - let segs: Vec<_> = path.split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) { + let segs: Vec<_> = conf.path().split("::").collect(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { self.def_ids.insert(id, conf.clone()); } } @@ -256,29 +254,27 @@ impl AwaitHolding { } } -fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) { - let (type_name, reason) = match disallowed { - DisallowedType::Simple(path) => (path, &None), - DisallowedType::WithReason { path, reason } => (path, reason), - }; - +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPath) { span_lint_and_then( cx, AWAIT_HOLDING_INVALID_TYPE, span, - &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",), + &format!( + "`{}` may not be held across an `await` point per `clippy.toml`", + disallowed.path() + ), |diag| { - if let Some(reason) = reason { - diag.note(reason.clone()); + if let Some(reason) = disallowed.reason() { + diag.note(reason); } }, ); } fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { - match_def_path(cx, def_id, &paths::MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) + cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id) + || cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id) + || cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id) || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index d9e2c9c8578f..9c0532474024 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -3,10 +3,11 @@ use clippy_utils::get_parent_expr; use clippy_utils::higher; use clippy_utils::source::snippet_block_with_applicability; use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::{for_each_expr, Descend}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{BlockCheckMode, Closure, Expr, ExprKind}; +use rustc_hir::{BlockCheckMode, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -44,39 +45,6 @@ declare_clippy_lint! { declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]); -struct ExVisitor<'a, 'tcx> { - found_block: Option<&'tcx Expr<'tcx>>, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let ExprKind::Closure(&Closure { body, .. }) = expr.kind { - // do not lint if the closure is called using an iterator (see #1141) - if_chain! { - if let Some(parent) = get_parent_expr(self.cx, expr); - if let ExprKind::MethodCall(_, self_arg, ..) = &parent.kind; - let caller = self.cx.typeck_results().expr_ty(self_arg); - if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator); - if implements_trait(self.cx, caller, iter_id, &[]); - then { - return; - } - } - - let body = self.cx.tcx.hir().body(body); - let ex = &body.value; - if let ExprKind::Block(block, _) = ex.kind { - if !body.value.span.from_expansion() && !block.stmts.is_empty() { - self.found_block = Some(ex); - return; - } - } - } - walk_expr(self, expr); - } -} - const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ instead, move the block or closure higher and bind it with a `let`"; @@ -145,11 +113,31 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions { } } } else { - let mut visitor = ExVisitor { found_block: None, cx }; - walk_expr(&mut visitor, cond); - if let Some(block) = visitor.found_block { - span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE); - } + let _: Option = for_each_expr(cond, |e| { + if let ExprKind::Closure(closure) = e.kind { + // do not lint if the closure is called using an iterator (see #1141) + if_chain! { + if let Some(parent) = get_parent_expr(cx, e); + if let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind; + let caller = cx.typeck_results().expr_ty(self_arg); + if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + if implements_trait(cx, caller, iter_id, &[]); + then { + return ControlFlow::Continue(Descend::No); + } + } + + let body = cx.tcx.hir().body(closure.body); + let ex = &body.value; + if let ExprKind::Block(block, _) = ex.kind { + if !body.value.span.from_expansion() && !block.stmts.is_empty() { + span_lint(cx, BLOCKS_IN_IF_CONDITIONS, ex.span, COMPLEX_BLOCK_MESSAGE); + return ControlFlow::Continue(Descend::No); + } + } + } + ControlFlow::Continue(Descend::Yes) + }); } } } diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 95abe8aa59fb..4bd55c1429c3 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -98,9 +98,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { cx, BOOL_ASSERT_COMPARISON, macro_call.span, - &format!("used `{}!` with a literal bool", macro_name), + &format!("used `{macro_name}!` with a literal bool"), "replace it with", - format!("{}!(..)", non_eq_mac), + format!("{non_eq_mac}!(..)"), Applicability::MaybeIncorrect, ); } diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 51e98cda8451..001d74c26054 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -3,7 +3,7 @@ use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, sugg::Sugg}; +use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, is_integer_literal, sugg::Sugg}; use rustc_errors::Applicability; declare_clippy_lint! { @@ -56,13 +56,9 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx && let Some(then_lit) = int_literal(then) && let Some(else_lit) = int_literal(else_) { - let inverted = if - check_int_literal_equals_val(then_lit, 1) - && check_int_literal_equals_val(else_lit, 0) { + let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) { false - } else if - check_int_literal_equals_val(then_lit, 0) - && check_int_literal_equals_val(else_lit, 1) { + } else if is_integer_literal(then_lit, 0) && is_integer_literal(else_lit, 1) { true } else { // Expression isn't boolean, exit @@ -123,14 +119,3 @@ fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hi None } } - -fn check_int_literal_equals_val<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>, expected_value: u128) -> bool { - if let ExprKind::Lit(lit) = &expr.kind - && let LitKind::Int(val, _) = lit.node - && val == expected_value - { - true - } else { - false - } -} diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 03d262d5a59c..2a15cbc7a3c3 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -263,9 +263,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } .and_then(|op| { Some(format!( - "{}{}{}", + "{}{op}{}", snippet_opt(cx, lhs.span)?, - op, snippet_opt(cx, rhs.span)? )) }) @@ -285,7 +284,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let path: &str = path.ident.name.as_str(); a == path }) - .and_then(|(_, neg_method)| Some(format!("{}.{}()", snippet_opt(cx, receiver.span)?, neg_method))) + .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", snippet_opt(cx, receiver.span)?))) }, _ => None, } diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs new file mode 100644 index 000000000000..792183ac4081 --- /dev/null +++ b/clippy_lints/src/box_default.rs @@ -0,0 +1,61 @@ +use clippy_utils::{diagnostics::span_lint_and_help, is_default_equivalent, path_def_id}; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// checks for `Box::new(T::default())`, which is better written as + /// `Box::::default()`. + /// + /// ### Why is this bad? + /// First, it's more complex, involving two calls instead of one. + /// Second, `Box::default()` can be faster + /// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). + /// + /// ### Known problems + /// The lint may miss some cases (e.g. Box::new(String::from(""))). + /// On the other hand, it will trigger on cases where the `default` + /// code comes from a macro that does something different based on + /// e.g. target operating system. + /// + /// ### Example + /// ```rust + /// let x: Box = Box::new(Default::default()); + /// ``` + /// Use instead: + /// ```rust + /// let x: Box = Box::default(); + /// ``` + #[clippy::version = "1.65.0"] + pub BOX_DEFAULT, + perf, + "Using Box::new(T::default()) instead of Box::default()" +} + +declare_lint_pass!(BoxDefault => [BOX_DEFAULT]); + +impl LateLintPass<'_> for BoxDefault { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Call(box_new, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind + && let ExprKind::Call(..) = arg.kind + && !in_external_macro(cx.sess(), expr.span) + && expr.span.eq_ctxt(arg.span) + && seg.ident.name == sym::new + && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box() + && is_default_equivalent(cx, arg) + { + span_lint_and_help( + cx, + BOX_DEFAULT, + expr.span, + "`Box::new(_)` of default value", + None, + "use `Box::default()` instead", + ); + } + } +} diff --git a/clippy_lints/src/cargo/common_metadata.rs b/clippy_lints/src/cargo/common_metadata.rs index e0442dda479d..805121bcced3 100644 --- a/clippy_lints/src/cargo/common_metadata.rs +++ b/clippy_lints/src/cargo/common_metadata.rs @@ -40,7 +40,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b } fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, field: &str) { - let message = format!("package `{}` is missing `{}` metadata", package.name, field); + let message = format!("package `{}` is missing `{field}` metadata", package.name); span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message); } diff --git a/clippy_lints/src/cargo/feature_name.rs b/clippy_lints/src/cargo/feature_name.rs index 79a469a4258b..37c169dbd95e 100644 --- a/clippy_lints/src/cargo/feature_name.rs +++ b/clippy_lints/src/cargo/feature_name.rs @@ -57,10 +57,8 @@ fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) { }, DUMMY_SP, &format!( - "the \"{}\" {} in the feature name \"{}\" is {}", - substring, + "the \"{substring}\" {} in the feature name \"{feature}\" is {}", if is_prefix { "prefix" } else { "suffix" }, - feature, if is_negative { "negative" } else { "redundant" } ), None, diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index 9f45db86a091..3a872e54c9a2 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -196,7 +196,7 @@ impl LateLintPass<'_> for Cargo { }, Err(e) => { for lint in NO_DEPS_LINTS { - span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {}", e)); + span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {e}")); } }, } @@ -212,7 +212,7 @@ impl LateLintPass<'_> for Cargo { }, Err(e) => { for lint in WITH_DEPS_LINTS { - span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {}", e)); + span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {e}")); } }, } diff --git a/clippy_lints/src/cargo/multiple_crate_versions.rs b/clippy_lints/src/cargo/multiple_crate_versions.rs index 76fd0819a39a..f9b17d45e9fb 100644 --- a/clippy_lints/src/cargo/multiple_crate_versions.rs +++ b/clippy_lints/src/cargo/multiple_crate_versions.rs @@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) { cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, - &format!("multiple versions for dependency `{}`: {}", name, versions), + &format!("multiple versions for dependency `{name}`: {versions}"), ); } } diff --git a/clippy_lints/src/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs index 6e1f8cd64f07..294d22d34de9 100644 --- a/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/clippy_lints/src/casts/borrow_as_ptr.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( expr.span, "borrow as raw pointer", "try", - format!("{}::ptr::{}!({})", core_or_std, macro_name, snip), + format!("{core_or_std}::ptr::{macro_name}!({snip})"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 938458e30cad..13c403234dad 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -41,15 +41,9 @@ pub(super) fn check( ); let message = if cast_from.is_bool() { - format!( - "casting `{0:}` to `{1:}` is more cleanly stated with `{1:}::from(_)`", - cast_from, cast_to - ) + format!("casting `{cast_from:}` to `{cast_to:}` is more cleanly stated with `{cast_to:}::from(_)`") } else { - format!( - "casting `{}` to `{}` may become silently lossy if you later change the type", - cast_from, cast_to - ) + format!("casting `{cast_from}` to `{cast_to}` may become silently lossy if you later change the type") }; span_lint_and_sugg( @@ -58,7 +52,7 @@ pub(super) fn check( expr.span, &message, "try", - format!("{}::from({})", cast_to, sugg), + format!("{cast_to}::from({sugg})"), applicability, ); } diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 406547a4454e..88deb4565eb2 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -103,10 +103,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, return; } - format!( - "casting `{}` to `{}` may truncate the value{}", - cast_from, cast_to, suffix, - ) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",) }, (ty::Adt(def, _), true) if def.is_enum() => { @@ -142,20 +139,17 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, CAST_ENUM_TRUNCATION, expr.span, &format!( - "casting `{}::{}` to `{}` will truncate the value{}", - cast_from, variant.name, cast_to, suffix, + "casting `{cast_from}::{}` to `{cast_to}` will truncate the value{suffix}", + variant.name, ), ); return; } - format!( - "casting `{}` to `{}` may truncate the value{}", - cast_from, cast_to, suffix, - ) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",) }, (ty::Float(_), true) => { - format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value") }, (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => { diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index 2c5c1d7cb465..28ecdea7ea06 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -35,10 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca cx, CAST_POSSIBLE_WRAP, expr.span, - &format!( - "casting `{}` to `{}` may wrap around the value{}", - cast_from, cast_to, suffix, - ), + &format!("casting `{cast_from}` to `{cast_to}` may wrap around the value{suffix}",), ); } } diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index da7b12f67266..97054a0d1015 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -49,9 +49,7 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f CAST_PTR_ALIGNMENT, expr.span, &format!( - "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", - cast_from, - cast_to, + "casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)", from_layout.align.abi.bytes(), to_layout.align.abi.bytes(), ), diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 5b59350be042..a20a97d4e56d 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -14,10 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, c cx, CAST_SIGN_LOSS, expr.span, - &format!( - "casting `{}` to `{}` may lose the sign of the value", - cast_from, cast_to - ), + &format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"), ); } } diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 027c660ce3b2..d31d10d22b92 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -35,8 +35,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Optio CAST_SLICE_DIFFERENT_SIZES, expr.span, &format!( - "casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count", - start_ty.ty, from_size, end_ty.ty, to_size, + "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count", + start_ty.ty, end_ty.ty, ), |diag| { let ptr_snippet = source::snippet(cx, left_cast.span, ".."); diff --git a/clippy_lints/src/casts/char_lit_as_u8.rs b/clippy_lints/src/casts/char_lit_as_u8.rs index 7cc406018dbe..82e07c98a7e0 100644 --- a/clippy_lints/src/casts/char_lit_as_u8.rs +++ b/clippy_lints/src/casts/char_lit_as_u8.rs @@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { diag.span_suggestion( expr.span, "use a byte literal instead", - format!("b{}", snippet), + format!("b{snippet}"), applicability, ); } diff --git a/clippy_lints/src/casts/fn_to_numeric_cast.rs b/clippy_lints/src/casts/fn_to_numeric_cast.rs index 35350d8a25b8..a26bfab4e7c1 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -25,9 +25,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST, expr.span, - &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), + &format!("casting function pointer `{from_snippet}` to `{cast_to}`"), "try", - format!("{} as usize", from_snippet), + format!("{from_snippet} as usize"), applicability, ); } diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs index 03621887a34a..75654129408e 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -23,9 +23,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST_ANY, expr.span, - &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), + &format!("casting function pointer `{from_snippet}` to `{cast_to}`"), "did you mean to invoke the function?", - format!("{}() as {}", from_snippet, cast_to), + format!("{from_snippet}() as {cast_to}"), applicability, ); }, diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs index 6287f479b5bf..556be1d15066 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -24,12 +24,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST_WITH_TRUNCATION, expr.span, - &format!( - "casting function pointer `{}` to `{}`, which truncates the value", - from_snippet, cast_to - ), + &format!("casting function pointer `{from_snippet}` to `{cast_to}`, which truncates the value"), "try", - format!("{} as usize", from_snippet), + format!("{from_snippet} as usize"), applicability, ); } diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 46d45d09661a..c2b9253ec35d 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -33,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option Cow::Borrowed(""), TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""), - _ => Cow::Owned(format!("::<{}>", to_pointee_ty)), + _ => Cow::Owned(format!("::<{to_pointee_ty}>")), }; span_lint_and_sugg( cx, @@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option( } } + let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); + if let Some(lit) = get_numeric_literal(cast_expr) { - let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); + let literal_str = &cast_str; if_chain! { if let LitKind::Int(n, _) = lit.node; @@ -49,12 +52,16 @@ pub(super) fn check<'tcx>( match lit.node { LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => { - lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); + lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); + return false; }, LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { - lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); + lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); + return false; + }, + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => { + return false; }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => @@ -62,48 +69,62 @@ pub(super) fn check<'tcx>( if let Some(src) = snippet_opt(cx, cast_expr.span) { if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + return true; } } }, - _ => { - if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { - span_lint_and_sugg( - cx, - UNNECESSARY_CAST, - expr.span, - &format!( - "casting to the same type is unnecessary (`{}` -> `{}`)", - cast_from, cast_to - ), - "try", - literal_str, - Applicability::MachineApplicable, - ); - return true; - } - }, + _ => {}, } } + if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { + span_lint_and_sugg( + cx, + UNNECESSARY_CAST, + expr.span, + &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), + "try", + cast_str, + Applicability::MachineApplicable, + ); + return true; + } + false } -fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { +fn lint_unnecessary_cast( + cx: &LateContext<'_>, + expr: &Expr<'_>, + raw_literal_str: &str, + cast_from: Ty<'_>, + cast_to: Ty<'_>, +) { let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; - let replaced_literal; - let matchless = if literal_str.contains(['(', ')']) { - replaced_literal = literal_str.replace(['(', ')'], ""); - &replaced_literal - } else { - literal_str + // first we remove all matches so `-(1)` become `-1`, and remove trailing dots, so `1.` become `1` + let literal_str = raw_literal_str + .replace(['(', ')'], "") + .trim_end_matches('.') + .to_string(); + // we know need to check if the parent is a method call, to add parenthesis accordingly (eg: + // (-1).foo() instead of -1.foo()) + let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(..) = parent_expr.kind + && literal_str.starts_with('-') + { + format!("({literal_str}_{cast_to})") + + } else { + format!("{literal_str}_{cast_to}") }; + span_lint_and_sugg( cx, UNNECESSARY_CAST, expr.span, - &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), + &format!("casting {literal_kind_name} literal to `{cast_to}` is unnecessary"), "try", - format!("{}_{}", matchless.trim_end_matches('.'), cast_to), + sugg, Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 37b2fdcff09f..78e9921f036f 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -2,9 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, meets_msrv, msrvs, SpanlessEq}; +use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -82,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { item.span, "checked cast can be simplified", "try", - format!("{}::try_from({}).is_ok()", to_type, snippet), + format!("{to_type}::try_from({snippet}).is_ok()"), applicability, ); } @@ -223,16 +222,7 @@ fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { /// Check for `expr >= 0` fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option> { - if_chain! { - if let ExprKind::Lit(ref lit) = &check.kind; - if let LitKind::Int(0, _) = &lit.node; - - then { - Some(Conversion::new_any(candidate)) - } else { - None - } - } + is_integer_literal(check, 0).then(|| Conversion::new_any(candidate)) } /// Check for `expr >= (to_type::MIN as from_type)` diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 33c44f8b2dba..77af3b53d633 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -3,10 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr; use clippy_utils::LimitStack; +use core::ops::ControlFlow; use rustc_ast::ast::Attribute; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, ExprKind, FnDecl, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -61,11 +63,27 @@ impl CognitiveComplexity { return; } - let expr = &body.value; + let expr = body.value; + + let mut cc = 1u64; + let mut returns = 0u64; + let _: Option = for_each_expr(expr, |e| { + match e.kind { + ExprKind::If(_, _, _) => { + cc += 1; + }, + ExprKind::Match(_, arms, _) => { + if arms.len() > 1 { + cc += 1; + } + cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; + }, + ExprKind::Ret(_) => returns += 1, + _ => {}, + } + ControlFlow::Continue(()) + }); - let mut helper = CcHelper { cc: 1, returns: 0 }; - helper.visit_expr(expr); - let CcHelper { cc, returns } = helper; let ret_ty = cx.typeck_results().node_type(expr.hir_id); let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) { returns @@ -74,13 +92,12 @@ impl CognitiveComplexity { (returns / 2) }; - let mut rust_cc = cc; // prevent degenerate cases where unreachable code contains `return` statements - if rust_cc >= ret_adjust { - rust_cc -= ret_adjust; + if cc >= ret_adjust { + cc -= ret_adjust; } - if rust_cc > self.limit.limit() { + if cc > self.limit.limit() { let fn_span = match kind { FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span, FnKind::Closure => { @@ -107,8 +124,7 @@ impl CognitiveComplexity { COGNITIVE_COMPLEXITY, fn_span, &format!( - "the function has a cognitive complexity of ({}/{})", - rust_cc, + "the function has a cognitive complexity of ({cc}/{})", self.limit.limit() ), None, @@ -141,27 +157,3 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity"); } } - -struct CcHelper { - cc: u64, - returns: u64, -} - -impl<'tcx> Visitor<'tcx> for CcHelper { - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - walk_expr(self, e); - match e.kind { - ExprKind::If(_, _, _) => { - self.cc += 1; - }, - ExprKind::Match(_, arms, _) => { - if arms.len() > 1 { - self.cc += 1; - } - self.cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; - }, - ExprKind::Ret(_) => self.returns += 1, - _ => {}, - } - } -} diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 4e68d6810e29..7f937de1dd31 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { cx, DEFAULT_TRAIT_ACCESS, expr.span, - &format!("calling `{}` is more clear than this expression", replacement), + &format!("calling `{replacement}` is more clear than this expression"), "try", replacement, Applicability::Unspecified, // First resolve the TODO above @@ -210,7 +210,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { .map(|(field, rhs)| { // extract and store the assigned value for help message let value_snippet = snippet_with_macro_callsite(cx, rhs.span, ".."); - format!("{}: {}", field, value_snippet) + format!("{field}: {value_snippet}") }) .collect::>() .join(", "); @@ -227,7 +227,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { .map(ToString::to_string) .collect::>() .join(", "); - format!("{}::<{}>", adt_def_ty_name, &tys_str) + format!("{adt_def_ty_name}::<{}>", &tys_str) } else { binding_type.to_string() } @@ -235,12 +235,12 @@ impl<'tcx> LateLintPass<'tcx> for Default { let sugg = if ext_with_default { if field_list.is_empty() { - format!("{}::default()", binding_type) + format!("{binding_type}::default()") } else { - format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) + format!("{binding_type} {{ {field_list}, ..Default::default() }}") } } else { - format!("{} {{ {} }}", binding_type, field_list) + format!("{binding_type} {{ {field_list} }}") }; // span lint once per statement that binds default @@ -250,10 +250,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { first_assign.unwrap().span, "field assignment outside of initializer for an instance created with Default::default()", Some(local.span), - &format!( - "consider initializing the variable with `{}` and removing relevant reassignments", - sugg - ), + &format!("consider initializing the variable with `{sugg}` and removing relevant reassignments"), ); self.reassigned_linted.insert(span); } diff --git a/clippy_lints/src/default_instead_of_iter_empty.rs b/clippy_lints/src/default_instead_of_iter_empty.rs index 3c996d3d2aee..1ad929864b2a 100644 --- a/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/clippy_lints/src/default_instead_of_iter_empty.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// let _ = std::iter::empty::(); /// let iter: std::iter::Empty = std::iter::empty(); /// ``` - #[clippy::version = "1.63.0"] + #[clippy::version = "1.64.0"] pub DEFAULT_INSTEAD_OF_ITER_EMPTY, style, "check `std::iter::Empty::default()` and replace with `std::iter::empty()`" diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index be02f328e989..3ed9cd36a229 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -95,8 +95,8 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { src } else { match lit.node { - LitKind::Int(src, _) => format!("{}", src), - LitKind::Float(src, _) => format!("{}", src), + LitKind::Int(src, _) => format!("{src}"), + LitKind::Float(src, _) => format!("{src}"), _ => return, } }; diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 3905a6c2e211..741edc131960 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{self as hir, HirId, Item, ItemKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index f0d5ed6f594b..3cd8f236e7a5 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -135,7 +135,7 @@ declare_clippy_lint! { /// let x = String::new(); /// let y: &str = &x; /// ``` - #[clippy::version = "1.60.0"] + #[clippy::version = "1.64.0"] pub EXPLICIT_AUTO_DEREF, complexity, "dereferencing when the compiler would automatically dereference" @@ -184,6 +184,7 @@ impl Dereferencing { } } +#[derive(Debug)] struct StateData { /// Span of the top level expression span: Span, @@ -191,12 +192,14 @@ struct StateData { position: Position, } +#[derive(Debug)] struct DerefedBorrow { count: usize, msg: &'static str, snip_expr: Option, } +#[derive(Debug)] enum State { // Any number of deref method calls. DerefMethod { @@ -276,10 +279,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { (None, kind) => { let expr_ty = typeck.expr_ty(expr); let (position, adjustments) = walk_parents(cx, expr, self.msrv); - match kind { RefOp::Deref => { - if let Position::FieldAccess(name) = position + if let Position::FieldAccess { + name, + of_union: false, + } = position && !ty_contains_field(typeck.expr_ty(sub_expr), name) { self.state = Some(( @@ -451,7 +456,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => { let position = data.position; report(cx, expr, State::DerefedBorrow(state), data); - if let Position::FieldAccess(name) = position + if let Position::FieldAccess{name, ..} = position && !ty_contains_field(typeck.expr_ty(sub_expr), name) { self.state = Some(( @@ -616,14 +621,17 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { } /// The position of an expression relative to it's parent. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] enum Position { MethodReceiver, /// The method is defined on a reference type. e.g. `impl Foo for &T` MethodReceiverRefImpl, Callee, ImplArg(HirId), - FieldAccess(Symbol), + FieldAccess { + name: Symbol, + of_union: bool, + }, // union fields cannot be auto borrowed Postfix, Deref, /// Any other location which will trigger auto-deref to a specific time. @@ -645,7 +653,10 @@ impl Position { } fn can_auto_borrow(self) -> bool { - matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee) + matches!( + self, + Self::MethodReceiver | Self::FieldAccess { of_union: false, .. } | Self::Callee + ) } fn lint_explicit_deref(self) -> bool { @@ -657,7 +668,7 @@ impl Position { Self::MethodReceiver | Self::MethodReceiverRefImpl | Self::Callee - | Self::FieldAccess(_) + | Self::FieldAccess { .. } | Self::Postfix => PREC_POSTFIX, Self::ImplArg(_) | Self::Deref => PREC_PREFIX, Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p, @@ -844,7 +855,10 @@ fn walk_parents<'tcx>( } }) }, - ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)), + ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess { + name: name.name, + of_union: is_union(cx.typeck_results(), child), + }), ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref), ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) | ExprKind::Index(child, _) @@ -865,6 +879,13 @@ fn walk_parents<'tcx>( (position, adjustments) } +fn is_union<'tcx>(typeck: &'tcx TypeckResults<'_>, path_expr: &'tcx Expr<'_>) -> bool { + typeck + .expr_ty_adjusted(path_expr) + .ty_adt_def() + .map_or(false, rustc_middle::ty::AdtDef::is_union) +} + fn closure_result_position<'tcx>( cx: &LateContext<'tcx>, closure: &'tcx Closure<'_>, @@ -1308,7 +1329,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data }; let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX { - format!("({})", expr_str) + format!("({expr_str})") } else { expr_str.into_owned() }; @@ -1322,7 +1343,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data Mutability::Mut => "explicit `deref_mut` method call", }, "try this", - format!("{}{}{}", addr_of_str, deref_str, expr_str), + format!("{addr_of_str}{deref_str}{expr_str}"), app, ); }, @@ -1336,7 +1357,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data && !has_enclosing_paren(&snip) && (expr.precedence().order() < data.position.precedence() || calls_field) { - format!("({})", snip) + format!("({snip})") } else { snip.into() }; @@ -1379,9 +1400,9 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); let sugg = if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) { - format!("{}({})", prefix, snip) + format!("{prefix}({snip})") } else { - format!("{}{}", prefix, snip) + format!("{prefix}{snip}") }; diag.span_suggestion(data.span, "try this", sugg, app); }, @@ -1460,14 +1481,14 @@ impl Dereferencing { } else { pat.always_deref = false; let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; - pat.replacements.push((e.span, format!("&{}", snip))); + pat.replacements.push((e.span, format!("&{snip}"))); } }, _ if !e.span.from_expansion() => { // Double reference might be needed at this point. pat.always_deref = false; let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); - pat.replacements.push((e.span, format!("&{}", snip))); + pat.replacements.push((e.span, format!("&{snip}"))); }, // Edge case for macros. The span of the identifier will usually match the context of the // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 751ca24d5f59..3fac93dcc90c 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -191,7 +191,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.63.0"] pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, - style, + nursery, "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" } diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs new file mode 100644 index 000000000000..5ab7144e2909 --- /dev/null +++ b/clippy_lints/src/disallowed_macros.rs @@ -0,0 +1,151 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::macro_backtrace; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{ExpnId, Span}; + +use crate::utils::conf; + +declare_clippy_lint! { + /// ### What it does + /// Denies the configured macros in clippy.toml + /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// macros are defined in the clippy.toml file. + /// + /// ### Why is this bad? + /// Some macros are undesirable in certain contexts, and it's beneficial to + /// lint for them as needed. + /// + /// ### Example + /// An example clippy.toml configuration: + /// ```toml + /// # clippy.toml + /// disallowed-macros = [ + /// # Can use a string as the path of the disallowed macro. + /// "std::print", + /// # Can also use an inline table with a `path` key. + /// { path = "std::println" }, + /// # When using an inline table, can add a `reason` for why the macro + /// # is disallowed. + /// { path = "serde::Serialize", reason = "no serializing" }, + /// ] + /// ``` + /// ``` + /// use serde::Serialize; + /// + /// // Example code where clippy issues a warning + /// println!("warns"); + /// + /// // The diagnostic will contain the message "no serializing" + /// #[derive(Serialize)] + /// struct Data { + /// name: String, + /// value: usize, + /// } + /// ``` + #[clippy::version = "1.65.0"] + pub DISALLOWED_MACROS, + style, + "use of a disallowed macro" +} + +pub struct DisallowedMacros { + conf_disallowed: Vec, + disallowed: DefIdMap, + seen: FxHashSet, +} + +impl DisallowedMacros { + pub fn new(conf_disallowed: Vec) -> Self { + Self { + conf_disallowed, + disallowed: DefIdMap::default(), + seen: FxHashSet::default(), + } + } + + fn check(&mut self, cx: &LateContext<'_>, span: Span) { + if self.conf_disallowed.is_empty() { + return; + } + + for mac in macro_backtrace(span) { + if !self.seen.insert(mac.expn) { + return; + } + + if let Some(&index) = self.disallowed.get(&mac.def_id) { + let conf = &self.conf_disallowed[index]; + + span_lint_and_then( + cx, + DISALLOWED_MACROS, + mac.span, + &format!("use of a disallowed macro `{}`", conf.path()), + |diag| { + if let Some(reason) = conf.reason() { + diag.note(&format!("{reason} (from clippy.toml)")); + } + }, + ); + } + } + } +} + +impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); + +impl LateLintPass<'_> for DisallowedMacros { + fn check_crate(&mut self, cx: &LateContext<'_>) { + for (index, conf) in self.conf_disallowed.iter().enumerate() { + let segs: Vec<_> = conf.path().split("::").collect(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) { + self.disallowed.insert(id, index); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + self.check(cx, expr.span); + } + + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + self.check(cx, stmt.span); + } + + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_>) { + self.check(cx, ty.span); + } + + fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { + self.check(cx, pat.span); + } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + self.check(cx, item.span); + self.check(cx, item.vis_span); + } + + fn check_foreign_item(&mut self, cx: &LateContext<'_>, item: &ForeignItem<'_>) { + self.check(cx, item.span); + self.check(cx, item.vis_span); + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) { + self.check(cx, item.span); + self.check(cx, item.vis_span); + } + + fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { + self.check(cx, item.span); + } + + fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) { + self.check(cx, path.span); + } +} diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 53973ab792a9..1a381f92c031 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, get_parent_expr, path_def_id}; -use rustc_hir::{def::Res, def_id::DefIdMap, Expr, ExprKind}; +use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -58,12 +60,12 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedMethods { - conf_disallowed: Vec, + conf_disallowed: Vec, disallowed: DefIdMap, } impl DisallowedMethods { - pub fn new(conf_disallowed: Vec) -> Self { + pub fn new(conf_disallowed: Vec) -> Self { Self { conf_disallowed, disallowed: DefIdMap::default(), @@ -77,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_crate(&mut self, cx: &LateContext<'_>) { for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) { + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) { self.disallowed.insert(id, index); } } @@ -102,11 +104,8 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }; let msg = format!("use of a disallowed method `{}`", conf.path()); span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { - if let conf::DisallowedMethod::WithReason { - reason: Some(reason), .. - } = conf - { - diag.note(&format!("{} (from clippy.toml)", reason)); + if let Some(reason) = conf.reason() { + diag.note(&format!("{reason} (from clippy.toml)")); } }); } diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs index 0c27c3f9255f..084190f00132 100644 --- a/clippy_lints/src/disallowed_script_idents.rs +++ b/clippy_lints/src/disallowed_script_idents.rs @@ -99,8 +99,7 @@ impl EarlyLintPass for DisallowedScriptIdents { DISALLOWED_SCRIPT_IDENTS, span, &format!( - "identifier `{}` has a Unicode script that is not allowed by configuration: {}", - symbol_str, + "identifier `{symbol_str}` has a Unicode script that is not allowed by configuration: {}", script.full_name() ), ); diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 28dbfbab2e19..c7131fc164d3 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{ - def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind, -}; +use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -52,13 +52,13 @@ declare_clippy_lint! { } #[derive(Clone, Debug)] pub struct DisallowedTypes { - conf_disallowed: Vec, + conf_disallowed: Vec, def_ids: FxHashMap>, prim_tys: FxHashMap>, } impl DisallowedTypes { - pub fn new(conf_disallowed: Vec) -> Self { + pub fn new(conf_disallowed: Vec) -> Self { Self { conf_disallowed, def_ids: FxHashMap::default(), @@ -88,15 +88,9 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_crate(&mut self, cx: &LateContext<'_>) { for conf in &self.conf_disallowed { - let (path, reason) = match conf { - conf::DisallowedType::Simple(path) => (path, None), - conf::DisallowedType::WithReason { path, reason } => ( - path, - reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)), - ), - }; - let segs: Vec<_> = path.split("::").collect(); - match clippy_utils::def_path_res(cx, &segs) { + let segs: Vec<_> = conf.path().split("::").collect(); + let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)")); + match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { Res::Def(_, id) => { self.def_ids.insert(id, reason); }, @@ -130,7 +124,7 @@ fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) { cx, DISALLOWED_TYPES, span, - &format!("`{}` is not allowed according to config", name), + &format!("`{name}` is not allowed according to config"), |diag| { if let Some(reason) = reason { diag.note(reason); diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index f48ba526d51e..36dc7e3396b8 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -198,6 +198,29 @@ declare_clippy_lint! { "presence of `fn main() {` in code examples" } +declare_clippy_lint! { + /// ### What it does + /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) + /// outside of code blocks + /// ### Why is this bad? + /// It is likely a typo when defining an intra-doc link + /// + /// ### Example + /// ```rust + /// /// See also: ['foo'] + /// fn bar() {} + /// ``` + /// Use instead: + /// ```rust + /// /// See also: [`foo`] + /// fn bar() {} + /// ``` + #[clippy::version = "1.63.0"] + pub DOC_LINK_WITH_QUOTES, + pedantic, + "possible typo for an intra-doc link" +} + #[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { @@ -214,9 +237,14 @@ impl DocMarkdown { } } -impl_lint_pass!(DocMarkdown => - [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN] -); +impl_lint_pass!(DocMarkdown => [ + DOC_LINK_WITH_QUOTES, + DOC_MARKDOWN, + MISSING_SAFETY_DOC, + MISSING_ERRORS_DOC, + MISSING_PANICS_DOC, + NEEDLESS_DOCTEST_MAIN +]); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_crate(&mut self, cx: &LateContext<'tcx>) { @@ -237,7 +265,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers(cx, item.def_id.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); + lint_for_missing_headers( + cx, + item.def_id.def_id, + item.span, + sig, + headers, + Some(body_id), + fpu.panic_span, + ); } }, hir::ItemKind::Impl(impl_) => { @@ -287,7 +323,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers(cx, item.def_id.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); + lint_for_missing_headers( + cx, + item.def_id.def_id, + item.span, + sig, + headers, + Some(body_id), + fpu.panic_span, + ); } } } @@ -416,7 +460,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: (no_stars, sizes) } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] struct DocHeaders { safety: bool, errors: bool, @@ -460,11 +504,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs } if doc.is_empty() { - return DocHeaders { - safety: false, - errors: false, - panics: false, - }; + return DocHeaders::default(); } let mut cb = fake_broken_link_callback; @@ -505,11 +545,7 @@ fn check_doc<'a, Events: Iterator, Range, Range, Range, + in_link: bool, + trimmed_text: &str, + span: Span, + range: &Range, + begin: usize, + text_len: usize, +) { + if in_link && trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'') { + // fix the span to only point at the text within the link + let lo = span.lo() + BytePos::from_usize(range.start - begin); + span_lint( + cx, + DOC_LINK_WITH_QUOTES, + span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)), + "possible intra-doc link using quotes instead of backticks", + ); + } +} + fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) { let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) { Ok(o) => o, @@ -790,7 +848,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { diag.span_suggestion_with_style( span, "try", - format!("`{}`", snippet), + format!("`{snippet}`"), applicability, // always show the suggestion in a separate line, since the // inline presentation adds another pair of backticks diff --git a/clippy_lints/src/doc_link_with_quotes.rs b/clippy_lints/src/doc_link_with_quotes.rs deleted file mode 100644 index 0ff1d2755daf..000000000000 --- a/clippy_lints/src/doc_link_with_quotes.rs +++ /dev/null @@ -1,60 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use itertools::Itertools; -use rustc_ast::{AttrKind, Attribute}; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) - /// outside of code blocks - /// ### Why is this bad? - /// It is likely a typo when defining an intra-doc link - /// - /// ### Example - /// ```rust - /// /// See also: ['foo'] - /// fn bar() {} - /// ``` - /// Use instead: - /// ```rust - /// /// See also: [`foo`] - /// fn bar() {} - /// ``` - #[clippy::version = "1.63.0"] - pub DOC_LINK_WITH_QUOTES, - pedantic, - "possible typo for an intra-doc link" -} -declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]); - -impl EarlyLintPass for DocLinkWithQuotes { - fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) { - if let AttrKind::DocComment(_, symbol) = attr.kind { - if contains_quote_link(symbol.as_str()) { - span_lint( - ctx, - DOC_LINK_WITH_QUOTES, - attr.span, - "possible intra-doc link using quotes instead of backticks", - ); - } - } - } -} - -fn contains_quote_link(s: &str) -> bool { - let mut in_backticks = false; - let mut found_opening = false; - - for c in s.chars().tuple_windows::<(char, char)>() { - match c { - ('`', _) => in_backticks = !in_backticks, - ('[', '\'') if !in_backticks => found_opening = true, - ('\'', ']') if !in_backticks && found_opening => return true, - _ => {}, - } - } - - false -} diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index b35f0b8ca52d..4721a7b37056 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::get_parent_node; 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_hir::{Arm, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -202,11 +203,13 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id) { let arg_ty = cx.typeck_results().expr_ty(arg); + let is_copy = is_copy(cx, arg_ty); + let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); 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_copy && !drop_is_single_call_in_arm => (DROP_COPY, DROP_COPY_SUMMARY), + sym::mem_forget if is_copy => (FORGET_COPY, FORGET_COPY_SUMMARY), sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => { span_lint_and_help( cx, @@ -221,7 +224,9 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { 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)) => + || is_must_use_ty(cx, arg_ty) + || drop_is_single_call_in_arm + ) => { (DROP_NON_DROP, DROP_NON_DROP_SUMMARY) }, @@ -236,8 +241,23 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { expr.span, msg, Some(arg.span), - &format!("argument has type `{}`", arg_ty), + &format!("argument has type `{arg_ty}`"), ); } } } + +// dropping returned value of a function like in the following snippet is considered idiomatic, see +// #9482 for examples match { +// => drop(fn_with_side_effect_and_returning_some_value()), +// .. +// } +fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool { + if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) { + let parent_node = get_parent_node(cx.tcx, drop_expr.hir_id); + if let Some(Node::Arm(Arm { body, .. })) = &parent_node { + return body.hir_id == drop_expr.hir_id; + } + } + false +} diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index e70df3f53c75..9c834cf01448 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -113,13 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { ), }; format!( - "if let {}::{} = {}.entry({}) {} else {}", + "if let {}::{entry_kind} = {map_str}.entry({key_str}) {then_str} else {else_str}", map_ty.entry_path(), - entry_kind, - map_str, - key_str, - then_str, - else_str, ) } else { // if .. { insert } else { insert } @@ -137,16 +132,11 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { let indent_str = snippet_indent(cx, expr.span); let indent_str = indent_str.as_deref().unwrap_or(""); format!( - "match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\ - {indent} {entry}::{} => {}\n{indent}}}", - map_str, - key_str, - then_entry, + "match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\ + {indent_str} {entry}::{else_entry} => {}\n{indent_str}}}", reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())), - else_entry, reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())), entry = map_ty.entry_path(), - indent = indent_str, ) } } else { @@ -163,20 +153,16 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { then_search.snippet_occupied(cx, then_expr.span, &mut app) }; format!( - "if let {}::{} = {}.entry({}) {}", + "if let {}::{entry_kind} = {map_str}.entry({key_str}) {body_str}", map_ty.entry_path(), - entry_kind, - map_str, - key_str, - body_str, ) } else if let Some(insertion) = then_search.as_single_insertion() { let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0; if contains_expr.negated { if insertion.value.can_have_side_effects() { - format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str) + format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});") } else { - format!("{}.entry({}).or_insert({});", map_str, key_str, value_str) + format!("{map_str}.entry({key_str}).or_insert({value_str});") } } else { // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. @@ -186,7 +172,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { } else { let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app); if contains_expr.negated { - format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str) + format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});") } else { // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. // This would need to be a different lint. diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index c39a909b3ccb..b019d07d53d1 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -202,12 +202,11 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n cx, ENUM_VARIANT_NAMES, span, - &format!("all variants have the same {}fix: `{}`", what, value), + &format!("all variants have the same {what}fix: `{value}`"), None, &format!( - "remove the {}fixes and use full paths to \ - the variants instead of glob imports", - what + "remove the {what}fixes and use full paths to \ + the variants instead of glob imports" ), ); } diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index bce49165e5b1..b40cb7cddaf1 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -51,9 +51,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { false }, PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), - PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => { - !etc.as_opt_usize().is_some() && array_rec(a) - } + PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x), PatKind::Path(_) | PatKind::Lit(_) => true, } @@ -93,9 +91,8 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { "this pattern matching can be expressed using equality", "try", format!( - "{} == {}", + "{} == {pat_str}", snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - pat_str, ), applicability, ); diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 8ccc969646ec..2e608fe527fd 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir; use rustc_hir::intravisit; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; @@ -10,7 +11,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::kw; use rustc_target::spec::abi::Abi; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; #[derive(Copy, Clone)] pub struct BoxedLocal { @@ -177,7 +177,13 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { } } - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 598f8c31859e..3732410e71e5 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; use if_chain::if_chain; @@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::{self, ClosureKind, Ty, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -122,15 +122,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { then { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if_chain! { - if let ty::Closure(_, substs) = callee_ty.peel_refs().kind(); - if substs.as_closure().kind() == ClosureKind::FnMut; - if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)); - - then { + if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait() + && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &[]) + && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)) + { // Mutable closure is used after current expr; we cannot consume it. - snippet = format!("&mut {}", snippet); - } + snippet = format!("&mut {snippet}"); } diag.span_suggestion( expr.span, @@ -157,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { diag.span_suggestion( expr.span, "replace the closure with the method itself", - format!("{}::{}", name, path.ident.name), + format!("{name}::{}", path.ident.name), Applicability::MachineApplicable, ); }) diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index f3d9ebc5f12d..be6242bd20b8 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -97,7 +97,7 @@ impl LateLintPass<'_> for ExhaustiveItems { item.span, msg, |diag| { - let sugg = format!("#[non_exhaustive]\n{}", indent); + let sugg = format!("#[non_exhaustive]\n{indent}"); diag.span_suggestion(suggestion_span, "try adding #[non_exhaustive]", sugg, diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index b9ed4af02190..c0ea6f338a23 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -80,12 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { // used. let (used, sugg_mac) = if let Some(macro_name) = calling_macro { ( - format!("{}!({}(), ...)", macro_name, dest_name), + format!("{macro_name}!({dest_name}(), ...)"), macro_name.replace("write", "print"), ) } else { ( - format!("{}().write_fmt(...)", dest_name), + format!("{dest_name}().write_fmt(...)"), "print".into(), ) }; @@ -100,9 +100,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { cx, EXPLICIT_WRITE, expr.span, - &format!("use of `{}.unwrap()`", used), + &format!("use of `{used}.unwrap()`"), "try this", - format!("{}{}!({})", prefix, sugg_mac, inputs_snippet), + format!("{prefix}{sugg_mac}!({inputs_snippet})"), applicability, ) } diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index f2e079809637..6fee7fb308ce 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -173,9 +173,9 @@ impl FloatFormat { T: fmt::UpperExp + fmt::LowerExp + fmt::Display, { match self { - Self::LowerExp => format!("{:e}", f), - Self::UpperExp => format!("{:E}", f), - Self::Normal => format!("{}", f), + Self::LowerExp => format!("{f:e}"), + Self::UpperExp => format!("{f:E}"), + Self::Normal => format!("{f}"), } } } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index ba53a9678801..0ed301964758 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -142,8 +142,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node; then { let op = format!( - "{}{}{}", - suggestion, + "{suggestion}{}{}", // Check for float literals without numbers following the decimal // separator such as `2.` and adds a trailing zero if sym.as_str().ends_with('.') { @@ -172,7 +171,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, ar expr.span, "logarithm for bases 2, 10 and e can be computed more accurately", "consider using", - format!("{}.{}()", Sugg::hir(cx, receiver, "..").maybe_par(), method), + format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_par()), Applicability::MachineApplicable, ); } @@ -251,7 +250,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: expr.span, "exponent for bases 2 and e can be computed more accurately", "consider using", - format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method), + format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])), Applicability::MachineApplicable, ); } @@ -312,7 +311,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: if let ExprKind::Binary( Spanned { - node: BinOpKind::Add, .. + node: op @ (BinOpKind::Add | BinOpKind::Sub), + .. }, lhs, rhs, @@ -320,6 +320,16 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: { let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; + // Negate expr if original code has subtraction and expr is on the right side + let maybe_neg_sugg = |expr, hir_id| { + let sugg = Sugg::hir(cx, expr, ".."); + if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { + format!("-{sugg}") + } else { + sugg.to_string() + } + }; + span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, @@ -329,8 +339,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: format!( "{}.mul_add({}, {})", Sugg::hir(cx, receiver, "..").maybe_par(), - Sugg::hir(cx, receiver, ".."), - Sugg::hir(cx, other_addend, ".."), + maybe_neg_sugg(receiver, expr.hir_id), + maybe_neg_sugg(other_addend, other_addend.hir_id), ), Applicability::MachineApplicable, ); @@ -444,7 +454,8 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&' fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Binary( Spanned { - node: BinOpKind::Add, .. + node: op @ (BinOpKind::Add | BinOpKind::Sub), + .. }, lhs, rhs, @@ -458,10 +469,27 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { } } + let maybe_neg_sugg = |expr| { + let sugg = Sugg::hir(cx, expr, ".."); + if let BinOpKind::Sub = op { + format!("-{sugg}") + } else { + sugg.to_string() + } + }; + let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) { - (inner_lhs, inner_rhs, rhs) + ( + inner_lhs, + Sugg::hir(cx, inner_rhs, "..").to_string(), + maybe_neg_sugg(rhs), + ) } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) { - (inner_lhs, inner_rhs, lhs) + ( + inner_lhs, + maybe_neg_sugg(inner_rhs), + Sugg::hir(cx, lhs, "..").to_string(), + ) } else { return; }; @@ -472,12 +500,7 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "multiply and add expressions can be calculated more efficiently and accurately", "consider using", - format!( - "{}.mul_add({}, {})", - prepare_receiver_sugg(cx, recv), - Sugg::hir(cx, arg1, ".."), - Sugg::hir(cx, arg2, ".."), - ), + format!("{}.mul_add({arg1}, {arg2})", prepare_receiver_sugg(cx, recv)), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index f10d82569536..bc0c68f535a9 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { [_] => { // Simulate macro expansion, converting {{ and }} to { and }. let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}"); - let sugg = format!("{}.to_string()", s_expand); + let sugg = format!("{s_expand}.to_string()"); span_useless_format(cx, call_site, sugg, applicability); }, [..] => {}, diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 9e1eaf248b73..cefebc2a98a2 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,16 +1,18 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::is_diag_trait_item; -use clippy_utils::macros::{is_format_macro, FormatArgsExpn}; +use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; +use clippy_utils::macros::{is_format_macro, FormatArgsExpn, FormatParam, FormatParamUsage}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; +use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs}; use if_chain::if_chain; use itertools::Itertools; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_hir::{Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol}; declare_clippy_lint! { @@ -64,7 +66,67 @@ declare_clippy_lint! { "`to_string` applied to a type that implements `Display` in format args" } -declare_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); +declare_clippy_lint! { + /// ### What it does + /// Detect when a variable is not inlined in a format string, + /// and suggests to inline it. + /// + /// ### Why is this bad? + /// Non-inlined code is slightly more difficult to read and understand, + /// as it requires arguments to be matched against the format string. + /// The inlined syntax, where allowed, is simpler. + /// + /// ### Example + /// ```rust + /// # let var = 42; + /// # let width = 1; + /// # let prec = 2; + /// format!("{}", var); + /// format!("{v:?}", v = var); + /// format!("{0} {0}", var); + /// format!("{0:1$}", var, width); + /// format!("{:.*}", prec, var); + /// ``` + /// Use instead: + /// ```rust + /// # let var = 42; + /// # let width = 1; + /// # let prec = 2; + /// format!("{var}"); + /// format!("{var:?}"); + /// format!("{var} {var}"); + /// format!("{var:width$}"); + /// format!("{var:.prec$}"); + /// ``` + /// + /// ### Known Problems + /// + /// There may be a false positive if the format string is expanded from certain proc macros: + /// + /// ```ignore + /// println!(indoc!("{}"), var); + /// ``` + /// + /// If a format string contains a numbered argument that cannot be inlined + /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. + #[clippy::version = "1.65.0"] + pub UNINLINED_FORMAT_ARGS, + pedantic, + "using non-inlined variables in `format!` calls" +} + +impl_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); + +pub struct FormatArgs { + msrv: Option, +} + +impl FormatArgs { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { @@ -86,9 +148,72 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); check_to_string_in_format_args(cx, name, arg.param.value); } + if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { + check_uninlined_args(cx, &format_args, outermost_expn_data.call_site); + } } } } + + extract_msrv_attr!(LateContext); +} + +fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span) { + if args.format_string.span.from_expansion() { + return; + } + + let mut fixes = Vec::new(); + // If any of the arguments are referenced by an index number, + // and that argument is not a simple variable and cannot be inlined, + // we cannot remove any other arguments in the format string, + // because the index numbers might be wrong after inlining. + // Example of an un-inlinable format: print!("{}{1}", foo, 2) + if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() { + return; + } + + // FIXME: Properly ignore a rare case where the format string is wrapped in a macro. + // Example: `format!(indoc!("{}"), foo);` + // If inlined, they will cause a compilation error: + // > to avoid ambiguity, `format_args!` cannot capture variables + // > when the format string is expanded from a macro + // @Alexendoo explanation: + // > indoc! is a proc macro that is producing a string literal with its span + // > set to its input it's not marked as from expansion, and since it's compatible + // > tokenization wise clippy_utils::is_from_proc_macro wouldn't catch it either + // This might be a relatively expensive test, so do it only we are ready to replace. + // See more examples in tests/ui/uninlined_format_args.rs + + span_lint_and_then( + cx, + UNINLINED_FORMAT_ARGS, + call_site, + "variables can be used directly in the `format!` string", + |diag| { + diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable); + }, + ); +} + +fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool { + if matches!(param.kind, Implicit | Starred | Named(_) | Numbered) + && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind + && let [segment] = path.segments + && let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id) + { + let replacement = match param.usage { + FormatParamUsage::Argument => segment.ident.name.to_string(), + FormatParamUsage::Width => format!("{}$", segment.ident.name), + FormatParamUsage::Precision => format!(".{}$", segment.ident.name), + }; + fixes.push((param.span, replacement)); + fixes.push((arg_span, String::new())); + true // successful inlining, continue checking + } else { + // if we can't inline a numbered argument, we can't continue + param.kind != Numbered + } } fn outermost_expn_data(expn_data: ExpnData) -> ExpnData { @@ -117,11 +242,10 @@ fn check_format_in_format_args( cx, FORMAT_IN_FORMAT_ARGS, call_site, - &format!("`format!` in `{}!` args", name), + &format!("`format!` in `{name}!` args"), |diag| { diag.help(&format!( - "combine the `format!(..)` arguments with the outer `{}!(..)` call", - name + "combine the `format!(..)` arguments with the outer `{name}!(..)` call" )); diag.help("or consider changing `format!` to `format_args!`"); }, @@ -149,8 +273,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex TO_STRING_IN_FORMAT_ARGS, value.span.with_lo(receiver.span.hi()), &format!( - "`to_string` applied to a type that implements `Display` in `{}!` args", - name + "`to_string` applied to a type that implements `Display` in `{name}!` args" ), "remove this", String::new(), @@ -162,16 +285,13 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex TO_STRING_IN_FORMAT_ARGS, value.span, &format!( - "`to_string` applied to a type that implements `Display` in `{}!` args", - name + "`to_string` applied to a type that implements `Display` in `{name}!` args" ), "use this", format!( - "{}{:*>width$}{}", + "{}{:*>n_needed_derefs$}{receiver_snippet}", if needs_ref { "&" } else { "" }, - "", - receiver_snippet, - width = n_needed_derefs + "" ), Applicability::MachineApplicable, ); @@ -180,7 +300,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex } } -// Returns true if `hir_id` is referred to by multiple format params +/// Returns true if `hir_id` is referred to by multiple format params fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool { args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err() } diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index b628fd9f7581..ed1342a54654 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -214,12 +214,12 @@ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: cx, PRINT_IN_FORMAT_IMPL, macro_call.span, - &format!("use of `{}!` in `{}` impl", name, impl_trait.name), + &format!("use of `{name}!` in `{}` impl", impl_trait.name), "replace with", if let Some(formatter_name) = impl_trait.formatter_name { - format!("{}!({}, ..)", replacement, formatter_name) + format!("{replacement}!({formatter_name}, ..)") } else { - format!("{}!(..)", replacement) + format!("{replacement}!(..)") }, Applicability::HasPlaceholders, ); diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 01cefe4af853..a866a68987d0 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -154,11 +154,10 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) { eqop_span, &format!( "this looks like you are trying to use `.. {op}= ..`, but you \ - really are doing `.. = ({op} ..)`", - op = op + really are doing `.. = ({op} ..)`" ), None, - &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op), + &format!("to remove this lint, use either `{op}=` or `= {op}`"), ); } } @@ -191,16 +190,12 @@ fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) { SUSPICIOUS_UNARY_OP_FORMATTING, eqop_span, &format!( - "by not having a space between `{binop}` and `{unop}` it looks like \ - `{binop}{unop}` is a single operator", - binop = binop_str, - unop = unop_str + "by not having a space between `{binop_str}` and `{unop_str}` it looks like \ + `{binop_str}{unop_str}` is a single operator" ), None, &format!( - "put a space between `{binop}` and `{unop}` and remove the space after `{unop}`", - binop = binop_str, - unop = unop_str + "put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`" ), ); } @@ -246,12 +241,11 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { cx, SUSPICIOUS_ELSE_FORMATTING, else_span, - &format!("this is an `else {}` but the formatting might hide it", else_desc), + &format!("this is an `else {else_desc}` but the formatting might hide it"), None, &format!( "to remove this lint, remove the `else` or remove the new line between \ - `else` and `{}`", - else_desc, + `else` and `{else_desc}`", ), ); } @@ -320,11 +314,10 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { cx, SUSPICIOUS_ELSE_FORMATTING, else_span, - &format!("this looks like {} but the `else` is missing", looks_like), + &format!("this looks like {looks_like} but the `else` is missing"), None, &format!( - "to remove this lint, add the missing `else` or add a new line before {}", - next_thing, + "to remove this lint, add the missing `else` or add a new line before {next_thing}", ), ); } diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 74941d817be3..cf8b7acd66d2 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_integer_literal; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; @@ -60,8 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { if pathseg.ident.name.as_str() == "from_str_radix"; // check if the second argument is a primitive `10` - if let ExprKind::Lit(lit) = &radix.kind; - if let rustc_ast::ast::LitKind::Int(10, _) = lit.node; + if is_integer_literal(radix, 10); then { let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { @@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { exp.span, "this call to `from_str_radix` can be replaced with a call to `str::parse`", "try", - format!("{}.parse::<{}>()", sugg, prim_ty.name_str()), + format!("{sugg}.parse::<{}>()", prim_ty.name_str()), Applicability::MaybeIncorrect ); } diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index d6d33bda1738..d263804f32cf 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -1,7 +1,7 @@ use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::def_id::{DefIdSet, LocalDefId}; -use rustc_hir::{self as hir, def::Res, intravisit, QPath}; +use rustc_hir::{self as hir, def::Res, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::{ lint::in_external_macro, @@ -13,8 +13,11 @@ use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{match_def_path, return_ty, trait_ref_of_method}; +use core::ops::ControlFlow; + use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { @@ -47,7 +50,8 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id.def_id).is_none() { + } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id.def_id).is_none() + { check_must_use_candidate( cx, sig.decl, @@ -143,7 +147,7 @@ fn check_must_use_candidate<'tcx>( diag.span_suggestion( fn_span, "add the attribute", - format!("#[must_use] {}", snippet), + format!("#[must_use] {snippet}"), Applicability::MachineApplicable, ); } @@ -199,63 +203,6 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m } } -struct StaticMutVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - mutates_static: bool, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; - - if self.mutates_static { - return; - } - match expr.kind { - Call(_, args) => { - let mut tys = DefIdSet::default(); - for arg in args { - if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - self.cx, - self.cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), - arg.span, - &mut tys, - ) - && is_mutated_static(arg) - { - self.mutates_static = true; - return; - } - tys.clear(); - } - }, - MethodCall(_, receiver, args, _) => { - let mut tys = DefIdSet::default(); - for arg in std::iter::once(receiver).chain(args.iter()) { - if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - self.cx, - self.cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), - arg.span, - &mut tys, - ) - && is_mutated_static(arg) - { - self.mutates_static = true; - return; - } - tys.clear(); - } - }, - Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => { - self.mutates_static |= is_mutated_static(target); - }, - _ => {}, - } - } -} - fn is_mutated_static(e: &hir::Expr<'_>) -> bool { use hir::ExprKind::{Field, Index, Path}; @@ -268,10 +215,53 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool { } fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { - let mut v = StaticMutVisitor { - cx, - mutates_static: false, - }; - intravisit::walk_expr(&mut v, body.value); - v.mutates_static + for_each_expr(body.value, |e| { + use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; + + match e.kind { + Call(_, args) => { + let mut tys = DefIdSet::default(); + for arg in args { + if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) + && is_mutable_ty( + cx, + cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), + arg.span, + &mut tys, + ) + && is_mutated_static(arg) + { + return ControlFlow::Break(()); + } + tys.clear(); + } + ControlFlow::Continue(()) + }, + MethodCall(_, receiver, args, _) => { + let mut tys = DefIdSet::default(); + for arg in std::iter::once(receiver).chain(args.iter()) { + if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) + && is_mutable_ty( + cx, + cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), + arg.span, + &mut tys, + ) + && is_mutated_static(arg) + { + return ControlFlow::Break(()); + } + tys.clear(); + } + ControlFlow::Continue(()) + }, + Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) + if is_mutated_static(target) => + { + ControlFlow::Break(()) + }, + _ => ControlFlow::Continue(()), + } + }) + .is_some() } diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 0b50431fbaab..b7595d101e0f 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -5,8 +5,11 @@ use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::type_is_unsafe_function; +use clippy_utils::visitors::for_each_expr_with_closures; use clippy_utils::{iter_input_pats, path_to_local}; +use core::ops::ControlFlow; + use super::NOT_UNSAFE_PTR_ARG_DEREF; pub(super) fn check_fn<'tcx>( @@ -39,21 +42,34 @@ fn check_raw_ptr<'tcx>( body: &'tcx hir::Body<'tcx>, def_id: LocalDefId, ) { - let expr = &body.value; if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(def_id) { let raw_ptrs = iter_input_pats(decl, body) .filter_map(|arg| raw_ptr_arg(cx, arg)) .collect::(); if !raw_ptrs.is_empty() { - let typeck_results = cx.tcx.typeck_body(body.id()); - let mut v = DerefVisitor { - cx, - ptrs: raw_ptrs, - typeck_results, - }; - - intravisit::walk_expr(&mut v, expr); + let typeck = cx.tcx.typeck_body(body.id()); + let _: Option = for_each_expr_with_closures(cx, body.value, |e| { + match e.kind { + hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => { + for arg in args { + check_arg(cx, &raw_ptrs, arg); + } + }, + hir::ExprKind::MethodCall(_, recv, args, _) => { + let def_id = typeck.type_dependent_def_id(e.hir_id).unwrap(); + if cx.tcx.fn_sig(def_id).skip_binder().unsafety == hir::Unsafety::Unsafe { + check_arg(cx, &raw_ptrs, recv); + for arg in args { + check_arg(cx, &raw_ptrs, arg); + } + } + }, + hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => check_arg(cx, &raw_ptrs, ptr), + _ => (), + } + ControlFlow::Continue(()) + }); } } } @@ -70,54 +86,13 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option } } -struct DerefVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - ptrs: HirIdSet, - typeck_results: &'a ty::TypeckResults<'tcx>, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - match expr.kind { - hir::ExprKind::Call(f, args) => { - let ty = self.typeck_results.expr_ty(f); - - if type_is_unsafe_function(self.cx, ty) { - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::MethodCall(_, receiver, args, _) => { - let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap(); - let base_type = self.cx.tcx.type_of(def_id); - - if type_is_unsafe_function(self.cx, base_type) { - self.check_arg(receiver); - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => self.check_arg(ptr), - _ => (), - } - - intravisit::walk_expr(self, expr); - } -} - -impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { - fn check_arg(&self, ptr: &hir::Expr<'_>) { - if let Some(id) = path_to_local(ptr) { - if self.ptrs.contains(&id) { - span_lint( - self.cx, - NOT_UNSAFE_PTR_ARG_DEREF, - ptr.span, - "this public function might dereference a raw pointer but is not marked `unsafe`", - ); - } - } +fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) { + if path_to_local(arg).map_or(false, |id| raw_ptrs.contains(&id)) { + span_lint( + cx, + NOT_UNSAFE_PTR_ARG_DEREF, + arg.span, + "this public function might dereference a raw pointer but is not marked `unsafe`", + ); } } diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs index 5c8d8b8e7552..1e08922a6166 100644 --- a/clippy_lints/src/functions/too_many_arguments.rs +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -59,10 +59,7 @@ fn check_arg_number(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span, cx, TOO_MANY_ARGUMENTS, fn_span, - &format!( - "this function has too many arguments ({}/{})", - args, too_many_arguments_threshold - ), + &format!("this function has too many arguments ({args}/{too_many_arguments_threshold})"), ); } } diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index 54bdea7ea25d..f83f8b40f94b 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -78,10 +78,7 @@ pub(super) fn check_fn( cx, TOO_MANY_LINES, span, - &format!( - "this function has too many lines ({}/{})", - line_count, too_many_lines_threshold - ), + &format!("this function has too many lines ({line_count}/{too_many_lines_threshold})"), ); } } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 11c43247868c..0d6718c168a5 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks}; +use clippy_utils::{ + contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks, +}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -76,15 +78,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind - && let ExprKind::Path(ref then_call_qpath) = then_call.kind - && is_lang_ctor(cx, then_call_qpath, OptionSome) - && let ExprKind::Path(ref qpath) = peel_blocks(els).kind - && is_lang_ctor(cx, qpath, OptionNone) + && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) + && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && !stmts_contains_early_return(then_block.stmts) { let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { - format!("({})", cond_snip) + format!("({cond_snip})") } else { cond_snip.into_owned() }; @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { let mut method_body = if then_block.stmts.is_empty() { arg_snip.into_owned() } else { - format!("{{ /* snippet */ {} }}", arg_snip) + format!("{{ /* snippet */ {arg_snip} }}") }; let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { "then_some" @@ -102,14 +102,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { }; let help = format!( - "consider using `bool::{}` like: `{}.{}({})`", - method_name, cond_snip, method_name, method_body, + "consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`", ); span_lint_and_help( cx, IF_THEN_SOME_ELSE_NONE, expr.span, - &format!("this could be simplified with `bool::{}`", method_name), + &format!("this could be simplified with `bool::{method_name}`"), None, &help, ); diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index a920c3bba2ae..93efe957b1dc 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -5,6 +5,7 @@ use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; @@ -12,7 +13,6 @@ use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; -use rustc_hir_analysis::hir_ty_to_ty; use if_chain::if_chain; @@ -89,8 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { ( generics_suggestion_span, format!( - "<{}{}S: ::std::hash::BuildHasher{}>", - generics_snip, + "<{generics_snip}{}S: ::std::hash::BuildHasher{}>", if generics_snip.is_empty() { "" } else { ", " }, if vis.suggestions.is_empty() { "" @@ -263,8 +262,8 @@ impl<'tcx> ImplicitHasherType<'tcx> { fn type_arguments(&self) -> String { match *self { - ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v), - ImplicitHasherType::HashSet(.., ref t) => format!("{}", t), + ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{k}, {v}"), + ImplicitHasherType::HashSet(.., ref t) => format!("{t}"), } } diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index feec8ec2e23f..946d04eff6f9 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -2,10 +2,11 @@ use clippy_utils::{ diagnostics::span_lint_hir_and_then, get_async_fn_body, is_async_fn, source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}, - visitors::expr_visitor_no_bodies, + visitors::for_each_expr, }; +use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::intravisit::{FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -53,7 +54,7 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) { span, "missing `return` statement", |diag| { - diag.span_suggestion(span, "add `return` as shown", format!("return {}", snip), app); + diag.span_suggestion(span, "add `return` as shown", format!("return {snip}"), app); }, ); } @@ -71,7 +72,7 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp diag.span_suggestion( break_span, "change `break` to `return` as shown", - format!("return {}", snip), + format!("return {snip}"), app, ); }, @@ -152,7 +153,7 @@ fn lint_implicit_returns( ExprKind::Loop(block, ..) => { let mut add_return = false; - expr_visitor_no_bodies(|e| { + let _: Option = for_each_expr(block, |e| { if let ExprKind::Break(dest, sub_expr) = e.kind { if dest.target_id.ok() == Some(expr.hir_id) { if call_site_span.is_none() && e.span.ctxt() == ctxt { @@ -167,9 +168,8 @@ fn lint_implicit_returns( } } } - true - }) - .visit_block(block); + ControlFlow::Continue(()) + }); if add_return { #[expect(clippy::option_if_let_else)] if let Some(span) = call_site_span { diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs new file mode 100644 index 000000000000..bf1351829c6a --- /dev/null +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -0,0 +1,114 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::get_parent_expr; +use clippy_utils::source::snippet_with_applicability; +use if_chain::if_chain; +use rustc_ast::ast::{LitIntType, LitKind}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Int, IntTy, Ty, Uint, UintTy}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for implicit saturating addition. + /// + /// ### Why is this bad? + /// The built-in function is more readable and may be faster. + /// + /// ### Example + /// ```rust + ///let mut u:u32 = 7000; + /// + /// if u != u32::MAX { + /// u += 1; + /// } + /// ``` + /// Use instead: + /// ```rust + ///let mut u:u32 = 7000; + /// + /// u = u.saturating_add(1); + /// ``` + #[clippy::version = "1.65.0"] + pub IMPLICIT_SATURATING_ADD, + style, + "Perform saturating addition instead of implicitly checking max bound of data type" +} +declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]); + +impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if_chain! { + if let ExprKind::If(cond, then, None) = expr.kind; + if let ExprKind::DropTemps(expr1) = cond.kind; + if let Some((c, op_node, l)) = get_const(cx, expr1); + if let BinOpKind::Ne | BinOpKind::Lt = op_node; + if let ExprKind::Block(block, None) = then.kind; + if let Block { + stmts: + [Stmt + { kind: StmtKind::Expr(ex) | StmtKind::Semi(ex), .. }], + expr: None, ..} | + Block { stmts: [], expr: Some(ex), ..} = block; + if let ExprKind::AssignOp(op1, target, value) = ex.kind; + let ty = cx.typeck_results().expr_ty(target); + if Some(c) == get_int_max(ty); + if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target); + if BinOpKind::Add == op1.node; + if let ExprKind::Lit(ref lit) = value.kind; + if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; + if block.expr.is_none(); + then { + let mut app = Applicability::MachineApplicable; + let code = snippet_with_applicability(cx, target.span, "_", &mut app); + let sugg = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind && else_.hir_id == expr.hir_id {format!("{{{code} = {code}.saturating_add(1); }}")} else {format!("{code} = {code}.saturating_add(1);")}; + span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app); + } + } + } +} + +fn get_int_max(ty: Ty<'_>) -> Option { + match ty.peel_refs().kind() { + Int(IntTy::I8) => i8::max_value().try_into().ok(), + Int(IntTy::I16) => i16::max_value().try_into().ok(), + Int(IntTy::I32) => i32::max_value().try_into().ok(), + Int(IntTy::I64) => i64::max_value().try_into().ok(), + Int(IntTy::I128) => i128::max_value().try_into().ok(), + Int(IntTy::Isize) => isize::max_value().try_into().ok(), + Uint(UintTy::U8) => u8::max_value().try_into().ok(), + Uint(UintTy::U16) => u16::max_value().try_into().ok(), + Uint(UintTy::U32) => u32::max_value().try_into().ok(), + Uint(UintTy::U64) => u64::max_value().try_into().ok(), + Uint(UintTy::U128) => Some(u128::max_value()), + Uint(UintTy::Usize) => usize::max_value().try_into().ok(), + _ => None, + } +} + +fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { + if let ExprKind::Binary(op, l, r) = expr.kind { + let tr = cx.typeck_results(); + if let Some((Constant::Int(c), _)) = constant(cx, tr, r) { + return Some((c, op.node, l)); + }; + if let Some((Constant::Int(c), _)) = constant(cx, tr, l) { + return Some((c, invert_op(op.node)?, r)); + } + } + None +} + +fn invert_op(op: BinOpKind) -> Option { + use rustc_hir::BinOpKind::{Ge, Gt, Le, Lt, Ne}; + match op { + Lt => Some(Gt), + Le => Some(Ge), + Ne => Some(Ne), + Ge => Some(Le), + Gt => Some(Lt), + _ => None, + } +} diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 46654bc61e0f..48edbf6ae576 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{higher, peel_blocks_with_stmt, SpanlessEq}; +use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -131,17 +131,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { match peel_blocks_with_stmt(expr).kind { ExprKind::AssignOp(ref op1, target, value) => { - if_chain! { - if BinOpKind::Sub == op1.node; - // Check if literal being subtracted is one - if let ExprKind::Lit(ref lit1) = value.kind; - if let LitKind::Int(1, _) = lit1.node; - then { - Some(target) - } else { - None - } - } + // Check if literal being subtracted is one + (BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target) }, ExprKind::Assign(target, value, _) => { if_chain! { @@ -150,8 +141,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp if SpanlessEq::new(cx).eq_expr(left1, target); - if let ExprKind::Lit(ref lit1) = right1.kind; - if let LitKind::Int(1, _) = lit1.node; + if is_integer_literal(right1, 1); then { Some(target) } else { @@ -170,7 +160,7 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) { expr.span, "implicitly performing saturating subtraction", "try", - format!("{} = {}.saturating_sub({});", var_name, var_name, '1'), + format!("{var_name} = {var_name}.saturating_sub({});", '1'), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 14b22d2b50d0..e2f2d3d42e69 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { let mut fields_snippet = String::new(); let (last_ident, idents) = ordered_fields.split_last().unwrap(); for ident in idents { - let _ = write!(fields_snippet, "{}, ", ident); + let _ = write!(fields_snippet, "{ident}, "); } fields_snippet.push_str(&last_ident.to_string()); @@ -100,10 +100,8 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { String::new() }; - let sugg = format!("{} {{ {}{} }}", + let sugg = format!("{} {{ {fields_snippet}{base_snippet} }}", snippet(cx, qpath.span(), ".."), - fields_snippet, - base_snippet, ); span_lint_and_sugg( diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 0dd7f5bf000d..c7b5badaae51 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -139,14 +139,14 @@ fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) { .map(|(index, _)| *index) .collect::>(); - let value_name = |index| format!("{}_{}", slice.ident.name, index); + let value_name = |index| format!("{}_{index}", slice.ident.name); if let Some(max_index) = used_indices.iter().max() { let opt_ref = if slice.needs_ref { "ref " } else { "" }; let pat_sugg_idents = (0..=*max_index) .map(|index| { if used_indices.contains(&index) { - format!("{}{}", opt_ref, value_name(index)) + format!("{opt_ref}{}", value_name(index)) } else { "_".to_string() } diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 8c2c96fa105a..d1d2db27c6fc 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::higher; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{higher, match_def_path, path_def_id, paths}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -168,9 +168,16 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { }, ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), - ExprKind::Call(path, _) => path_def_id(cx, path) - .map_or(false, |id| match_def_path(cx, id, &paths::ITER_REPEAT)) - .into(), + ExprKind::Call(path, _) => { + if let ExprKind::Path(ref qpath) = path.kind { + cx.qpath_res(qpath, path.hir_id) + .opt_def_id() + .map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id)) + .into() + } else { + Finite + } + }, ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(), _ => Finite, } diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index cb6c2ec0fb98..676136df572b 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method}; +use clippy_utils::{return_ty, trait_ref_of_method}; use if_chain::if_chain; use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -118,7 +118,10 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { } fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { - let display_trait_id = get_trait_def_id(cx, &paths::DISPLAY_TRAIT).expect("Failed to get trait ID of `Display`!"); + let display_trait_id = cx + .tcx + .get_diagnostic_item(sym::Display) + .expect("Failed to get trait ID of `Display`!"); // Get the real type of 'self' let self_type = cx.tcx.fn_sig(item.def_id).input(0); @@ -131,23 +134,19 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { INHERENT_TO_STRING_SHADOW_DISPLAY, item.span, &format!( - "type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`", - self_type + "type `{self_type}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`" ), None, - &format!("remove the inherent method from type `{}`", self_type), + &format!("remove the inherent method from type `{self_type}`"), ); } else { span_lint_and_help( cx, INHERENT_TO_STRING, item.span, - &format!( - "implementation of inherent method `to_string(&self) -> String` for type `{}`", - self_type - ), + &format!("implementation of inherent method `to_string(&self) -> String` for type `{self_type}`"), None, - &format!("implement trait `Display` for type `{}` instead", self_type), + &format!("implement trait `Display` for type `{self_type}` instead"), ); } } diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index dd7177e0131c..d609a5ca4d46 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -51,7 +51,7 @@ fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) { cx, INLINE_FN_WITHOUT_BODY, attr.span, - &format!("use of `#[inline]` on trait method `{}` which has no body", name), + &format!("use of `#[inline]` on trait method `{name}` which has no body"), |diag| { diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable); }, diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 9a944def3eb2..33491da3fc5a 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -138,8 +138,8 @@ impl IntPlusOne { if let Some(snippet) = snippet_opt(cx, node.span) { if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) { let rec = match side { - Side::Lhs => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)), - Side::Rhs => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)), + Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")), + Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")), }; return rec; } diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 2027c23d328c..ea9f046fb973 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -80,10 +80,7 @@ fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefI cx, ITER_NOT_RETURNING_ITERATOR, sig.span, - &format!( - "this method is named `{}` but its return type does not implement `Iterator`", - name - ), + &format!("this method is named `{name}` but its return type does not implement `Iterator`"), ); } } diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index d6eb53ae29b5..76c83ab47d09 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ConstKind}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{BytePos, Pos, Span}; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 7d15dd4cb216..3a563736fb07 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -210,7 +210,8 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } } - if cx.access_levels.is_exported(visited_trait.def_id.def_id) && trait_items.iter().any(|i| is_named_self(cx, i, sym::len)) + if cx.access_levels.is_exported(visited_trait.def_id.def_id) + && trait_items.iter().any(|i| is_named_self(cx, i, sym::len)) { let mut current_and_super_traits = DefIdSet::default(); fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx); @@ -278,15 +279,13 @@ impl<'tcx> LenOutput<'tcx> { _ => "", }; match self { - Self::Integral => format!("expected signature: `({}self) -> bool`", self_ref), - Self::Option(_) => format!( - "expected signature: `({}self) -> bool` or `({}self) -> Option", - self_ref, self_ref - ), - Self::Result(..) => format!( - "expected signature: `({}self) -> bool` or `({}self) -> Result", - self_ref, self_ref - ), + Self::Integral => format!("expected signature: `({self_ref}self) -> bool`"), + Self::Option(_) => { + format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option") + }, + Self::Result(..) => { + format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result") + }, } } } @@ -326,8 +325,7 @@ fn check_for_is_empty<'tcx>( let (msg, is_empty_span, self_kind) = match is_empty { None => ( format!( - "{} `{}` has a public `len` method, but no `is_empty` method", - item_kind, + "{item_kind} `{}` has a public `len` method, but no `is_empty` method", item_name.as_str(), ), None, @@ -335,8 +333,7 @@ fn check_for_is_empty<'tcx>( ), Some(is_empty) if !cx.access_levels.is_exported(is_empty.def_id.expect_local()) => ( format!( - "{} `{}` has a public `len` method, but a private `is_empty` method", - item_kind, + "{item_kind} `{}` has a public `len` method, but a private `is_empty` method", item_name.as_str(), ), Some(cx.tcx.def_span(is_empty.def_id)), @@ -348,8 +345,7 @@ fn check_for_is_empty<'tcx>( { ( format!( - "{} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature", - item_kind, + "{item_kind} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature", item_name.as_str(), ), Some(cx.tcx.def_span(is_empty.def_id)), @@ -419,10 +415,9 @@ fn check_len( LEN_ZERO, span, &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), - &format!("using `{}is_empty` is clearer and more explicit", op), + &format!("using `{op}is_empty` is clearer and more explicit"), format!( - "{}{}.is_empty()", - op, + "{op}{}.is_empty()", snippet_with_applicability(cx, receiver.span, "_", &mut applicability) ), applicability, @@ -439,10 +434,9 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex COMPARISON_TO_EMPTY, span, "comparison to empty slice", - &format!("using `{}is_empty` is clearer and more explicit", op), + &format!("using `{op}is_empty` is clearer and more explicit"), format!( - "{}{}.is_empty()", - op, + "{op}{}.is_empty()", snippet_with_applicability(cx, lit1.span, "_", &mut applicability) ), applicability, diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 10fc0f4018ef..13071d64441a 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -106,8 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { // use mutably after the `if` let sug = format!( - "let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", - mut=mutability, + "let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", name=ident.name, cond=snippet(cx, cond.span, "_"), then=if then.stmts.len() > 1 { " ..;" } else { "" }, diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 8718d5fa1da0..5d26e4b33601 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -21,6 +21,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), LintId::of(borrow_deref_ref::BORROW_DEREF_REF), + LintId::of(box_default::BOX_DEFAULT), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), @@ -44,7 +45,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), + LintId::of(disallowed_macros::DISALLOWED_MACROS), LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_names::DISALLOWED_NAMES), LintId::of(disallowed_types::DISALLOWED_TYPES), @@ -85,6 +86,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(functions::RESULT_UNIT_ERR), LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(if_let_mutex::IF_LET_MUTEX), + LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(infinite_iter::INFINITE_ITER), LintId::of(inherent_to_string::INHERENT_TO_STRING), @@ -125,6 +127,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_bits::MANUAL_BITS), + LintId::of(manual_clamp::MANUAL_CLAMP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), LintId::of(manual_retain::MANUAL_RETAIN), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 185189a6af5b..a58d066fa6b6 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -22,6 +22,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(loops::MANUAL_FLATTEN), LintId::of(loops::SINGLE_ELEMENT_LOOP), LintId::of(loops::WHILE_LET_LOOP), + LintId::of(manual_clamp::MANUAL_CLAMP), LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), LintId::of(manual_strip::MANUAL_STRIP), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs index be63646a12f5..71dfdab369b9 100644 --- a/clippy_lints/src/lib.register_internal.rs +++ b/clippy_lints/src/lib.register_internal.rs @@ -13,10 +13,10 @@ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE), LintId::of(utils::internal_lints::INVALID_PATHS), LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE), LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL), LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), LintId::of(utils::internal_lints::PRODUCE_ICE), + LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH), LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), ]) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 02fcc8de5072..05d927dbea79 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -24,8 +24,6 @@ store.register_lints(&[ #[cfg(feature = "internal")] utils::internal_lints::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal")] - utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - #[cfg(feature = "internal")] utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, #[cfg(feature = "internal")] utils::internal_lints::MISSING_MSRV_ATTR_IMPL, @@ -34,6 +32,8 @@ store.register_lints(&[ #[cfg(feature = "internal")] utils::internal_lints::PRODUCE_ICE, #[cfg(feature = "internal")] + utils::internal_lints::UNNECESSARY_DEF_PATH, + #[cfg(feature = "internal")] utils::internal_lints::UNNECESSARY_SYMBOL_STR, almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, @@ -60,6 +60,7 @@ store.register_lints(&[ booleans::NONMINIMAL_BOOL, booleans::OVERLY_COMPLEX_BOOL_EXPR, borrow_deref_ref::BORROW_DEREF_REF, + box_default::BOX_DEFAULT, cargo::CARGO_COMMON_METADATA, cargo::MULTIPLE_CRATE_VERSIONS, cargo::NEGATIVE_FEATURE_NAMES, @@ -113,16 +114,17 @@ store.register_lints(&[ derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ, derive::EXPL_IMPL_CLONE_ON_COPY, derive::UNSAFE_DERIVE_DESERIALIZE, + disallowed_macros::DISALLOWED_MACROS, disallowed_methods::DISALLOWED_METHODS, disallowed_names::DISALLOWED_NAMES, disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS, disallowed_types::DISALLOWED_TYPES, + doc::DOC_LINK_WITH_QUOTES, doc::DOC_MARKDOWN, doc::MISSING_ERRORS_DOC, doc::MISSING_PANICS_DOC, doc::MISSING_SAFETY_DOC, doc::NEEDLESS_DOCTEST_MAIN, - doc_link_with_quotes::DOC_LINK_WITH_QUOTES, double_parens::DOUBLE_PARENS, drop_forget_ref::DROP_COPY, drop_forget_ref::DROP_NON_DROP, @@ -159,6 +161,7 @@ store.register_lints(&[ format::USELESS_FORMAT, format_args::FORMAT_IN_FORMAT_ARGS, format_args::TO_STRING_IN_FORMAT_ARGS, + format_args::UNINLINED_FORMAT_ARGS, format_impl::PRINT_IN_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL, format_push_string::FORMAT_PUSH_STRING, @@ -182,6 +185,7 @@ store.register_lints(&[ if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, implicit_hasher::IMPLICIT_HASHER, implicit_return::IMPLICIT_RETURN, + implicit_saturating_add::IMPLICIT_SATURATING_ADD, implicit_saturating_sub::IMPLICIT_SATURATING_SUB, inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, index_refutable_slice::INDEX_REFUTABLE_SLICE, @@ -243,6 +247,7 @@ store.register_lints(&[ manual_assert::MANUAL_ASSERT, manual_async_fn::MANUAL_ASYNC_FN, manual_bits::MANUAL_BITS, + manual_clamp::MANUAL_CLAMP, manual_instant_elapsed::MANUAL_INSTANT_ELAPSED, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_rem_euclid::MANUAL_REM_EUCLID, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index f1783dd9ddef..e0b4639af53e 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -6,6 +6,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), + LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), LintId::of(equatable_if_let::EQUATABLE_IF_LET), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 584ccf55e511..bc2f0beb358a 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -20,15 +20,16 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(dereference::REF_BINDING_TO_REFERENCE), LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), + LintId::of(doc::DOC_LINK_WITH_QUOTES), LintId::of(doc::DOC_MARKDOWN), LintId::of(doc::MISSING_ERRORS_DOC), LintId::of(doc::MISSING_PANICS_DOC), - LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES), LintId::of(empty_enum::EMPTY_ENUM), LintId::of(enum_variants::MODULE_NAME_REPETITIONS), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(functions::MUST_USE_CANDIDATE), LintId::of(functions::TOO_MANY_LINES), LintId::of(if_not_else::IF_NOT_ELSE), diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index 195ce41e31e9..8e927470e02f 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -3,6 +3,7 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ + LintId::of(box_default::BOX_DEFAULT), LintId::of(entry::MAP_ENTRY), LintId::of(escape::BOXED_LOCAL), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 05d2ec2e9e1e..8e1390167dc8 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -15,7 +15,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY), LintId::of(dereference::NEEDLESS_BORROW), - LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), + LintId::of(disallowed_macros::DISALLOWED_MACROS), LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_names::DISALLOWED_NAMES), LintId::of(disallowed_types::DISALLOWED_TYPES), @@ -30,6 +30,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(functions::DOUBLE_MUST_USE), LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), + LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(len_zero::COMPARISON_TO_EMPTY), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c3db194c4ad8..2dcefd78763b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -31,6 +31,7 @@ extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; +extern crate rustc_hir_analysis; extern crate rustc_hir_pretty; extern crate rustc_index; extern crate rustc_infer; @@ -43,7 +44,6 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; extern crate rustc_trait_selection; -extern crate rustc_hir_analysis; #[macro_use] extern crate clippy_utils; @@ -180,6 +180,7 @@ mod bool_assert_comparison; mod bool_to_int_with_if; mod booleans; mod borrow_deref_ref; +mod box_default; mod cargo; mod casts; mod checked_conversions; @@ -198,12 +199,12 @@ mod default_union_representation; mod dereference; mod derivable_impls; mod derive; +mod disallowed_macros; mod disallowed_methods; mod disallowed_names; mod disallowed_script_idents; mod disallowed_types; mod doc; -mod doc_link_with_quotes; mod double_parens; mod drop_forget_ref; mod duplicate_mod; @@ -238,6 +239,7 @@ mod if_not_else; mod if_then_some_else_none; mod implicit_hasher; mod implicit_return; +mod implicit_saturating_add; mod implicit_saturating_sub; mod inconsistent_struct_constructor; mod index_refutable_slice; @@ -267,6 +269,7 @@ mod main_recursion; mod manual_assert; mod manual_async_fn; mod manual_bits; +mod manual_clamp; mod manual_instant_elapsed; mod manual_non_exhaustive; mod manual_rem_euclid; @@ -416,8 +419,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { sess.err(&format!( - "error reading Clippy's configuration file. `{}` is not a valid Rust version", - s + "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None }) @@ -433,8 +435,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { let clippy_msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { sess.err(&format!( - "error reading Clippy's configuration file. `{}` is not a valid Rust version", - s + "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None }) @@ -445,8 +446,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { // if both files have an msrv, let's compare them and emit a warning if they differ if clippy_msrv != cargo_msrv { sess.warn(&format!( - "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{}` from `clippy.toml`", - clippy_msrv + "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" )); } @@ -465,7 +465,7 @@ pub fn read_conf(sess: &Session) -> Conf { Ok(Some(path)) => path, Ok(None) => return Conf::default(), Err(error) => { - sess.struct_err(&format!("error finding Clippy's configuration file: {}", error)) + sess.struct_err(&format!("error finding Clippy's configuration file: {error}")) .emit(); return Conf::default(); }, @@ -535,9 +535,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new())); store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle)); store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths)); - store.register_late_pass(|_| Box::new(utils::internal_lints::InterningDefinedSymbol::default())); - store.register_late_pass(|_| Box::new(utils::internal_lints::LintWithoutLintPass::default())); - store.register_late_pass(|_| Box::new(utils::internal_lints::MatchTypeOnDiagItem)); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath)); store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass)); store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl)); } @@ -629,10 +629,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: msrv, )) }); - store.register_late_pass(|_| Box::new(shadow::Shadow::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); store.register_late_pass(|_| Box::new(loops::Loops)); - store.register_late_pass(|_| Box::new(main_recursion::MainRecursion::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); store.register_late_pass(|_| Box::new(entry::HashMapPass)); store.register_late_pass(|_| Box::new(minmax::MinMaxPass)); @@ -666,7 +666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(format::UselessFormat)); store.register_late_pass(|_| Box::new(swap::Swap)); store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional)); - store.register_late_pass(|_| Box::new(new_without_default::NewWithoutDefault::default())); + store.register_late_pass(|_| Box::::default()); let disallowed_names = conf.disallowed_names.iter().cloned().collect::>(); store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone()))); let too_many_arguments_threshold = conf.too_many_arguments_threshold; @@ -705,7 +705,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef)); store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter)); store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)); - store.register_late_pass(|_| Box::new(useless_conversion::UselessConversion::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher)); store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom)); store.register_late_pass(|_| Box::new(question_mark::QuestionMark)); @@ -775,7 +775,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: upper_case_acronyms_aggressive, )) }); - store.register_late_pass(|_| Box::new(default::Default::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api))); store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(exit::Exit)); @@ -798,7 +798,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports; store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); - store.register_late_pass(|_| Box::new(redundant_pub_crate::RedundantPubCrate::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv))); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); @@ -816,11 +816,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::>(); store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher))); - store.register_late_pass(|_| Box::new(macro_use::MacroUseImports::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); + let disallowed_macros = conf.disallowed_macros.clone(); + store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone()))); let disallowed_methods = conf.disallowed_methods.clone(); store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); @@ -829,7 +831,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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)); - store.register_late_pass(|_| Box::new(vec_init_then_push::VecInitThenPush::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); @@ -857,7 +859,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); - store.register_late_pass(move |_| Box::new(format_args::FormatArgs)); + store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv))); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); @@ -866,8 +868,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); - store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes)); - store.register_late_pass(|_| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default())); + store.register_late_pass(|_| Box::::default()); let allow_dbg_in_tests = conf.allow_dbg_in_tests; store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; @@ -876,7 +877,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ignore_publish: cargo_ignore_publish, }) }); - store.register_late_pass(|_| Box::new(write::Write::default())); + store.register_late_pass(|_| Box::::default()); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); @@ -886,7 +887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); - store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); + store.register_early_pass(|| Box::::default()); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); @@ -898,13 +899,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); - store.register_late_pass(|_| Box::new(std_instead_of_core::StdReexports::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed)); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); + store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)); + store.register_late_pass(|_| Box::new(box_default::BoxDefault)); + store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 399a03187d99..aef253303a8f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -9,8 +9,8 @@ use rustc_hir::intravisit::{ use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, - TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, TraitFn, + TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter as middle_nested_filter; @@ -276,7 +276,7 @@ fn could_use_elision<'tcx>( let mut checker = BodyLifetimeChecker { lifetimes_used_in_body: false, }; - checker.visit_expr(&body.value); + checker.visit_expr(body.value); if checker.lifetimes_used_in_body { return false; } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index fb2104861c87..25f19b9c6e6c 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -478,7 +478,7 @@ impl DecimalLiteralRepresentation { if num_lit.radix == Radix::Decimal; if val >= u128::from(self.threshold); then { - let hex = format!("{:#X}", val); + let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| { warning_type.display(num_lit.format(), cx, lit.span); diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 8e3ab26a947f..14f223481327 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -44,11 +44,10 @@ pub(super) fn check<'tcx>( cx, EXPLICIT_COUNTER_LOOP, span, - &format!("the variable `{}` is used as a loop counter", name), + &format!("the variable `{name}` is used as a loop counter"), "consider using", format!( - "for ({}, {}) in {}.enumerate()", - name, + "for ({name}, {}) in {}.enumerate()", snippet_with_applicability(cx, pat.span, "item", &mut applicability), make_iterator_snippet(cx, arg, &mut applicability), ), @@ -65,24 +64,21 @@ pub(super) fn check<'tcx>( cx, EXPLICIT_COUNTER_LOOP, span, - &format!("the variable `{}` is used as a loop counter", name), + &format!("the variable `{name}` is used as a loop counter"), |diag| { diag.span_suggestion( span, "consider using", format!( - "for ({}, {}) in (0_{}..).zip({})", - name, + "for ({name}, {}) in (0_{int_name}..).zip({})", snippet_with_applicability(cx, pat.span, "item", &mut applicability), - int_name, make_iterator_snippet(cx, arg, &mut applicability), ), applicability, ); diag.note(&format!( - "`{}` is of type `{}`, making it ineligible for `Iterator::enumerate`", - name, int_name + "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" )); }, ); diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 5f5beccd030c..b1f2941622ab 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, m "it is more concise to loop over references to containers instead of using explicit \ iteration methods", "to write this more concisely, try", - format!("&{}{}", muta, object), + format!("&{muta}{object}"), applicability, ); } diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index bee0e1d76831..ed620460dbe6 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx cx, FOR_KV_MAP, arg_span, - &format!("you seem to want to iterate on a map's {}s", kind), + &format!("you seem to want to iterate on a map's {kind}s"), |diag| { let map = sugg::Sugg::hir(cx, arg, "map"); multispan_sugg( @@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx "use the corresponding method", vec![ (pat_span, snippet(cx, new_pat_span, kind).into_owned()), - (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)), + (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_par())), ], ); }, diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index 09b2376d5c04..4bb9936e9cde 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -1,7 +1,7 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FIND; use clippy_utils::{ - diagnostics::span_lint_and_then, higher, is_lang_ctor, path_res, peel_blocks_with_stmt, + diagnostics::span_lint_and_then, higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt, source::snippet_with_applicability, ty::implements_trait, }; use if_chain::if_chain; @@ -30,8 +30,8 @@ pub(super) fn check<'tcx>( if let [stmt] = block.stmts; if let StmtKind::Semi(semi) = stmt.kind; if let ExprKind::Ret(Some(ret_value)) = semi.kind; - if let ExprKind::Call(Expr { kind: ExprKind::Path(ctor), .. }, [inner_ret]) = ret_value.kind; - if is_lang_ctor(cx, ctor, LangItem::OptionSome); + if let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind; + if is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome); if path_res(cx, inner_ret) == Res::Local(binding_id); if let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr); then { @@ -143,8 +143,7 @@ fn last_stmt_and_ret<'tcx>( if let Some((_, Node::Block(block))) = parent_iter.next(); if let Some((last_stmt, last_ret)) = extract(block); if last_stmt.hir_id == node_hir; - if let ExprKind::Path(path) = &last_ret.kind; - if is_lang_ctor(cx, path, LangItem::OptionNone); + if is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone); if let Some((_, Node::Expr(_block))) = parent_iter.next(); // This includes the function header if let Some((_, func)) = parent_iter.next(); diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 1d6ddf4b99f7..8c27c09404b1 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -3,13 +3,13 @@ use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_lang_ctor, path_to_local_id, peel_blocks_with_stmt}; +use clippy_utils::{path_to_local_id, peel_blocks_with_stmt}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionSome, ResultOk}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, Pat, PatKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, DefIdTree}; use rustc_span::source_map::Span; /// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the @@ -30,15 +30,17 @@ pub(super) fn check<'tcx>( if path_to_local_id(let_expr, pat_hir_id); // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind; - let some_ctor = is_lang_ctor(cx, qpath, OptionSome); - let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id); + if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); + let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id); + let ok_ctor = cx.tcx.lang_items().result_ok_variant() == Some(variant_id); if some_ctor || ok_ctor; // Ensure expr in `if let` is not used afterwards if !is_local_used(cx, if_then, pat_hir_id); then { let if_let_type = if some_ctor { "Some" } else { "Ok" }; // Prepare the error message - let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type); + let msg = format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used"); // Prepare the help message let mut applicability = Applicability::MaybeIncorrect; diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 3fc569af89ec..c87fc4f90e21 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -177,13 +177,7 @@ fn build_manual_memcpy_suggestion<'tcx>( let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY { dst_base_str } else { - format!( - "{}[{}..{}]", - dst_base_str, - dst_offset.maybe_par(), - dst_limit.maybe_par() - ) - .into() + format!("{dst_base_str}[{}..{}]", dst_offset.maybe_par(), dst_limit.maybe_par()).into() }; let method_str = if is_copy(cx, elem_ty) { @@ -193,10 +187,7 @@ fn build_manual_memcpy_suggestion<'tcx>( }; format!( - "{}.{}(&{}[{}..{}]);", - dst, - method_str, - src_base_str, + "{dst}.{method_str}(&{src_base_str}[{}..{}]);", src_offset.maybe_par(), src_limit.maybe_par() ) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 74f3bda9f43e..c0a0444485e3 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -635,7 +635,7 @@ declare_clippy_lint! { /// arr.into_iter().find(|&el| el == 1) /// } /// ``` - #[clippy::version = "1.61.0"] + #[clippy::version = "1.64.0"] pub MANUAL_FIND, complexity, "manual implementation of `Iterator::find`" diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 6d585c2e45de..0ee42b61c9a5 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -4,11 +4,11 @@ use clippy_utils::{get_enclosing_block, higher, path_to_local}; use if_chain::if_chain; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::{mir::FakeReadCause, ty}; use rustc_span::source_map::Span; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { if_chain! { @@ -114,7 +114,13 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { } } - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } impl MutatePairDelegate<'_, '_> { diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 6e6faa79adc9..66f9e28596e8 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -45,7 +45,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont let (arg, pred) = contains_arg .strip_prefix('&') .map_or(("&x", &*contains_arg), |s| ("x", s)); - format!("any(|{}| x == {})", arg, pred) + format!("any(|{arg}| x == {pred})") } _ => return, } @@ -141,9 +141,9 @@ impl IterFunction { IterFunctionKind::Contains(span) => { let s = snippet(cx, *span, ".."); if let Some(stripped) = s.strip_prefix('&') { - format!(".any(|x| x == {})", stripped) + format!(".any(|x| x == {stripped})") } else { - format!(".any(|x| x == *{})", s) + format!(".any(|x| x == *{s})") } }, } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 8ab640051b63..00cfc6d49f19 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -145,7 +145,7 @@ pub(super) fn check<'tcx>( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed), + &format!("the loop variable `{}` is used to index `{indexed}`", ident.name), |diag| { multispan_sugg( diag, @@ -154,7 +154,7 @@ pub(super) fn check<'tcx>( (pat.span, format!("({}, )", ident.name)), ( arg.span, - format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2), + format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), ), ], ); @@ -162,16 +162,16 @@ pub(super) fn check<'tcx>( ); } else { let repl = if starts_at_zero && take_is_empty { - format!("&{}{}", ref_mut, indexed) + format!("&{ref_mut}{indexed}") } else { - format!("{}.{}(){}{}", indexed, method, method_1, method_2) + format!("{indexed}.{method}(){method_1}{method_2}") }; span_lint_and_then( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed), + &format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), |diag| { multispan_sugg( diag, diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 116e589cad6f..16b00ad66378 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -42,6 +42,7 @@ pub(super) fn check( } } +#[derive(Copy, Clone)] enum NeverLoopResult { // A break/return always get triggered but not necessarily for the main loop. AlwaysBreak, @@ -51,8 +52,8 @@ enum NeverLoopResult { } #[must_use] -fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult { - match *arg { +fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult { + match arg { NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise, NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, } @@ -92,19 +93,29 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult } fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { - let mut iter = block.stmts.iter().filter_map(stmt_to_expr).chain(block.expr); + let mut iter = block + .stmts + .iter() + .filter_map(stmt_to_expr) + .chain(block.expr.map(|expr| (expr, None))); never_loop_expr_seq(&mut iter, main_loop_id) } -fn never_loop_expr_seq<'a, T: Iterator>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { - es.map(|e| never_loop_expr(e, main_loop_id)) - .fold(NeverLoopResult::Otherwise, combine_seq) +fn never_loop_expr_seq<'a, T: Iterator, Option<&'a Block<'a>>)>>( + es: &mut T, + main_loop_id: HirId, +) -> NeverLoopResult { + es.map(|(e, els)| { + let e = never_loop_expr(e, main_loop_id); + els.map_or(e, |els| combine_branches(e, never_loop_block(els, main_loop_id))) + }) + .fold(NeverLoopResult::Otherwise, combine_seq) } -fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { +fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> { match stmt.kind { - StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e), - StmtKind::Local(local) => local.init, + StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)), + StmtKind::Local(local) => local.init.map(|init| (init, local.els)), StmtKind::Item(..) => None, } } @@ -139,7 +150,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id), ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. - absorb_break(&never_loop_block(b, main_loop_id)) + absorb_break(never_loop_block(b, main_loop_id)) }, ExprKind::If(e, e2, e3) => { let e1 = never_loop_expr(e, main_loop_id); @@ -211,9 +222,5 @@ fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) let pat_snippet = snippet(cx, pat.span, "_"); let iter_snippet = make_iterator_snippet(cx, iterator, &mut Applicability::Unspecified); - format!( - "if let Some({pat}) = {iter}.next()", - pat = pat_snippet, - iter = iter_snippet - ) + format!("if let Some({pat_snippet}) = {iter_snippet}.next()") } diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index aeefe6e33fbe..07edee46fa65 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -30,10 +30,7 @@ pub(super) fn check<'tcx>( vec.span, "it looks like the same item is being pushed into this Vec", None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), + &format!("try using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), ); } diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index f1f58db80b30..b6f4cf7bbb37 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -5,12 +5,12 @@ use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Symbol}; -use rustc_hir_analysis::hir_ty_to_ty; use std::iter::Iterator; #[derive(Debug, PartialEq, Eq)] @@ -344,9 +344,8 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic _ => arg, }; format!( - "{}.{}()", + "{}.{method_name}()", sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_par(), - method_name, ) }, _ => format!( diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index deb21894f36a..153f97e4e66c 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -3,13 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{ - get_enclosing_loop_or_multi_call_closure, is_refutable, is_trait_method, match_def_path, paths, - visitors::is_res_used, + get_enclosing_loop_or_multi_call_closure, is_refutable, is_res_lang_ctor, is_trait_method, visitors::is_res_used, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; +use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty::adjustment::Adjust; @@ -19,9 +18,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr); // check for `Some(..)` pattern - if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind; - if let Res::Def(_, pat_did) = pat_path.res; - if match_def_path(cx, pat_did, &paths::OPTION_SOME); + if let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind; + if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome); // check for call to `Iterator::next` if let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind; if method_name.ident.name == sym::next; @@ -67,7 +65,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { expr.span.with_hi(scrutinee_expr.span.hi()), "this loop could be written as a `for` loop", "try", - format!("for {} in {}{}", loop_var, iterator, by_ref), + format!("for {loop_var} in {iterator}{by_ref}"), applicability, ); } diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index d573a1b4fbb5..594f6af76b3d 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -189,9 +189,9 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { let mut suggestions = vec![]; for ((root, span, hir_id), path) in used { if path.len() == 1 { - suggestions.push((span, format!("{}::{}", root, path[0]), hir_id)); + suggestions.push((span, format!("{root}::{}", path[0]), hir_id)); } else { - suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")), hir_id)); + suggestions.push((span, format!("{root}::{{{}}}", path.join(", ")), hir_id)); } } @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { // such as `std::prelude::v1::foo` or some other macro that expands to an import. if self.mac_refs.is_empty() { for (span, import, hir_id) in suggestions { - let help = format!("use {};", import); + let help = format!("use {import};"); span_lint_hir_and_then( cx, MACRO_USE_IMPORTS, diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 26b53ab5d683..825ec84b4a81 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,7 +1,8 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use crate::rustc_lint::LintContext; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{peel_blocks_with_stmt, sugg}; +use clippy_utils::{peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -50,20 +51,36 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { let mut applicability = Applicability::MachineApplicable; let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); let cond = cond.peel_drop_temps(); + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); + if !comments.is_empty() { + comments += "\n"; + } let (cond, not) = match cond.kind { ExprKind::Unary(UnOp::Not, e) => (e, ""), _ => (cond, "!"), }; let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); - span_lint_and_sugg( + // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block + span_lint_and_then( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", - "try", - sugg, - Applicability::MachineApplicable, + |diag| { + // comments can be noisy, do not show them to the user + diag.tool_only_span_suggestion( + expr.span.shrink_to_lo(), + "add comments back", + comments, + applicability); + diag.span_suggestion( + expr.span, + "try instead", + sugg, + applicability); + } + ); } } diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 754b0e78a148..9a0a26c0991b 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -74,11 +74,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { if let Some(ret_pos) = position_before_rarrow(&header_snip); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { - let help = format!("make the function `async` and {}", ret_sugg); + let help = format!("make the function `async` and {ret_sugg}"); diag.span_suggestion( header_span, &help, - format!("async {}{}", &header_snip[..ret_pos], ret_snip), + format!("async {}{ret_snip}", &header_snip[..ret_pos]), Applicability::MachineApplicable ); @@ -196,7 +196,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, }, _ => { let sugg = "return the output of the future directly"; - snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {}", snip))) + snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {snip}"))) }, } } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs new file mode 100644 index 000000000000..ece4df95505c --- /dev/null +++ b/clippy_lints/src/manual_clamp.rs @@ -0,0 +1,713 @@ +use itertools::Itertools; +use rustc_errors::Diagnostic; +use rustc_hir::{ + def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::sym, Span}; +use std::ops::Deref; + +use clippy_utils::{ + diagnostics::{span_lint_and_then, span_lint_hir_and_then}, + eq_expr_value, get_trait_def_id, + higher::If, + is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, paths, peel_blocks, + peel_blocks_with_stmt, + sugg::Sugg, + ty::implements_trait, + visitors::is_const_evaluatable, + MaybePath, +}; +use rustc_errors::Applicability; + +declare_clippy_lint! { + /// ### What it does + /// Identifies good opportunities for a clamp function from std or core, and suggests using it. + /// + /// ### Why is this bad? + /// clamp is much shorter, easier to read, and doesn't use any control flow. + /// + /// ### Known issue(s) + /// If the clamped variable is NaN this suggestion will cause the code to propagate NaN + /// rather than returning either `max` or `min`. + /// + /// `clamp` functions will panic if `max < min`, `max.is_nan()`, or `min.is_nan()`. + /// Some may consider panicking in these situations to be desirable, but it also may + /// introduce panicking where there wasn't any before. + /// + /// ### Examples + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// if input > max { + /// max + /// } else if input < min { + /// min + /// } else { + /// input + /// } + /// # ; + /// ``` + /// + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// input.max(min).min(max) + /// # ; + /// ``` + /// + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// match input { + /// x if x > max => max, + /// x if x < min => min, + /// x => x, + /// } + /// # ; + /// ``` + /// + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// let mut x = input; + /// if x < min { x = min; } + /// if x > max { x = max; } + /// ``` + /// Use instead: + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// input.clamp(min, max) + /// # ; + /// ``` + #[clippy::version = "1.66.0"] + pub MANUAL_CLAMP, + complexity, + "using a clamp pattern instead of the clamp function" +} +impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]); + +pub struct ManualClamp { + msrv: Option, +} + +impl ManualClamp { + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +#[derive(Debug)] +struct ClampSuggestion<'tcx> { + params: InputMinMax<'tcx>, + span: Span, + make_assignment: Option<&'tcx Expr<'tcx>>, + hir_with_ignore_attr: Option, +} + +#[derive(Debug)] +struct InputMinMax<'tcx> { + input: &'tcx Expr<'tcx>, + min: &'tcx Expr<'tcx>, + max: &'tcx Expr<'tcx>, + is_float: bool, +} + +impl<'tcx> LateLintPass<'tcx> for ManualClamp { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if !meets_msrv(self.msrv, msrvs::CLAMP) { + return; + } + if !expr.span.from_expansion() { + let suggestion = is_if_elseif_else_pattern(cx, expr) + .or_else(|| is_max_min_pattern(cx, expr)) + .or_else(|| is_call_max_min_pattern(cx, expr)) + .or_else(|| is_match_pattern(cx, expr)) + .or_else(|| is_if_elseif_pattern(cx, expr)); + if let Some(suggestion) = suggestion { + emit_suggestion(cx, &suggestion); + } + } + } + + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if !meets_msrv(self.msrv, msrvs::CLAMP) { + return; + } + for suggestion in is_two_if_pattern(cx, block) { + emit_suggestion(cx, &suggestion); + } + } + extract_msrv_attr!(LateContext); +} + +fn emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggestion<'tcx>) { + let ClampSuggestion { + params: InputMinMax { + input, + min, + max, + is_float, + }, + span, + make_assignment, + hir_with_ignore_attr, + } = suggestion; + let input = Sugg::hir(cx, input, "..").maybe_par(); + let min = Sugg::hir(cx, min, ".."); + let max = Sugg::hir(cx, max, ".."); + let semicolon = if make_assignment.is_some() { ";" } else { "" }; + let assignment = if let Some(assignment) = make_assignment { + let assignment = Sugg::hir(cx, assignment, ".."); + format!("{assignment} = ") + } else { + String::new() + }; + let suggestion = format!("{assignment}{input}.clamp({min}, {max}){semicolon}"); + let msg = "clamp-like pattern without using clamp function"; + let lint_builder = |d: &mut Diagnostic| { + d.span_suggestion(*span, "replace with clamp", suggestion, Applicability::MaybeIncorrect); + if *is_float { + d.note("clamp will panic if max < min, min.is_nan(), or max.is_nan()") + .note("clamp returns NaN if the input is NaN"); + } else { + d.note("clamp will panic if max < min"); + } + }; + if let Some(hir_id) = hir_with_ignore_attr { + span_lint_hir_and_then(cx, MANUAL_CLAMP, *hir_id, *span, msg, lint_builder); + } else { + span_lint_and_then(cx, MANUAL_CLAMP, *span, msg, lint_builder); + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum TypeClampability { + Float, + Ord, +} + +impl TypeClampability { + fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option { + if ty.is_floating_point() { + Some(TypeClampability::Float) + } else if get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) { + Some(TypeClampability::Ord) + } else { + None + } + } + + fn is_float(self) -> bool { + matches!(self, TypeClampability::Float) + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min, max) = (0, -3, 12); +/// +/// if input < min { +/// min +/// } else if input > max { +/// max +/// } else { +/// input +/// } +/// # ; +/// ``` +fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + if let Some(If { + cond, + then, + r#else: Some(else_if), + }) = If::hir(expr) + && let Some(If { + cond: else_if_cond, + then: else_if_then, + r#else: Some(else_body), + }) = If::hir(peel_blocks(else_if)) + { + let params = is_clamp_meta_pattern( + cx, + &BinaryOp::new(peel_blocks(cond))?, + &BinaryOp::new(peel_blocks(else_if_cond))?, + peel_blocks(then), + peel_blocks(else_if_then), + None, + )?; + // Contents of the else should be the resolved input. + if !eq_expr_value(cx, params.input, peel_blocks(else_body)) { + return None; + } + Some(ClampSuggestion { + params, + span: expr.span, + make_assignment: None, + hir_with_ignore_attr: None, + }) + } else { + None + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min_value, max_value) = (0, -3, 12); +/// +/// input.max(min_value).min(max_value) +/// # ; +/// ``` +fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = &expr.kind + && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord)) + && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind + && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || is_trait_method(cx, receiver, sym::Ord)) + { + let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); + let (min, max) = match (seg_first.ident.as_str(), seg_second.ident.as_str()) { + ("min", "max") => (arg_second, arg_first), + ("max", "min") => (arg_first, arg_second), + _ => return None, + }; + Some(ClampSuggestion { + params: InputMinMax { input, min, max, is_float }, + span: expr.span, + make_assignment: None, + hir_with_ignore_attr: None, + }) + } else { + None + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min_value, max_value) = (0, -3, 12); +/// # use std::cmp::{max, min}; +/// min(max(input, min_value), max_value) +/// # ; +/// ``` +fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + fn segment<'tcx>(cx: &LateContext<'_>, func: &Expr<'tcx>) -> Option> { + match func.kind { + ExprKind::Path(QPath::Resolved(None, path)) => { + let id = path.res.opt_def_id()?; + match cx.tcx.get_diagnostic_name(id) { + Some(sym::cmp_min) => Some(FunctionType::CmpMin), + Some(sym::cmp_max) => Some(FunctionType::CmpMax), + _ if is_diag_trait_item(cx, id, sym::Ord) => { + Some(FunctionType::OrdOrFloat(path.segments.last().expect("infallible"))) + }, + _ => None, + } + }, + ExprKind::Path(QPath::TypeRelative(ty, seg)) => { + matches!(path_res(cx, ty), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg)) + }, + _ => None, + } + } + + enum FunctionType<'tcx> { + CmpMin, + CmpMax, + OrdOrFloat(&'tcx PathSegment<'tcx>), + } + + fn check<'tcx>( + cx: &LateContext<'tcx>, + outer_fn: &'tcx Expr<'tcx>, + inner_call: &'tcx Expr<'tcx>, + outer_arg: &'tcx Expr<'tcx>, + span: Span, + ) -> Option> { + if let ExprKind::Call(inner_fn, [first, second]) = &inner_call.kind + && let Some(inner_seg) = segment(cx, inner_fn) + && let Some(outer_seg) = segment(cx, outer_fn) + { + let (input, inner_arg) = match (is_const_evaluatable(cx, first), is_const_evaluatable(cx, second)) { + (true, false) => (second, first), + (false, true) => (first, second), + _ => return None, + }; + let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); + let (min, max) = match (inner_seg, outer_seg) { + (FunctionType::CmpMin, FunctionType::CmpMax) => (outer_arg, inner_arg), + (FunctionType::CmpMax, FunctionType::CmpMin) => (inner_arg, outer_arg), + (FunctionType::OrdOrFloat(first_segment), FunctionType::OrdOrFloat(second_segment)) => { + match (first_segment.ident.as_str(), second_segment.ident.as_str()) { + ("min", "max") => (outer_arg, inner_arg), + ("max", "min") => (inner_arg, outer_arg), + _ => return None, + } + } + _ => return None, + }; + Some(ClampSuggestion { + params: InputMinMax { input, min, max, is_float }, + span, + make_assignment: None, + hir_with_ignore_attr: None, + }) + } else { + None + } + } + + if let ExprKind::Call(outer_fn, [first, second]) = &expr.kind { + check(cx, outer_fn, first, second, expr.span).or_else(|| check(cx, outer_fn, second, first, expr.span)) + } else { + None + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min, max) = (0, -3, 12); +/// +/// match input { +/// input if input > max => max, +/// input if input < min => min, +/// input => input, +/// } +/// # ; +/// ``` +fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + if let ExprKind::Match(value, [first_arm, second_arm, last_arm], rustc_hir::MatchSource::Normal) = &expr.kind { + // Find possible min/max branches + let minmax_values = |a: &'tcx Arm<'tcx>| { + if let PatKind::Binding(_, var_hir_id, _, None) = &a.pat.kind + && let Some(Guard::If(e)) = a.guard { + Some((e, var_hir_id, a.body)) + } else { + None + } + }; + let (first, first_hir_id, first_expr) = minmax_values(first_arm)?; + let (second, second_hir_id, second_expr) = minmax_values(second_arm)?; + let first = BinaryOp::new(first)?; + let second = BinaryOp::new(second)?; + if let PatKind::Binding(_, binding, _, None) = &last_arm.pat.kind + && path_to_local_id(peel_blocks_with_stmt(last_arm.body), *binding) + && last_arm.guard.is_none() + { + // Proceed as normal + } else { + return None; + } + if let Some(params) = is_clamp_meta_pattern( + cx, + &first, + &second, + first_expr, + second_expr, + Some((*first_hir_id, *second_hir_id)), + ) { + return Some(ClampSuggestion { + params: InputMinMax { + input: value, + min: params.min, + max: params.max, + is_float: params.is_float, + }, + span: expr.span, + make_assignment: None, + hir_with_ignore_attr: None, + }); + } + } + None +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min, max) = (0, -3, 12); +/// +/// let mut x = input; +/// if x < min { x = min; } +/// if x > max { x = max; } +/// ``` +fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Vec> { + block_stmt_with_last(block) + .tuple_windows() + .filter_map(|(maybe_set_first, maybe_set_second)| { + if let StmtKind::Expr(first_expr) = *maybe_set_first + && let StmtKind::Expr(second_expr) = *maybe_set_second + && let Some(If { cond: first_cond, then: first_then, r#else: None }) = If::hir(first_expr) + && let Some(If { cond: second_cond, then: second_then, r#else: None }) = If::hir(second_expr) + && let ExprKind::Assign( + maybe_input_first_path, + maybe_min_max_first, + _ + ) = peel_blocks_with_stmt(first_then).kind + && let ExprKind::Assign( + maybe_input_second_path, + maybe_min_max_second, + _ + ) = peel_blocks_with_stmt(second_then).kind + && eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path) + && let Some(first_bin) = BinaryOp::new(first_cond) + && let Some(second_bin) = BinaryOp::new(second_cond) + && let Some(input_min_max) = is_clamp_meta_pattern( + cx, + &first_bin, + &second_bin, + maybe_min_max_first, + maybe_min_max_second, + None + ) + { + Some(ClampSuggestion { + params: InputMinMax { + input: maybe_input_first_path, + min: input_min_max.min, + max: input_min_max.max, + is_float: input_min_max.is_float, + }, + span: first_expr.span.to(second_expr.span), + make_assignment: Some(maybe_input_first_path), + hir_with_ignore_attr: Some(first_expr.hir_id()), + }) + } else { + None + } + }) + .collect() +} + +/// Targets patterns like +/// +/// ``` +/// # let (mut input, min, max) = (0, -3, 12); +/// +/// if input < min { +/// input = min; +/// } else if input > max { +/// input = max; +/// } +/// ``` +fn is_if_elseif_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + if let Some(If { + cond, + then, + r#else: Some(else_if), + }) = If::hir(expr) + && let Some(If { + cond: else_if_cond, + then: else_if_then, + r#else: None, + }) = If::hir(peel_blocks(else_if)) + && let ExprKind::Assign( + maybe_input_first_path, + maybe_min_max_first, + _ + ) = peel_blocks_with_stmt(then).kind + && let ExprKind::Assign( + maybe_input_second_path, + maybe_min_max_second, + _ + ) = peel_blocks_with_stmt(else_if_then).kind + { + let params = is_clamp_meta_pattern( + cx, + &BinaryOp::new(peel_blocks(cond))?, + &BinaryOp::new(peel_blocks(else_if_cond))?, + peel_blocks(maybe_min_max_first), + peel_blocks(maybe_min_max_second), + None, + )?; + if !eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path) { + return None; + } + Some(ClampSuggestion { + params, + span: expr.span, + make_assignment: Some(maybe_input_first_path), + hir_with_ignore_attr: None, + }) + } else { + None + } +} + +/// `ExprKind::Binary` but more narrowly typed +#[derive(Debug, Clone, Copy)] +struct BinaryOp<'tcx> { + op: BinOpKind, + left: &'tcx Expr<'tcx>, + right: &'tcx Expr<'tcx>, +} + +impl<'tcx> BinaryOp<'tcx> { + fn new(e: &'tcx Expr<'tcx>) -> Option> { + match &e.kind { + ExprKind::Binary(op, left, right) => Some(BinaryOp { + op: op.node, + left, + right, + }), + _ => None, + } + } + + fn flip(&self) -> Self { + Self { + op: match self.op { + BinOpKind::Le => BinOpKind::Ge, + BinOpKind::Lt => BinOpKind::Gt, + BinOpKind::Ge => BinOpKind::Le, + BinOpKind::Gt => BinOpKind::Lt, + other => other, + }, + left: self.right, + right: self.left, + } + } +} + +/// The clamp meta pattern is a pattern shared between many (but not all) patterns. +/// In summary, this pattern consists of two if statements that meet many criteria, +/// - binary operators that are one of [`>`, `<`, `>=`, `<=`]. +/// - Both binary statements must have a shared argument +/// - Which can appear on the left or right side of either statement +/// - The binary operators must define a finite range for the shared argument. To put this in +/// the terms of Rust `std` library, the following ranges are acceptable +/// - `Range` +/// - `RangeInclusive` +/// And all other range types are not accepted. For the purposes of `clamp` it's irrelevant +/// whether the range is inclusive or not, the output is the same. +/// - The result of each if statement must be equal to the argument unique to that if statement. The +/// result can not be the shared argument in either case. +fn is_clamp_meta_pattern<'tcx>( + cx: &LateContext<'tcx>, + first_bin: &BinaryOp<'tcx>, + second_bin: &BinaryOp<'tcx>, + first_expr: &'tcx Expr<'tcx>, + second_expr: &'tcx Expr<'tcx>, + // This parameters is exclusively for the match pattern. + // It exists because the variable bindings used in that pattern + // refer to the variable bound in the match arm, not the variable + // bound outside of it. Fortunately due to context we know this has to + // be the input variable, not the min or max. + input_hir_ids: Option<(HirId, HirId)>, +) -> Option> { + fn check<'tcx>( + cx: &LateContext<'tcx>, + first_bin: &BinaryOp<'tcx>, + second_bin: &BinaryOp<'tcx>, + first_expr: &'tcx Expr<'tcx>, + second_expr: &'tcx Expr<'tcx>, + input_hir_ids: Option<(HirId, HirId)>, + is_float: bool, + ) -> Option> { + match (&first_bin.op, &second_bin.op) { + (BinOpKind::Ge | BinOpKind::Gt, BinOpKind::Le | BinOpKind::Lt) => { + let (min, max) = (second_expr, first_expr); + let refers_to_input = match input_hir_ids { + Some((first_hir_id, second_hir_id)) => { + path_to_local_id(peel_blocks(first_bin.left), first_hir_id) + && path_to_local_id(peel_blocks(second_bin.left), second_hir_id) + }, + None => eq_expr_value(cx, first_bin.left, second_bin.left), + }; + (refers_to_input + && eq_expr_value(cx, first_bin.right, first_expr) + && eq_expr_value(cx, second_bin.right, second_expr)) + .then_some(InputMinMax { + input: first_bin.left, + min, + max, + is_float, + }) + }, + _ => None, + } + } + // First filter out any expressions with side effects + let exprs = [ + first_bin.left, + first_bin.right, + second_bin.left, + second_bin.right, + first_expr, + second_expr, + ]; + let clampability = TypeClampability::is_clampable(cx, cx.typeck_results().expr_ty(first_expr))?; + let is_float = clampability.is_float(); + if exprs.iter().any(|e| peel_blocks(e).can_have_side_effects()) { + return None; + } + if !(is_ord_op(first_bin.op) && is_ord_op(second_bin.op)) { + return None; + } + let cases = [ + (*first_bin, *second_bin), + (first_bin.flip(), second_bin.flip()), + (first_bin.flip(), *second_bin), + (*first_bin, second_bin.flip()), + ]; + + cases.into_iter().find_map(|(first, second)| { + check(cx, &first, &second, first_expr, second_expr, input_hir_ids, is_float).or_else(|| { + check( + cx, + &second, + &first, + second_expr, + first_expr, + input_hir_ids.map(|(l, r)| (r, l)), + is_float, + ) + }) + }) +} + +fn block_stmt_with_last<'tcx>(block: &'tcx Block<'tcx>) -> impl Iterator> { + block + .stmts + .iter() + .map(|s| MaybeBorrowedStmtKind::Borrowed(&s.kind)) + .chain( + block + .expr + .as_ref() + .map(|e| MaybeBorrowedStmtKind::Owned(StmtKind::Expr(e))), + ) +} + +fn is_ord_op(op: BinOpKind) -> bool { + matches!(op, BinOpKind::Ge | BinOpKind::Gt | BinOpKind::Le | BinOpKind::Lt) +} + +/// Really similar to Cow, but doesn't have a `Clone` requirement. +#[derive(Debug)] +enum MaybeBorrowedStmtKind<'a> { + Borrowed(&'a StmtKind<'a>), + Owned(StmtKind<'a>), +} + +impl<'a> Clone for MaybeBorrowedStmtKind<'a> { + fn clone(&self) -> Self { + match self { + Self::Borrowed(t) => Self::Borrowed(t), + Self::Owned(StmtKind::Expr(e)) => Self::Owned(StmtKind::Expr(e)), + Self::Owned(_) => unreachable!("Owned should only ever contain a StmtKind::Expr."), + } + } +} + +impl<'a> Deref for MaybeBorrowedStmtKind<'a> { + type Target = StmtKind<'a>; + + fn deref(&self) -> &Self::Target { + match self { + Self::Borrowed(t) => t, + Self::Owned(t) => t, + } + } +} diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 53e7565bd33f..6a42275322b4 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -133,7 +133,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { diag.span_suggestion( header_span, "add the attribute", - format!("#[non_exhaustive] {}", snippet), + format!("#[non_exhaustive] {snippet}"), Applicability::Unspecified, ); } @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { diag.span_suggestion( header_span, "add the attribute", - format!("#[non_exhaustive] {}", snippet), + format!("#[non_exhaustive] {snippet}"), Applicability::Unspecified, ); } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 95cc6bdbd8ba..6f25a2ed8e43 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// let x: i32 = 24; /// let rem = x.rem_euclid(4); /// ``` - #[clippy::version = "1.63.0"] + #[clippy::version = "1.64.0"] pub MANUAL_REM_EUCLID, complexity, "manually reimplementing `rem_euclid`" diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index f28c37d3dca7..3181bc86d179 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { /// let mut vec = vec![0, 1, 2]; /// vec.retain(|x| x % 2 == 0); /// ``` - #[clippy::version = "1.63.0"] + #[clippy::version = "1.64.0"] pub MANUAL_RETAIN, perf, "`retain()` is simpler and the same functionalitys" @@ -92,7 +92,7 @@ fn check_into_iter( && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER) && let hir::ExprKind::MethodCall(_, struct_expr, [], _) = &into_iter_expr.kind && let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id) - && match_def_path(cx, into_iter_def_id, &paths::CORE_ITER_INTO_ITER) + && cx.tcx.lang_items().require(hir::LangItem::IntoIterIntoIter).ok() == Some(into_iter_def_id) && match_acceptable_type(cx, left_expr, msrv) && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) { suggest(cx, parent_expr, left_expr, target_expr); @@ -153,7 +153,7 @@ fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::E && let [filter_params] = filter_body.params && let Some(sugg) = match filter_params.pat.kind { hir::PatKind::Binding(_, _, filter_param_ident, None) => { - Some(format!("{}.retain(|{}| {})", snippet(cx, left_expr.span, ".."), filter_param_ident, snippet(cx, filter_body.value.span, ".."))) + Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, ".."))) }, hir::PatKind::Tuple([key_pat, value_pat], _) => { make_sugg(cx, key_pat, value_pat, left_expr, filter_body) @@ -161,7 +161,7 @@ fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::E hir::PatKind::Ref(pat, _) => { match pat.kind { hir::PatKind::Binding(_, _, filter_param_ident, None) => { - Some(format!("{}.retain(|{}| {})", snippet(cx, left_expr.span, ".."), filter_param_ident, snippet(cx, filter_body.value.span, ".."))) + Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, ".."))) }, _ => None } @@ -190,23 +190,19 @@ fn make_sugg( match (&key_pat.kind, &value_pat.kind) { (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Binding(_, _, value_param_ident, None)) => { Some(format!( - "{}.retain(|{}, &mut {}| {})", + "{}.retain(|{key_param_ident}, &mut {value_param_ident}| {})", snippet(cx, left_expr.span, ".."), - key_param_ident, - value_param_ident, snippet(cx, filter_body.value.span, "..") )) }, (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Wild) => Some(format!( - "{}.retain(|{}, _| {})", + "{}.retain(|{key_param_ident}, _| {})", snippet(cx, left_expr.span, ".."), - key_param_ident, snippet(cx, filter_body.value.span, "..") )), (hir::PatKind::Wild, hir::PatKind::Binding(_, _, value_param_ident, None)) => Some(format!( - "{}.retain(|_, &mut {}| {})", + "{}.retain(|_, &mut {value_param_ident}| {})", snippet(cx, left_expr.span, ".."), - value_param_ident, snippet(cx, filter_body.value.span, "..") )), _ => None, diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 7941c8c9c7e3..0976940afac3 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -108,15 +108,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { }; let test_span = expr.span.until(then.span); - span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {} manually", kind_word), |diag| { - diag.span_note(test_span, &format!("the {} was tested here", kind_word)); + span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| { + diag.span_note(test_span, &format!("the {kind_word} was tested here")); multispan_sugg( diag, - &format!("try using the `strip_{}` method", kind_word), + &format!("try using the `strip_{kind_word}` method"), vec![(test_span, - format!("if let Some() = {}.strip_{}({}) ", + format!("if let Some() = {}.strip_{kind_word}({}) ", snippet(cx, target_arg.span, ".."), - kind_word, snippet(cx, pattern.span, "..")))] .into_iter().chain(strippings.into_iter().map(|span| (span, "".into()))), ); diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 33d744815299..32da37a862d8 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -131,12 +131,12 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> }, hir::ExprKind::Block(block, _) => { match (block.stmts, block.expr.as_ref()) { - (&[], Some(inner_expr)) => { + ([], Some(inner_expr)) => { // If block only contains an expression, // reduce `{ X }` to `X` reduce_unit_expression(cx, inner_expr) }, - (&[ref inner_stmt], None) => { + ([inner_stmt], None) => { // If block only contains statements, // reduce `{ X; }` to `X` or `X;` match inner_stmt.kind { @@ -194,10 +194,7 @@ fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String { #[must_use] fn suggestion_msg(function_type: &str, map_type: &str) -> String { - format!( - "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type `()`", - map_type, function_type - ) + format!("called `map(f)` on an `{map_type}` value where `f` is a {function_type} that returns the unit type `()`") } fn lint_map_unit_fn( diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index 8588ab1ed8db..a020282d234f 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -70,9 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability); let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability); let sugg = format!( - "{} let Ok({}) = {}", - ifwhile, - some_expr_string, + "{ifwhile} let Ok({some_expr_string}) = {}", trimmed_ok.trim().trim_end_matches('.'), ); span_lint_and_sugg( @@ -80,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { MATCH_RESULT_OK, expr.span.with_hi(let_expr.span.hi()), "matching on `Some` with `ok()` is redundant", - &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), + &format!("consider matching on `Ok({some_expr_string})` and removing the call to `ok` instead"), sugg, applicability, ); diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 07021f1bcad8..fd14d868df34 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; 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 clippy_utils::{ + is_res_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; @@ -110,7 +112,7 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } match arm.pat.kind { PatKind::Binding(..) | PatKind::Wild => true, - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), _ => false, } } diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index b0198e856d5b..76f5e1c941c7 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -3,8 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; use clippy_utils::{ - can_move_expr_to_closure, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, peel_blocks, - peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, + can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; @@ -144,7 +144,7 @@ fn check<'tcx>( let scrutinee = peel_hir_expr_refs(scrutinee).0; let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({})", scrutinee_str) + format!("({scrutinee_str})") } else { scrutinee_str.into() }; @@ -172,9 +172,9 @@ fn check<'tcx>( }; let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; if some_expr.needs_unsafe_block { - format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip) + format!("|{annotation}{some_binding}| unsafe {{ {expr_snip} }}") } else { - format!("|{}{}| {}", annotation, some_binding, expr_snip) + format!("|{annotation}{some_binding}| {expr_snip}") } } } @@ -183,9 +183,9 @@ fn check<'tcx>( let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; if some_expr.needs_unsafe_block { - format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip) + format!("|{pat_snip}| unsafe {{ {expr_snip} }}") } else { - format!("|{}| {}", pat_snip, expr_snip) + format!("|{pat_snip}| {expr_snip}") } } else { // Refutable bindings and mixed reference annotations can't be handled by `map`. @@ -199,9 +199,9 @@ fn check<'tcx>( "manual implementation of `Option::map`", "try this", if else_pat.is_none() && is_else_clause(cx.tcx, expr) { - format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + format!("{{ {scrutinee_str}{as_ref_str}.map({body_str}) }}") } else { - format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) + format!("{scrutinee_str}{as_ref_str}.map({body_str})") }, app, ); @@ -251,9 +251,11 @@ fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: Syn match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), + PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { + Some(OptionPat::None) + }, PatKind::TupleStruct(ref qpath, [pattern], _) - if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => + if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, @@ -272,16 +274,14 @@ fn get_some_expr<'tcx>( ) -> Option> { // TODO: Allow more complex expressions. match expr.kind { - ExprKind::Call( - Expr { - kind: ExprKind::Path(ref qpath), - .. - }, - [arg], - ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr { - expr: arg, - needs_unsafe_block, - }), + ExprKind::Call(callee, [arg]) + if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => + { + Some(SomeExpr { + expr: arg, + needs_unsafe_block, + }) + }, ExprKind::Block( Block { stmts: [], @@ -302,5 +302,5 @@ fn get_some_expr<'tcx>( // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) + is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) } diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index e1111c80f2fe..587c926dc01c 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -3,12 +3,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{is_lang_ctor, path_to_local_id, sugg}; +use clippy_utils::{is_res_lang_ctor, path_to_local_id, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::LangItem::{OptionNone, ResultErr}; use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; +use rustc_middle::ty::DefIdTree; use rustc_span::sym; use super::MANUAL_UNWRAP_OR; @@ -42,12 +44,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `{}::unwrap_or`", ty_name), + &format!("this pattern reimplements `{ty_name}::unwrap_or`"), "replace with", format!( - "{}.unwrap_or({})", - suggestion, - reindented_or_body, + "{suggestion}.unwrap_or({reindented_or_body})", ), Applicability::MachineApplicable, ); @@ -61,15 +61,19 @@ fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&' if arms.iter().all(|arm| arm.guard.is_none()); if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { match arm.pat.kind { - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [pat], _) => - matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), + matches!(pat.kind, PatKind::Wild) + && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr), _ => false, } }); let unwrap_arm = &arms[1 - idx]; if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind; - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, unwrap_arm.pat.hir_id); + if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); + if cx.tcx.lang_items().option_some_variant() == Some(variant_id) + || cx.tcx.lang_items().result_ok_variant() == Some(variant_id); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 91d17f481e2d..2818f030b7a6 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_lang_ctor, peel_blocks}; +use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; use rustc_lint::LateContext; @@ -45,13 +45,11 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: cx, MATCH_AS_REF, expr.span, - &format!("use `{}()` instead", suggestion), + &format!("use `{suggestion}()` instead"), "try this", format!( - "{}.{}(){}", + "{}.{suggestion}(){cast}", snippet_with_applicability(cx, ex.span, "_", &mut applicability), - suggestion, - cast, ), applicability, ); @@ -61,18 +59,20 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: // Checks if arm has the form `None => None` fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, LangItem::OptionNone)) + matches!( + arm.pat.kind, + PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone) + ) } // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if_chain! { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind; - if is_lang_ctor(cx, qpath, LangItem::OptionSome); + if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome); if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind; if let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind; - if let ExprKind::Path(ref some_path) = e.kind; - if is_lang_ctor(cx, some_path, LangItem::OptionSome); + if is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome); if let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind; if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; then { diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 34cc082687ec..107fad32393c 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -112,7 +112,7 @@ where .join(" | ") }; let pat_and_guard = if let Some(Guard::If(g)) = first_guard { - format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability)) + format!("{pat} if {}", snippet_with_applicability(cx, g.span, "..", &mut applicability)) } else { pat }; @@ -131,10 +131,9 @@ where &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }), "try this", format!( - "{}matches!({}, {})", + "{}matches!({}, {pat_and_guard})", if b0 { "" } else { "!" }, snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), - pat_and_guard, ), applicability, ); diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index d37f44d4a17e..37049f835775 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -134,7 +134,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { diag.span_suggestion( keep_arm.pat.span, "try merging the arm patterns", - format!("{} | {}", keep_pat_snip, move_pat_snip), + format!("{keep_pat_snip} | {move_pat_snip}"), Applicability::MaybeIncorrect, ) .help("or try changing either arm body") diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 5ae4a65acaf3..68682cedf1de 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -75,12 +75,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e Some(AssignmentExpr::Local { span, pat_span }) => ( span, format!( - "let {} = {};\n{}let {} = {};", + "let {} = {};\n{}let {} = {snippet_body};", snippet_with_applicability(cx, bind_names, "..", &mut applicability), snippet_with_applicability(cx, matched_vars, "..", &mut applicability), " ".repeat(indent_of(cx, expr.span).unwrap_or(0)), - snippet_with_applicability(cx, pat_span, "..", &mut applicability), - snippet_body + snippet_with_applicability(cx, pat_span, "..", &mut applicability) ), ), None => { @@ -110,10 +109,8 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e if ex.can_have_side_effects() { let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0)); let sugg = format!( - "{};\n{}{}", - snippet_with_applicability(cx, ex.span, "..", &mut applicability), - indent, - snippet_body + "{};\n{indent}{snippet_body}", + snippet_with_applicability(cx, ex.span, "..", &mut applicability) ); span_lint_and_sugg( @@ -178,10 +175,10 @@ fn sugg_with_curlies<'a>( let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); if let Some(parent_expr) = get_parent_expr(cx, match_expr) { if let ExprKind::Closure { .. } = parent_expr.kind { - cbrace_end = format!("\n{}}}", indent); + cbrace_end = format!("\n{indent}}}"); // Fix body indent due to the closure indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); + cbrace_start = format!("{{\n{indent}"); } } @@ -190,10 +187,10 @@ fn sugg_with_curlies<'a>( let parent_node_id = cx.tcx.hir().get_parent_node(match_expr.hir_id); if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { if let ExprKind::Match(..) = arm.body.kind { - cbrace_end = format!("\n{}}}", indent); + cbrace_end = format!("\n{indent}}}"); // Fix body indent due to the match indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); + cbrace_start = format!("{{\n{indent}"); } } @@ -204,13 +201,8 @@ fn sugg_with_curlies<'a>( }); format!( - "{}let {} = {};\n{}{}{}{}", - cbrace_start, + "{cbrace_start}let {} = {};\n{indent}{assignment_str}{snippet_body}{cbrace_end}", snippet_with_applicability(cx, bind_names, "..", applicability), - snippet_with_applicability(cx, matched_vars, "..", applicability), - indent, - assignment_str, - snippet_body, - cbrace_end + snippet_with_applicability(cx, matched_vars, "..", applicability) ) } diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 1e80b6cf2d83..6647322caa37 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -118,8 +118,8 @@ fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad MATCH_STR_CASE_MISMATCH, bad_case_span, "this `match` arm has a differing case than its expression", - &format!("consider changing the case of this arm to respect `{}`", method_str), - format!("\"{}\"", suggestion), + &format!("consider changing the case of this arm to respect `{method_str}`"), + format!("\"{suggestion}\""), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/matches/match_wild_err_arm.rs b/clippy_lints/src/matches/match_wild_err_arm.rs index a3aa2e4b389a..42f1e2629d41 100644 --- a/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/clippy_lints/src/matches/match_wild_err_arm.rs @@ -38,7 +38,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' span_lint_and_note(cx, MATCH_WILD_ERR_ARM, arm.pat.span, - &format!("`Err({})` matches all errors", ident_bind_name), + &format!("`Err({ident_bind_name})` matches all errors"), None, "match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable", ); diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 58ea43e69d9b..c4f6852aedc3 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -3,15 +3,15 @@ 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, over, + eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_res_lang_ctor, over, path_res, peel_blocks_with_stmt, }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_span::sym; -use rustc_hir_analysis::hir_ty_to_ty; pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) { @@ -112,10 +112,7 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool 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) { - if let ExprKind::Path(ref qpath) = ret.kind { - return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); - } - return false; + return is_res_lang_ctor(cx, path_res(cx, ret), OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); } return eq_expr_value(cx, if_let.let_expr, ret); } diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index c89784065b8b..81bebff34c82 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -4,11 +4,12 @@ use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::any_temporaries_need_ordered_drop; -use clippy_utils::{higher, is_lang_ctor, is_trait_method}; +use clippy_utils::{higher, is_trait_method}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::LangItem::{self, OptionSome, OptionNone, PollPending, PollReady, ResultOk, ResultErr}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty}; @@ -87,15 +88,21 @@ fn find_sugg_for_if_let<'tcx>( } }, PatKind::Path(ref path) => { - let method = if is_lang_ctor(cx, path, OptionNone) { - "is_none()" - } else if is_lang_ctor(cx, path, PollPending) { - "is_pending()" + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, check_pat.hir_id) + && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) + { + let method = if cx.tcx.lang_items().option_none_variant() == Some(variant_id) { + "is_none()" + } else if cx.tcx.lang_items().poll_pending_variant() == Some(variant_id) { + "is_pending()" + } else { + return; + }; + // `None` and `Pending` don't have an inner type. + (method, cx.tcx.types.unit) } else { return; - }; - // `None` and `Pending` don't have an inner type. - (method, cx.tcx.types.unit) + } }, _ => return, }; @@ -138,7 +145,7 @@ fn find_sugg_for_if_let<'tcx>( cx, REDUNDANT_PATTERN_MATCHING, let_pat.span, - &format!("redundant pattern matching, consider using `{}`", good_method), + &format!("redundant pattern matching, consider using `{good_method}`"), |diag| { // if/while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -162,7 +169,7 @@ fn find_sugg_for_if_let<'tcx>( .maybe_par() .to_string(); - diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app); + diag.span_suggestion(span, "try this", format!("{keyword} {sugg}.{good_method}"), app); if needs_drop { diag.note("this will change drop order of the result, as well as all temporaries"); @@ -213,7 +220,6 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { - find_good_method_for_match( cx, arms, @@ -253,12 +259,12 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op cx, REDUNDANT_PATTERN_MATCHING, expr.span, - &format!("redundant pattern matching, consider using `{}`", good_method), + &format!("redundant pattern matching, consider using `{good_method}`"), |diag| { diag.span_suggestion( span, "try this", - format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method), + format!("{}.{good_method}", snippet(cx, result_expr.span, "_")), Applicability::MaybeIncorrect, // snippet ); }, @@ -269,8 +275,8 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op #[derive(Clone, Copy)] enum Item { - Lang(LangItem), - Diag(Symbol, Symbol), + Lang(LangItem), + Diag(Symbol, Symbol), } fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expected_item: Item) -> bool { @@ -285,15 +291,16 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte let ty = cx.typeck_results().pat_ty(pat); if is_type_diagnostic_item(cx, ty, expected_ty) { - let variant = ty.ty_adt_def() + let variant = ty + .ty_adt_def() .expect("struct pattern type is not an ADT") .variant_of_res(cx.qpath_res(path, pat.hir_id)); - return variant.name == expected_variant + return variant.name == expected_variant; } false - } + }, } } @@ -308,20 +315,16 @@ fn find_good_method_for_match<'a>( should_be_left: &'a str, should_be_right: &'a str, ) -> Option<&'a str> { - let pat_left = arms[0].pat; - let pat_right = arms[1].pat; + let first_pat = arms[0].pat; + let second_pat = arms[1].pat; - let body_node_pair = if ( - is_pat_variant(cx, pat_left, path_left, expected_item_left) - ) && ( - is_pat_variant(cx, pat_right, path_right, expected_item_right) - ) { + let body_node_pair = if (is_pat_variant(cx, first_pat, path_left, expected_item_left)) + && (is_pat_variant(cx, second_pat, path_right, expected_item_right)) + { (&arms[0].body.kind, &arms[1].body.kind) - } else if ( - is_pat_variant(cx, pat_left, path_left, expected_item_right) - ) && ( - is_pat_variant(cx, pat_right, path_right, expected_item_left) - ) { + } else if (is_pat_variant(cx, first_pat, path_left, expected_item_right)) + && (is_pat_variant(cx, second_pat, path_right, expected_item_left)) + { (&arms[1].body.kind, &arms[0].body.kind) } else { return None; diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 86a9df034979..85269e533a06 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -50,13 +50,13 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0)); let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy { - format!("let value = *{};\n{}", original, trailing_indent) + format!("let value = *{original};\n{trailing_indent}") } else if found.is_unit_return_val { // If the return value of the expression to be moved is unit, then we don't need to // capture the result in a temporary -- we can just replace it completely with `()`. - format!("{};\n{}", original, trailing_indent) + format!("{original};\n{trailing_indent}") } else { - format!("let value = {};\n{}", original, trailing_indent) + format!("let value = {original};\n{trailing_indent}") }; let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly { diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 56bcdc01fe4d..d496107ffd6b 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -99,23 +99,21 @@ fn report_single_pattern( let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; let sugg = format!( - "if {} == {}{} {}{}", + "if {} == {}{} {}{els_str}", snippet(cx, ex.span, ".."), // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), snippet(cx, arms[0].pat.span, ".."), expr_block(cx, arms[0].body, None, "..", Some(expr.span)), - els_str, ); (msg, sugg) } else { let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"; let sugg = format!( - "if let {} = {} {}{}", + "if let {} = {} {}{els_str}", snippet(cx, arms[0].pat.span, ".."), snippet(cx, ex.span, ".."), expr_block(cx, arms[0].body, None, "..", Some(expr.span)), - els_str, ); (msg, sugg) } diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index 663277d11365..c6cba81d8718 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,7 +1,7 @@ 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::{get_parent_expr, is_lang_ctor, match_def_path, paths}; +use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; @@ -27,8 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine if let ExprKind::Path(ref match_fun_path) = match_fun.kind; if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)); if let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind; - if let ExprKind::Path(ref err_fun_path) = err_fun.kind; - if is_lang_ctor(cx, err_fun_path, ResultErr); + if is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr); if let Some(return_ty) = find_return_type(cx, &expr.kind); then { let prefix; @@ -61,9 +60,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine "return " }; let suggestion = if err_ty == expr_err_ty { - format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) + format!("{ret_prefix}{prefix}{origin_snippet}{suffix}") } else { - format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) + format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}") }; span_lint_and_sugg( diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index cad3ea2a176c..0c4d9f100f7a 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_lang_ctor, meets_msrv, msrvs}; +use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; @@ -102,40 +102,38 @@ impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { - if let ExprKind::Path(ref replacement_qpath) = src.kind { - // Check that second argument is `Option::None` - if is_lang_ctor(cx, replacement_qpath, OptionNone) { - // Since this is a late pass (already type-checked), - // and we already know that the second argument is an - // `Option`, we do not need to check the first - // argument's type. All that's left is to get - // replacee's path. - let replaced_path = match dest.kind { - ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => { - if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind { - replaced_path - } else { - return; - } - }, - ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path, - _ => return, - }; + // Check that second argument is `Option::None` + if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { + // Since this is a late pass (already type-checked), + // and we already know that the second argument is an + // `Option`, we do not need to check the first + // argument's type. All that's left is to get + // replacee's path. + let replaced_path = match dest.kind { + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => { + if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind { + replaced_path + } else { + return; + } + }, + ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path, + _ => return, + }; - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - MEM_REPLACE_OPTION_WITH_NONE, - expr_span, - "replacing an `Option` with `None`", - "consider `Option::take()` instead", - format!( - "{}.take()", - snippet_with_applicability(cx, replaced_path.span, "", &mut applicability) - ), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MEM_REPLACE_OPTION_WITH_NONE, + expr_span, + "replacing an `Option` with `None`", + "consider `Option::take()` instead", + format!( + "{}.take()", + snippet_with_applicability(cx, replaced_path.span, "", &mut applicability) + ), + applicability, + ); } } @@ -203,10 +201,8 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< return; } // disable lint for Option since it is covered in another lint - if let ExprKind::Path(q) = &src.kind { - if is_lang_ctor(cx, q, OptionNone) { - return; - } + if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { + return; } if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) { span_lint_and_then( diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 22f5635a5bcc..cc26b0f7fa82 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -85,7 +85,7 @@ pub(crate) trait BindInsteadOfMap { let closure_args_snip = snippet(cx, closure_args_span, ".."); let option_snip = snippet(cx, recv.span, ".."); - let note = format!("{}.{}({} {})", option_snip, Self::GOOD_METHOD_NAME, closure_args_snip, some_inner_snip); + let note = format!("{option_snip}.{}({closure_args_snip} {some_inner_snip})", Self::GOOD_METHOD_NAME); span_lint_and_sugg( cx, BIND_INSTEAD_OF_MAP, diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 44857d61fef8..2e96346be977 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 `.bytes().nth()` on a `{}`", caller_type), + &format!("called `.bytes().nth()` on a `{caller_type}`"), "try", format!( "{}.as_bytes().get({})", diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index b2bc1ad5c9ed..56b7fbb9d4bc 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -33,12 +33,11 @@ pub(super) fn check( cx, lint, info.expr.span, - &format!("you should use the `{}` method", suggest), + &format!("you should use the `{suggest}` method"), "like this", - format!("{}{}.{}({})", + format!("{}{}.{suggest}({})", if info.eq { "" } else { "!" }, snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), - suggest, snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)), applicability, ); diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index b85bfec2b12b..7e808760663a 100644 --- a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -26,12 +26,11 @@ pub(super) fn check<'tcx>( cx, lint, info.expr.span, - &format!("you should use the `{}` method", suggest), + &format!("you should use the `{suggest}` method"), "like this", - format!("{}{}.{}('{}')", + format!("{}{}.{suggest}('{}')", if info.eq { "" } else { "!" }, snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), - suggest, c.escape_default()), applicability, ); diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 7ab6b84c2074..7c7938dd2e8b 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -49,8 +49,7 @@ pub(super) fn check( expr.span, &format!( "using `clone` on a double-reference; \ - this will copy the reference of type `{}` instead of cloning the inner type", - ty + this will copy the reference of type `{ty}` instead of cloning the inner type" ), |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { @@ -62,11 +61,11 @@ pub(super) fn check( } let refs = "&".repeat(n + 1); let derefs = "*".repeat(n); - let explicit = format!("<{}{}>::clone({})", refs, ty, snip); + let explicit = format!("<{refs}{ty}>::clone({snip})"); diag.span_suggestion( expr.span, "try dereferencing it", - format!("{}({}{}).clone()", refs, derefs, snip.deref()), + format!("{refs}({derefs}{}).clone()", snip.deref()), Applicability::MaybeIncorrect, ); diag.span_suggestion( @@ -121,16 +120,16 @@ pub(super) fn check( let (help, sugg) = if deref_count == 0 { ("try removing the `clone` call", snip.into()) } else if parent_is_suffix_expr { - ("try dereferencing it", format!("({}{})", "*".repeat(deref_count), snip)) + ("try dereferencing it", format!("({}{snip})", "*".repeat(deref_count))) } else { - ("try dereferencing it", format!("{}{}", "*".repeat(deref_count), snip)) + ("try dereferencing it", format!("{}{snip}", "*".repeat(deref_count))) }; span_lint_and_sugg( cx, CLONE_ON_COPY, expr.span, - &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), + &format!("using `clone` on type `{ty}` which implements the `Copy` trait"), help, sugg, app, diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index f82ca8912006..355f53532e26 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -41,7 +41,7 @@ pub(super) fn check( expr.span, "using `.clone()` on a ref-counted pointer", "try this", - format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet), + format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak ); } diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index bd846d71d466..d0cf411dfd34 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -143,9 +143,9 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - &format!("use of `{}` followed by a function call", name), + &format!("use of `{name}` followed by a function call"), "try this", - format!("unwrap_or_else({} panic!({}))", closure_args, sugg), + format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, ); return; @@ -160,12 +160,9 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - &format!("use of `{}` followed by a function call", name), + &format!("use of `{name}` followed by a function call"), "try this", - format!( - "unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})", - closure_args, arg_root_snippet - ), + format!("unwrap_or_else({closure_args} {{ panic!(\"{{}}\", {arg_root_snippet}) }})"), applicability, ); } diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index 7b2967feb0fe..3fef53739fbd 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -1,17 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::match_type; -use clippy_utils::{get_parent_expr, paths}; +use clippy_utils::get_parent_expr; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; +use rustc_span::sym; use super::FILETYPE_IS_FILE; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv); - if !match_type(cx, ty, &paths::FILE_TYPE) { + if !is_type_diagnostic_item(cx, ty, sym::FileType) { return; } @@ -35,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr span = expr.span; } } - let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb); - let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary); + let lint_msg = format!("`{lint_unary}FileType::is_file()` only {verb} regular files"); + let help_msg = format!("use `{help_unary}FileType::is_dir()` instead"); span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg); } diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index 38ec4d8e3ab8..ddf8a1f09b87 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try this", - format!("{}.find_map({})", iter_snippet, filter_snippet), + format!("{iter_snippet}.find_map({filter_snippet})"), Applicability::MachineApplicable, ); } else { diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index bcf8d93b602e..edcec0fc1015 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try this", - format!("{}.find({})", iter_snippet, filter_snippet), + format!("{iter_snippet}.find({filter_snippet})"), Applicability::MachineApplicable, ); } else { diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 6436e28a63c5..66dfce3682b5 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -23,7 +23,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp // `expr` implements `FromIterator` trait let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); let turbofish = extract_turbofish(cx, expr, ty); - let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish); + let sugg = format!("{iter_expr}.collect::<{turbofish}>()"); span_lint_and_sugg( cx, FROM_ITER_INSTEAD_OF_COLLECT, @@ -63,7 +63,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> if e == type_specifier { None } else { Some((*e).to_string()) } }).collect::>(); // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) - format!("{}{}", without_ts.join("::"), type_specifier) + format!("{}{type_specifier}", without_ts.join("::")) } else { // type is not explicitly specified so wildcards are needed // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` @@ -72,7 +72,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> let end = ty_str.find('>').unwrap_or(ty_str.len()); let nb_wildcard = ty_str[start..end].split(',').count(); let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); - format!("{}<{}>", elements.join("::"), wildcards) + format!("{}<{wildcards}>", elements.join("::")) } } } diff --git a/clippy_lints/src/methods/get_first.rs b/clippy_lints/src/methods/get_first.rs index 4de77de74042..cb17af608a3f 100644 --- a/clippy_lints/src/methods/get_first.rs +++ b/clippy_lints/src/methods/get_first.rs @@ -29,9 +29,9 @@ pub(super) fn check<'tcx>( cx, GET_FIRST, expr.span, - &format!("accessing first element with `{0}.get(0)`", slice_name), + &format!("accessing first element with `{slice_name}.get(0)`"), "try", - format!("{}.first()", slice_name), + format!("{slice_name}.first()"), app, ); } diff --git a/clippy_lints/src/methods/get_last_with_len.rs b/clippy_lints/src/methods/get_last_with_len.rs index 02aada87202c..3bdc154df049 100644 --- a/clippy_lints/src/methods/get_last_with_len.rs +++ b/clippy_lints/src/methods/get_last_with_len.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::SpanlessEq; -use rustc_ast::LitKind; +use clippy_utils::{is_integer_literal, SpanlessEq}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -26,8 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: && lhs_path.ident.name == sym::len // RHS of subtraction is 1 - && let ExprKind::Lit(rhs_lit) = &rhs.kind - && let LitKind::Int(1, ..) = rhs_lit.node + && is_integer_literal(rhs, 1) // check that recv == lhs_recv `recv.get(lhs_recv.len() - 1)` && SpanlessEq::new(cx).eq_expr(recv, lhs_recv) diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index 18e08d6ee232..ffc3a4d780e5 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -71,16 +71,11 @@ pub(super) fn check<'tcx>( cx, GET_UNWRAP, span, - &format!( - "called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise", - mut_str, caller_type - ), + &format!("called `.get{mut_str}().unwrap()` on a {caller_type}. Using `[]` is more clear and more concise"), "try this", format!( - "{}{}[{}]", - borrow_str, - snippet_with_applicability(cx, recv.span, "..", &mut applicability), - get_args_str + "{borrow_str}{}[{get_args_str}]", + snippet_with_applicability(cx, recv.span, "..", &mut applicability) ), applicability, ); diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 9651a52be4e7..429cdc1918d7 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -26,12 +26,12 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv cx, IMPLICIT_CLONE, expr.span, - &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_name), + &format!("implicitly cloning a `{ty_name}` by calling `{method_name}` on its dereferenced type"), "consider using", if ref_count > 1 { - format!("({}{}).clone()", "*".repeat(ref_count - 1), recv_snip) + format!("({}{recv_snip}).clone()", "*".repeat(ref_count - 1)) } else { - format!("{}.clone()", recv_snip) + format!("{recv_snip}.clone()") }, app, ); diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index e1c9b5248a8a..ede3b8bb74e9 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -34,18 +34,17 @@ pub fn check<'tcx>( cx, INEFFICIENT_TO_STRING, expr.span, - &format!("calling `to_string` on `{}`", arg_ty), + &format!("calling `to_string` on `{arg_ty}`"), |diag| { diag.help(&format!( - "`{}` implements `ToString` through a slower blanket impl, but `{}` has a fast specialization of `ToString`", - self_ty, deref_self_ty + "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" )); let mut applicability = Applicability::MachineApplicable; let arg_snippet = snippet_with_applicability(cx, receiver.span, "..", &mut applicability); diag.span_suggestion( expr.span, "try dereferencing the receiver", - format!("({}{}).to_string()", "*".repeat(deref_count), arg_snippet), + format!("({}{arg_snippet}).to_string()", "*".repeat(deref_count)), applicability, ); }, @@ -66,7 +65,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { } if let ty::Adt(adt, substs) = ty.kind() { - match_def_path(cx, adt.did(), &paths::COW) && substs.type_at(1).is_str() + cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) && substs.type_at(1).is_str() } else { false } diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index 11e76841e9f0..be56b63506a4 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -30,8 +30,7 @@ pub(super) fn check( INTO_ITER_ON_REF, method_span, &format!( - "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", - method_name, kind, + "this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`", ), "call directly", method_name.to_string(), diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index aa176dcc8b4a..304024e80666 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -37,12 +37,11 @@ pub(super) fn check<'tcx>( cx, IS_DIGIT_ASCII_RADIX, expr.span, - &format!("use of `char::is_digit` with literal radix of {}", num), + &format!("use of `char::is_digit` with literal radix of {num}"), "try", format!( - "{}.{}()", - snippet_with_applicability(cx, self_arg.span, "..", &mut applicability), - replacement + "{}.{replacement}()", + snippet_with_applicability(cx, self_arg.span, "..", &mut applicability) ), applicability, ); diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs index 30d56113c6c1..bde6f92b076e 100644 --- a/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -20,8 +20,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir: cx, ITER_CLONED_COLLECT, to_replace, - &format!("called `iter().{}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ - more readable", method_name), + &format!("called `iter().{method_name}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ + more readable"), "try", ".to_vec()".to_string(), Applicability::MachineApplicable, diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index 052be3d8ee7c..bcddc7c786a5 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -37,7 +37,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E cx, ITER_COUNT, expr.span, - &format!("called `.{}().count()` on a `{}`", iter_method, caller_type), + &format!("called `.{iter_method}().count()` on a `{caller_type}`"), "try", format!( "{}.len()", diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index a7eecabd6849..2244ebfb1292 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -54,9 +54,9 @@ pub(super) fn check<'tcx>( cx, ITER_KV_MAP, expr.span, - &format!("iterating on a map's {}s", replacement_kind), + &format!("iterating on a map's {replacement_kind}s"), "try", - format!("{}.{}{}s()", recv_snippet, into_prefix, replacement_kind), + format!("{recv_snippet}.{into_prefix}{replacement_kind}s()"), applicability, ); } else { @@ -64,9 +64,9 @@ pub(super) fn check<'tcx>( cx, ITER_KV_MAP, expr.span, - &format!("iterating on a map's {}s", replacement_kind), + &format!("iterating on a map's {replacement_kind}s"), "try", - format!("{}.{}{}s().map(|{}| {})", recv_snippet, into_prefix, replacement_kind, binded_ident, + format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{binded_ident}| {})", snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)), applicability, ); diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index b8d1dabe0076..83c1bf203467 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal let suggest = if start_idx == 0 { format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) } else { - format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx) + format!("{}.get({start_idx})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index 80ca4c94219f..ceee12784cbb 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -32,8 +32,8 @@ pub(super) fn check<'tcx>( cx, ITER_NTH, expr.span, - &format!("called `.iter{0}().nth()` on a {1}", mut_str, caller_type), + &format!("called `.iter{mut_str}().nth()` on a {caller_type}"), None, - &format!("calling `.get{}()` is both faster and more readable", mut_str), + &format!("calling `.get{mut_str}()` is both faster and more readable"), ); } diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index cea7b0d82ff3..4f73b3ec4224 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate}; +use clippy_utils::{get_expr_use_or_unification_node, is_no_std_crate, is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -26,26 +26,11 @@ impl IterType { } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { - let item = match &recv.kind { - ExprKind::Array(v) if v.len() <= 1 => v.first(), - ExprKind::Path(p) => { - if is_lang_ctor(cx, p, OptionNone) { - None - } else { - return; - } - }, - ExprKind::Call(f, some_args) if some_args.len() == 1 => { - if let ExprKind::Path(p) = &f.kind { - if is_lang_ctor(cx, p, OptionSome) { - Some(&some_args[0]) - } else { - return; - } - } else { - return; - } - }, + let item = match recv.kind { + ExprKind::Array([]) => None, + ExprKind::Array([e]) => Some(e), + ExprKind::Path(ref p) if is_res_lang_ctor(cx, cx.qpath_res(p, recv.hir_id), OptionNone) => None, + ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, path_res(cx, f), OptionSome) => Some(arg), _ => return, }; let iter_type = match method_name { diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs index a669cbbbcc60..3da230e12d7f 100644 --- a/clippy_lints/src/methods/iter_with_drain.rs +++ b/clippy_lints/src/methods/iter_with_drain.rs @@ -22,7 +22,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span cx, ITER_WITH_DRAIN, span.with_hi(expr.span.hi()), - &format!("`drain(..)` used on a `{}`", ty_name), + &format!("`drain(..)` used on a `{ty_name}`"), "try this", "into_iter()".to_string(), Applicability::MaybeIncorrect, diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index ffd2f4a38b8a..5b758f1e6547 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_lang_ctor, path_to_local_id}; +use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; -use rustc_hir::{Closure, Expr, ExprKind, PatKind}; +use rustc_hir::{Expr, ExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -22,8 +22,8 @@ pub(super) fn check<'tcx>( if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option); - if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind; - if is_lang_ctor(cx, err_path, ResultErr); + if let ExprKind::Call(err_path, [err_arg]) = or_expr.kind; + if is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr); if is_ok_wrapping(cx, map_expr); if let Some(recv_snippet) = snippet_opt(cx, recv.span); if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); @@ -37,9 +37,7 @@ pub(super) fn check<'tcx>( "this pattern reimplements `Option::ok_or`", "replace with", format!( - "{}.ok_or({})", - recv_snippet, - reindented_err_arg_snippet + "{recv_snippet}.ok_or({reindented_err_arg_snippet})" ), Applicability::MachineApplicable, ); @@ -48,17 +46,19 @@ pub(super) fn check<'tcx>( } fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { - if let ExprKind::Path(ref qpath) = map_expr.kind { - if is_lang_ctor(cx, qpath, ResultOk) { - return true; - } - } - if_chain! { - if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind; - let body = cx.tcx.hir().body(body); - if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; - if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; - if is_lang_ctor(cx, ok_path, ResultOk); - then { path_to_local_id(ok_arg, param_id) } else { false } + match map_expr.kind { + ExprKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, map_expr.hir_id), ResultOk) => true, + ExprKind::Closure(closure) => { + let body = cx.tcx.hir().body(closure.body); + if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind + && let ExprKind::Call(callee, [ok_arg]) = body.value.kind + && is_res_lang_ctor(cx, path_res(cx, callee), ResultOk) + { + path_to_local_id(ok_arg, param_id) + } else { + false + } + }, + _ => false, } } diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 0fe510beaa07..ec694cf6882e 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -57,11 +57,10 @@ pub fn check( super::MANUAL_SATURATING_ARITHMETIC, expr.span, "manual saturating arithmetic", - &format!("try using `saturating_{}`", arith), + &format!("try using `saturating_{arith}`"), format!( - "{}.saturating_{}({})", + "{}.saturating_{arith}({})", snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), - arith, snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), ), applicability, diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 46d2fc493f81..8b6b8f1bf16c 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_path_diagnostic_item; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; -use clippy_utils::{is_expr_path_def_path, paths}; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -38,7 +38,7 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { let ty = cx.typeck_results().expr_ty(e); if is_type_diagnostic_item(cx, ty, sym::String) || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str)) - || (match_type(cx, ty, &paths::COW) && get_ty_param(ty).map_or(false, Ty::is_str)) + || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str)) { Some(RepeatKind::String) } else { @@ -57,7 +57,7 @@ pub(super) fn check( ) { if_chain! { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; - if is_expr_path_def_path(cx, repeat_fn, &paths::ITER_REPEAT); + if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String); if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id); if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); @@ -91,7 +91,7 @@ pub(super) fn check( collect_expr.span, "manual implementation of `str::repeat` using iterators", "try this", - format!("{}.repeat({})", val_str, count_snip), + format!("{val_str}.repeat({count_snip})"), app ) } diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 8261ef5e1ce3..7ce14ec080b1 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -111,11 +111,10 @@ fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_cop MAP_CLONE, replace, message, - &format!("consider calling the dedicated `{}` method", sugg_method), + &format!("consider calling the dedicated `{sugg_method}` method"), format!( - "{}.{}()", + "{}.{sugg_method}()", snippet_with_applicability(cx, root, "..", &mut applicability), - sugg_method, ), applicability, ); diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 13853dec99de..361ffcb5ef3f 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -20,12 +20,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_ cx, MAP_FLATTEN, expr.span.with_lo(map_span.lo()), - &format!("called `map(..).flatten()` on `{}`", caller_ty_name), - &format!( - "try replacing `map` with `{}` and remove the `.flatten()`", - method_to_use - ), - format!("{}({})", method_to_use, closure_snippet), + &format!("called `map(..).flatten()` on `{caller_ty_name}`"), + &format!("try replacing `map` with `{method_to_use}` and remove the `.flatten()`"), + format!("{method_to_use}({closure_snippet})"), applicability, ); } diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 862a9578e6ff..0f25ef82ed42 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -30,7 +30,7 @@ pub(super) fn check( MAP_IDENTITY, sugg_span, "unnecessary map of the identity function", - &format!("remove the call to `{}`", name), + &format!("remove the call to `{name}`"), String::new(), Applicability::MachineApplicable, ) diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 4a8e7ce4ddbb..74fdead216b0 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -65,7 +65,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try this", - format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet), + format!("{var_snippet}.map_or_else({unwrap_snippet}, {map_snippet})"), Applicability::MachineApplicable, ); return true; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 428a354ec6b1..cfcf9596c50d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -109,13 +109,13 @@ use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does @@ -3255,65 +3255,59 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = cx.tcx.type_of(item.def_id); let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); - if_chain! { - if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; - if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next(); - - let method_sig = cx.tcx.fn_sig(impl_item.def_id.def_id); + if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { + let method_sig = cx.tcx.fn_sig(impl_item.def_id); let method_sig = cx.tcx.erase_late_bound_regions(method_sig); - - let first_arg_ty = method_sig.inputs().iter().next(); - - // check conventions w.r.t. conversion method names and predicates - if let Some(first_arg_ty) = first_arg_ty; - - then { - // if this impl block implements a trait, lint in trait definition instead - if !implements_trait && cx.access_levels.is_exported(impl_item.def_id.def_id) { - // check missing trait implementations - for method_config in &TRAIT_METHODS { - if name == method_config.method_name && - sig.decl.inputs.len() == method_config.param_count && - method_config.output_type.matches(&sig.decl.output) && - method_config.self_kind.matches(cx, self_ty, *first_arg_ty) && - fn_header_equals(method_config.fn_header, sig.header) && - method_config.lifetime_param_cond(impl_item) - { - span_lint_and_help( - cx, - SHOULD_IMPLEMENT_TRAIT, - impl_item.span, - &format!( - "method `{}` can be confused for the standard trait method `{}::{}`", - method_config.method_name, - method_config.trait_name, - method_config.method_name - ), - None, - &format!( - "consider implementing the trait `{}` or choosing a less ambiguous method name", - method_config.trait_name - ) - ); - } + let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait && cx.access_levels.is_exported(impl_item.def_id.def_id) { + // check missing trait implementations + for method_config in &TRAIT_METHODS { + if name == method_config.method_name + && sig.decl.inputs.len() == method_config.param_count + && method_config.output_type.matches(&sig.decl.output) + // in case there is no first arg, since we already have checked the number of arguments + // it's should be always true + && first_arg_ty_opt.map_or(true, |first_arg_ty| method_config + .self_kind.matches(cx, self_ty, first_arg_ty) + ) + && fn_header_equals(method_config.fn_header, sig.header) + && method_config.lifetime_param_cond(impl_item) + { + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + &format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, method_config.trait_name, method_config.method_name + ), + None, + &format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ), + ); } } + } - if sig.decl.implicit_self.has_implicit_self() + if sig.decl.implicit_self.has_implicit_self() && !(self.avoid_breaking_exported_api - && cx.access_levels.is_exported(impl_item.def_id.def_id)) + && cx.access_levels.is_exported(impl_item.def_id.def_id)) + && let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next() + && let Some(first_arg_ty) = first_arg_ty_opt { wrong_self_convention::check( cx, name, self_ty, - *first_arg_ty, + first_arg_ty, first_arg.pat.span, implements_trait, false ); } - } } // if this impl block implements a trait, lint in trait definition instead @@ -3799,7 +3793,6 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), - // FIXME: default doesn't work ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true), ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), @@ -3827,7 +3820,7 @@ enum SelfKind { Value, Ref, RefMut, - No, + No, // When we want the first argument type to be different than `Self` } impl SelfKind { diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index c409268de769..6fb92d1c663c 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -98,13 +98,12 @@ pub(super) fn check<'tcx>( format!(".as_ref().map({})", snippet(cx, map_arg.span, "..")) }; let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; - let hint = format!("{}.{}()", snippet(cx, as_ref_recv.span, ".."), method_hint); - let suggestion = format!("try using {} instead", method_hint); + let hint = format!("{}.{method_hint}()", snippet(cx, as_ref_recv.span, "..")); + let suggestion = format!("try using {method_hint} instead"); let msg = format!( - "called `{0}` on an Option value. This can be done more directly \ - by calling `{1}` instead", - current_method, hint + "called `{current_method}` on an Option value. This can be done more directly \ + by calling `{hint}` instead" ); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 6657cdccd010..3a23ecc50dc1 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_lang_ctor, path_def_id}; +use clippy_utils::{is_res_lang_ctor, path_def_id, path_res}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -51,22 +51,12 @@ pub(super) fn check<'tcx>( return; } - let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind { - is_lang_ctor(cx, qpath, OptionNone) - } else { - return; - }; - - if !default_arg_is_none { + if !is_res_lang_ctor(cx, path_res(cx, def_arg), OptionNone) { // nothing to lint! return; } - let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind { - is_lang_ctor(cx, qpath, OptionSome) - } else { - false - }; + let f_arg_is_some = is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome); if is_option { let self_snippet = snippet(cx, recv.span, ".."); @@ -87,7 +77,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try using `map` instead", - format!("{0}.map({1} {2})", self_snippet, arg_snippet,func_snippet), + format!("{self_snippet}.map({arg_snippet} {func_snippet})"), Applicability::MachineApplicable, ); } @@ -102,7 +92,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try using `and_then` instead", - format!("{0}.and_then({1})", self_snippet, func_snippet), + format!("{self_snippet}.and_then({func_snippet})"), Applicability::MachineApplicable, ); } else if f_arg_is_some { @@ -115,7 +105,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try using `ok` instead", - format!("{0}.ok()", self_snippet), + format!("{self_snippet}.ok()"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 3c4002a3aef9..30421a6dd5af 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -65,9 +65,8 @@ pub(super) fn check<'tcx>( "map_or(, )" }; let msg = &format!( - "called `map().unwrap_or({})` on an `Option` value. \ - This can be done more directly by calling `{}` instead", - arg, suggest + "called `map().unwrap_or({arg})` on an `Option` value. \ + This can be done more directly by calling `{suggest}` instead" ); span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| { @@ -82,10 +81,10 @@ pub(super) fn check<'tcx>( ]; if !unwrap_snippet_none { - suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{}, ", unwrap_snippet))); + suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, "))); } - diag.multipart_suggestion(&format!("use `{}` instead", suggest), suggestion, applicability); + diag.multipart_suggestion(&format!("use `{suggest}` instead"), suggestion, applicability); }); } } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index b43b9258c471..6a35024d0361 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -62,9 +62,9 @@ pub(super) fn check<'tcx>( cx, OR_FUN_CALL, method_span.with_hi(span.hi()), - &format!("use of `{}` followed by a call to `{}`", name, path), + &format!("use of `{name}` followed by a call to `{path}`"), "try this", - format!("{}()", sugg), + format!("{sugg}()"), Applicability::MachineApplicable, ); @@ -131,7 +131,7 @@ pub(super) fn check<'tcx>( if use_lambda { let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{}| {}", l_arg, snippet).into() + format!("|{l_arg}| {snippet}").into() } else { snippet } @@ -141,9 +141,9 @@ pub(super) fn check<'tcx>( cx, OR_FUN_CALL, span_replace_word, - &format!("use of `{}` followed by a function call", name), + &format!("use of `{name}` followed by a function call"), "try this", - format!("{}_{}({})", name, suffix, sugg), + format!("{name}_{suffix}({sugg})"), Applicability::HasPlaceholders, ); } diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index be5768c35450..55ba6e262df7 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,6 +1,6 @@ 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 clippy_utils::{diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::{lang_items::LangItem, Expr, ExprKind}; use rustc_lint::LateContext; @@ -58,8 +58,7 @@ pub(super) fn check<'tcx>( 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) + && is_res_lang_ctor(cx, path_res(cx, some_expr), item) { Some(arg.span) } else { diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 7572ba3fe9a9..324c9c17b5a9 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -30,10 +30,7 @@ pub(super) fn check<'tcx>( let option_check_method = if is_some { "is_some" } else { "is_none" }; // lint if caller of search is an Iterator if is_trait_method(cx, is_some_recv, sym::Iterator) { - let msg = format!( - "called `{}()` after searching an `Iterator` with `{}`", - option_check_method, search_method - ); + let msg = format!("called `{option_check_method}()` after searching an `Iterator` with `{search_method}`"); let search_snippet = snippet(cx, search_arg.span, ".."); if search_snippet.lines().count() <= 1 { // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` @@ -86,8 +83,7 @@ pub(super) fn check<'tcx>( &msg, "use `!_.any()` instead", format!( - "!{}.any({})", - iter, + "!{iter}.any({})", any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) ), applicability, @@ -119,7 +115,7 @@ pub(super) fn check<'tcx>( if is_string_or_str_slice(search_recv); if is_string_or_str_slice(search_arg); then { - let msg = format!("called `{}()` after calling `find()` on a string", option_check_method); + let msg = format!("called `{option_check_method}()` after calling `find()` on a string"); match option_check_method { "is_some" => { let mut applicability = Applicability::MachineApplicable; @@ -130,7 +126,7 @@ pub(super) fn check<'tcx>( method_span.with_hi(expr.span.hi()), &msg, "use `contains()` instead", - format!("contains({})", find_arg), + format!("contains({find_arg})"), applicability, ); }, @@ -144,7 +140,7 @@ pub(super) fn check<'tcx>( expr.span, &msg, "use `!_.contains()` instead", - format!("!{}.contains({})", string, find_arg), + format!("!{string}.contains({find_arg})"), applicability, ); }, diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs index 18b6b5be175d..44a7ad394fa0 100644 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ b/clippy_lints/src/methods/single_char_insert_string.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); - let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); + let sugg = format!("{base_string_snippet}.insert({pos_arg}, {extension_string})"); span_lint_and_sugg( cx, SINGLE_CHAR_ADD_STR, diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs index 9ea6751956ab..0698bd6a0c52 100644 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ b/clippy_lints/src/methods/single_char_push_string.rs @@ -13,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability) { let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let sugg = format!("{}.push({})", base_string_snippet, extension_string); + let sugg = format!("{base_string_snippet}.push({extension_string})"); span_lint_and_sugg( cx, SINGLE_CHAR_ADD_STR, diff --git a/clippy_lints/src/methods/stable_sort_primitive.rs b/clippy_lints/src/methods/stable_sort_primitive.rs index 91951c65bb30..09c8ca4cbe44 100644 --- a/clippy_lints/src/methods/stable_sort_primitive.rs +++ b/clippy_lints/src/methods/stable_sort_primitive.rs @@ -17,11 +17,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx cx, STABLE_SORT_PRIMITIVE, e.span, - &format!("used `sort` on primitive type `{}`", slice_type), + &format!("used `sort` on primitive type `{slice_type}`"), |diag| { let mut app = Applicability::MachineApplicable; let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0; - diag.span_suggestion(e.span, "try", format!("{}.sort_unstable()", recv_snip), app); + diag.span_suggestion(e.span, "try", format!("{recv_snip}.sort_unstable()"), app); diag.note( "an unstable sort typically performs faster without any observable difference for this data type", ); diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 9ca4d65550d3..ae3594bd36c3 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -2,11 +2,11 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::visitors::expr_visitor; +use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::Visitor; use rustc_hir::{ BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, }; @@ -211,7 +211,7 @@ fn indirect_usage<'tcx>( binding: HirId, ctxt: SyntaxContext, ) -> Option> { - if let StmtKind::Local(Local { + if let StmtKind::Local(&Local { pat: Pat { kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None), .. @@ -222,14 +222,12 @@ fn indirect_usage<'tcx>( }) = stmt.kind { let mut path_to_binding = None; - expr_visitor(cx, |expr| { - if path_to_local_id(expr, binding) { - path_to_binding = Some(expr); + let _: Option = for_each_expr_with_closures(cx, init_expr, |e| { + if path_to_local_id(e, binding) { + path_to_binding = Some(e); } - - path_to_binding.is_none() - }) - .visit_expr(init_expr); + ControlFlow::Continue(Descend::from(path_to_binding.is_none())) + }); let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id); let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?; @@ -250,7 +248,7 @@ fn indirect_usage<'tcx>( .. } = iter_usage { - if parent_id == *local_hir_id { + if parent_id == local_hir_id { return Some(IndirectUsage { name: ident.name, span: stmt.span, diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 143dcee35052..6974260f70db 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -34,9 +34,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr "calling `.extend(_.chars())`", "try this", format!( - "{}.push_str({}{})", + "{}.push_str({ref_str}{})", snippet_with_applicability(cx, recv.span, "..", &mut applicability), - ref_str, snippet_with_applicability(cx, target.span, "..", &mut applicability) ), applicability, diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index 55567d8625e5..219a9edd6576 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -24,10 +24,10 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se } let (msg, note_msg) = if count == 0 { - (format!("`{}` called with `0` splits", method_name), + (format!("`{method_name}` called with `0` splits"), "the resulting iterator will always return `None`") } else { - (format!("`{}` called with `1` split", method_name), + (format!("`{method_name}` called with `1` split"), if self_ty.is_slice() { "the resulting iterator will always return the entire slice followed by `None`" } else { diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index 6b306fbf0085..15c1c618c513 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -24,9 +24,9 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - cx, SUSPICIOUS_TO_OWNED, expr.span, - &format!("this `to_owned` call clones the {0} itself and does not cause the {0} contents to become owned", input_type), + &format!("this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned"), "consider using, depending on intent", - format!("{0}.clone()` or `{0}.into_owned()", recv_snip), + format!("{recv_snip}.clone()` or `{recv_snip}.into_owned()"), app, ); return true; diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 4e8c201f470b..1cef6226ad4f 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -2,9 +2,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; -use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id}; +use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; +use core::ops::ControlFlow; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -13,7 +14,7 @@ use rustc_span::sym; use super::UNNECESSARY_FILTER_MAP; use super::UNNECESSARY_FIND_MAP; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, name: &str) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, name: &str) { if !is_trait_method(cx, expr, sym::Iterator) { return; } @@ -26,10 +27,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value); - let mut return_visitor = ReturnVisitor::new(cx, arg_id); - return_visitor.visit_expr(body.value); - found_mapping |= return_visitor.found_mapping; - found_filtering |= return_visitor.found_filtering; + let _: Option = for_each_expr(body.value, |e| { + if let hir::ExprKind::Ret(Some(e)) = &e.kind { + let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e); + found_mapping |= found_mapping_res; + found_filtering |= found_filtering_res; + ControlFlow::Continue(Descend::No) + } else { + ControlFlow::Continue(Descend::Yes) + } + }); let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); let sugg = if !found_filtering { @@ -54,22 +61,20 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< UNNECESSARY_FIND_MAP }, expr.span, - &format!("this `.{}` can be written more simply using `.{}`", name, sugg), + &format!("this `.{name}` can be written more simply using `.{sugg}`"), ); } } // returns (found_mapping, found_filtering) fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { - match &expr.kind { + match expr.kind { hir::ExprKind::Call(func, args) => { - if let hir::ExprKind::Path(ref path) = func.kind { - if is_lang_ctor(cx, path, OptionSome) { - if path_to_local_id(&args[0], arg_id) { - return (false, false); - } - return (true, false); + if is_res_lang_ctor(cx, path_res(cx, func), OptionSome) { + if path_to_local_id(&args[0], arg_id) { + return (false, false); } + return (true, false); } (true, true) }, @@ -80,7 +85,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc hir::ExprKind::Match(_, arms, _) => { let mut found_mapping = false; let mut found_filtering = false; - for arm in *arms { + for arm in arms { let (m, f) = check_expression(cx, arg_id, arm.body); found_mapping |= m; found_filtering |= f; @@ -93,39 +98,9 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) }, - hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true), + hir::ExprKind::Path(ref path) if is_res_lang_ctor(cx, cx.qpath_res(path, expr.hir_id), OptionNone) => { + (false, true) + }, _ => (true, true), } } - -struct ReturnVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - arg_id: hir::HirId, - // Found a non-None return that isn't Some(input) - found_mapping: bool, - // Found a return that isn't Some - found_filtering: bool, -} - -impl<'a, 'tcx> ReturnVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>, arg_id: hir::HirId) -> ReturnVisitor<'a, 'tcx> { - ReturnVisitor { - cx, - arg_id, - found_mapping: false, - found_filtering: false, - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if let hir::ExprKind::Ret(Some(expr)) = &expr.kind { - let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr); - self.found_mapping |= found_mapping; - self.found_filtering |= found_filtering; - } else { - walk_expr(self, expr); - } - } -} diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index c17ef6809f91..aa87dead38f0 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -49,15 +49,12 @@ pub(super) fn check( let mut applicability = Applicability::MachineApplicable; let sugg = if replacement_has_args { format!( - "{replacement}(|{s}| {r})", - replacement = replacement_method_name, - s = second_arg_ident, + "{replacement_method_name}(|{second_arg_ident}| {r})", r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), ) } else { format!( - "{replacement}()", - replacement = replacement_method_name, + "{replacement_method_name}()", ) }; diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 95138c0e25b0..1966a85f7a73 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -68,7 +68,7 @@ pub fn check_for_loop_iter( cx, UNNECESSARY_TO_OWNED, expr.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), |diag| { // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to // a `to_owned`-like function was removed, then the next suggestion may be diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index a187a8d6016f..0e73459ad65f 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,7 +1,7 @@ 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}; +use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -18,6 +18,10 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, simplify_using: &str, ) { + if is_from_proc_macro(cx, expr) { + return; + } + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); @@ -58,8 +62,8 @@ pub(super) fn check<'tcx>( 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, "..")), + &format!("use `{simplify_using}(..)` instead"), + 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 559f32a563ed..9ab0d6141146 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -8,6 +8,7 @@ use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trai use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; +use rustc_hir_analysis::check::{FnCtxt, Inherited}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; @@ -18,7 +19,6 @@ use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitP use rustc_semver::RustcVersion; use rustc_span::{sym, Symbol}; use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; -use rustc_hir_analysis::check::{FnCtxt, Inherited}; use std::cmp::max; use super::UNNECESSARY_TO_OWNED; @@ -132,12 +132,11 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", format!( - "{:&>width$}{}", + "{:&>width$}{receiver_snippet}", "", - receiver_snippet, width = n_target_refs - n_receiver_refs ), Applicability::MachineApplicable, @@ -154,7 +153,7 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", receiver_snippet, Applicability::MachineApplicable, @@ -164,7 +163,7 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, expr.span.with_lo(receiver.span.hi()), - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "remove this", String::new(), Applicability::MachineApplicable, @@ -181,9 +180,9 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", - format!("{}.as_ref()", receiver_snippet), + format!("{receiver_snippet}.as_ref()"), Applicability::MachineApplicable, ); return true; @@ -228,9 +227,9 @@ fn check_into_iter_call_arg( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", - format!("{}.iter().{}()", receiver_snippet, cloned_or_copied), + format!("{receiver_snippet}.iter().{cloned_or_copied}()"), Applicability::MaybeIncorrect, ); return true; @@ -275,9 +274,9 @@ fn check_other_call_arg<'tcx>( cx, UNNECESSARY_TO_OWNED, maybe_arg.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", - format!("{:&>width$}{}", "", receiver_snippet, width = n_refs), + format!("{:&>n_refs$}{receiver_snippet}", ""), Applicability::MachineApplicable, ); return true; diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index ca5d33ee8b07..c1139d84e2f4 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; -use clippy_utils::{get_parent_expr, match_trait_method, paths}; +use clippy_utils::{get_parent_expr, is_trait_method}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::USELESS_ASREF; @@ -13,7 +14,7 @@ use super::USELESS_ASREF; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait - if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) { + if is_trait_method(cx, expr, sym::AsRef) || is_trait_method(cx, expr, sym::AsMut) { // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); @@ -35,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, cx, USELESS_ASREF, expr.span, - &format!("this call to `{}` does nothing", call_name), + &format!("this call to `{call_name}` does nothing"), "try this", snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), applicability, diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 4b368d3ffae2..1fbf783b8860 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -61,20 +61,20 @@ impl Convention { impl fmt::Display for Convention { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - Self::Eq(this) => format!("`{}`", this).fmt(f), - Self::StartsWith(this) => format!("`{}*`", this).fmt(f), - Self::EndsWith(this) => format!("`*{}`", this).fmt(f), - Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f), + Self::Eq(this) => format!("`{this}`").fmt(f), + Self::StartsWith(this) => format!("`{this}*`").fmt(f), + Self::EndsWith(this) => format!("`*{this}`").fmt(f), + Self::NotEndsWith(this) => format!("`~{this}`").fmt(f), Self::IsSelfTypeCopy(is_true) => { format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f) }, Self::ImplementsTrait(is_true) => { let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; - format!("method{} implement{} a trait", negation, s_suffix).fmt(f) + format!("method{negation} implement{s_suffix} a trait").fmt(f) }, Self::IsTraitItem(is_true) => { let suffix = if is_true { " is" } else { " is not" }; - format!("method{} a trait item", suffix).fmt(f) + format!("method{suffix} a trait item").fmt(f) }, } } @@ -138,8 +138,7 @@ pub(super) fn check<'tcx>( WRONG_SELF_CONVENTION, first_arg_span, &format!( - "{} usually take {}", - suggestion, + "{suggestion} usually take {}", &self_kinds .iter() .map(|k| k.description()) diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 4d8579135fc0..4f967755bfa1 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{match_trait_method, paths}; +use clippy_utils::is_trait_method; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -83,7 +83,7 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { - if cx.typeck_results().expr_ty(receiver).is_floating_point() || match_trait_method(cx, expr, &paths::ORD) { + if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { if path.ident.name == sym!(max) { fetch_const(cx, Some(receiver), args, MinMax::Max) } else if path.ident.name == sym!(min) { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index ea245edd7704..516dee20f8b1 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_opt}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -15,7 +14,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{ExpnKind, Span}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, iter_input_pats, last_path_segment, SpanlessEq}; +use clippy_utils::{get_parent_expr, in_constant, is_integer_literal, iter_input_pats, last_path_segment, SpanlessEq}; declare_clippy_lint! { /// ### What it does @@ -178,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { ("", sugg_init.addr()) }; let tyopt = if let Some(ty) = local.ty { - format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) + format!(": &{mutopt}{ty}", ty=snippet(cx, ty.span, "..")) } else { String::new() }; @@ -195,8 +194,6 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { format!( "let {name}{tyopt} = {initref};", name=snippet(cx, name.span, ".."), - tyopt=tyopt, - initref=initref, ), Applicability::MachineApplicable, ); @@ -222,8 +219,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { stmt.span, "replace it with", format!( - "if {} {{ {}; }}", - sugg, + "if {sugg} {{ {}; }}", &snippet(cx, b.span, ".."), ), Applicability::MachineApplicable, // snippet @@ -275,9 +271,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { USED_UNDERSCORE_BINDING, expr.span, &format!( - "used binding `{}` which is prefixed with an underscore. A leading \ - underscore signals that a binding will not be used", - binding + "used binding `{binding}` which is prefixed with an underscore. A leading \ + underscore signals that a binding will not be used" ), ); } @@ -318,8 +313,7 @@ fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool { fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { if_chain! { if let TyKind::Ptr(ref mut_ty) = ty.kind; - if let ExprKind::Lit(ref lit) = e.kind; - if let LitKind::Int(0, _) = lit.node; + if is_integer_literal(e, 0); if !in_constant(cx, e.hir_id); then { let (msg, sugg_fn) = match mut_ty.mutbl { @@ -328,12 +322,12 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) }; let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { - (format!("{}()", sugg_fn), Applicability::MachineApplicable) + (format!("{sugg_fn}()"), Applicability::MachineApplicable) } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { - (format!("{}::<{}>()", sugg_fn, mut_ty_snip), Applicability::MachineApplicable) + (format!("{sugg_fn}::<{mut_ty_snip}>()"), Applicability::MachineApplicable) } else { // `MaybeIncorrect` as type inference may not work with the suggested code - (format!("{}()", sugg_fn), Applicability::MaybeIncorrect) + (format!("{sugg_fn}()"), Applicability::MaybeIncorrect) }; span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); } diff --git a/clippy_lints/src/misc_early/literal_suffix.rs b/clippy_lints/src/misc_early/literal_suffix.rs index 1165c19a0cf0..62c6ca32d31a 100644 --- a/clippy_lints/src/misc_early/literal_suffix.rs +++ b/clippy_lints/src/misc_early/literal_suffix.rs @@ -18,9 +18,9 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s cx, SEPARATED_LITERAL_SUFFIX, lit.span, - &format!("{} type suffix should not be separated by an underscore", sugg_type), + &format!("{sugg_type} type suffix should not be separated by an underscore"), "remove the underscore", - format!("{}{}", &lit_snip[..maybe_last_sep_idx], suffix), + format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]), Applicability::MachineApplicable, ); } else { @@ -28,9 +28,9 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s cx, UNSEPARATED_LITERAL_SUFFIX, lit.span, - &format!("{} type suffix should be separated by an underscore", sugg_type), + &format!("{sugg_type} type suffix should be separated by an underscore"), "add an underscore", - format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), + format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 704918c0b979..c8227ca44505 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -357,9 +357,8 @@ impl EarlyLintPass for MiscEarlyLints { DUPLICATE_UNDERSCORE_ARGUMENT, *correspondence, &format!( - "`{}` already exists, having another argument having almost the same \ - name makes code comprehension and documentation more difficult", - arg_name + "`{arg_name}` already exists, having another argument having almost the same \ + name makes code comprehension and documentation more difficult" ), ); } diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs index fff533167ede..676e5d40bb77 100644 --- a/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { pat.span, "all the struct fields are matched to a wildcard pattern, consider using `..`", None, - &format!("try with `{} {{ .. }}` instead", type_name), + &format!("try with `{type_name} {{ .. }}` instead"), ); return; } @@ -63,7 +63,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { "you matched a field with a wildcard pattern, consider using `..` \ instead", None, - &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), + &format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", ")), ); } } diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 020efeaebf02..6dd76a6531e4 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -91,10 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { let type_name = segment.ident; for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() { if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) { - let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order", - type_name, impl_param_name); - let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params", - type_param_names[i], type_name); + let msg = format!("`{type_name}` has a similarly named generic type parameter `{impl_param_name}` in its declaration, but in a different order"); + let help = format!("try `{}`, or a name that does not conflict with `{type_name}`'s generic params", + type_param_names[i]); span_lint_and_help( cx, MISMATCHING_TYPE_PARAM_ORDER, diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 00376f0d7902..71cc0d0a81cd 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -8,12 +8,12 @@ use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 472195566768..b3f1553cfea9 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -103,7 +103,7 @@ impl MissingDoc { cx, MISSING_DOCS_IN_PRIVATE_ITEMS, sp, - &format!("missing documentation for {} {}", article, desc), + &format!("missing documentation for {article} {desc}"), ); } } diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 3d0a23822838..872679f25ab5 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -58,7 +58,8 @@ impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]); impl LateLintPass<'_> for ImportRename { fn check_crate(&mut self, cx: &LateContext<'_>) { for Rename { path, rename } in &self.conf_renames { - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &path.split("::").collect::>()) { + let segs = path.split("::").collect::>(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, None) { self.renames.insert(id, Symbol::intern(rename)); } } @@ -90,9 +91,7 @@ impl LateLintPass<'_> for ImportRename { "this import should be renamed", "try", format!( - "{} as {}", - import, - name, + "{import} as {name}", ), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 9d5764ac0926..655df5419ac6 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -65,7 +65,7 @@ fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, - &format!("missing `#[inline]` for {}", desc), + &format!("missing `#[inline]` for {desc}"), ); } } diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 102b9fbae83c..0742943dff2b 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -118,13 +118,7 @@ impl EarlyLintPass for ModStyle { SELF_NAMED_MODULE_FILES, Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), format!("`mod.rs` files are required, found `{}`", path.display()), - |lint| { - lint.help(format!( - "move `{}` to `{}`", - path.display(), - correct.display(), - )) - }, + |lint| lint.help(format!("move `{}` to `{}`", path.display(), correct.display(),)), ); } } diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 084c0d471dde..4547ed7eafc8 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -87,7 +87,7 @@ fn check_arguments<'tcx>( cx, UNNECESSARY_MUT_PASSED, argument.span, - &format!("the {} `{}` doesn't need a mutable reference", fn_kind, name), + &format!("the {fn_kind} `{name}` doesn't need a mutable reference"), ); } }, diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 44fdf84c6df7..d8647a991058 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -56,10 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { cx, DEBUG_ASSERT_WITH_MUT_CALL, span, - &format!( - "do not call a function with mutable arguments inside of `{}!`", - macro_name - ), + &format!("do not call a function with mutable arguments inside of `{macro_name}!`"), ); } } diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index a98577093ed5..09cb53331763 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -84,9 +84,8 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { let mutex_param = subst.type_at(0); if let Some(atomic_name) = get_atomic_name(mutex_param) { let msg = format!( - "consider using an `{}` instead of a `Mutex` here; if you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`", - atomic_name + "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ + behavior and not the internal type, consider using `Mutex<()>`" ); match *mutex_param.kind() { ty::Uint(t) if t != ty::UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg), diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index b8855e5adbff..10c3ff026b6d 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -1,6 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -8,36 +6,26 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// Checks for bindings that destructure a reference and borrow the inner + /// Checks for bindings that needlessly destructure a reference and borrow the inner /// value with `&ref`. /// /// ### Why is this bad? /// This pattern has no effect in almost all cases. /// - /// ### Known problems - /// In some cases, `&ref` is needed to avoid a lifetime mismatch error. - /// Example: - /// ```rust - /// fn foo(a: &Option, b: &Option) { - /// match (a, b) { - /// (None, &ref c) | (&ref c, None) => (), - /// (&Some(ref c), _) => (), - /// }; - /// } - /// ``` - /// /// ### Example /// ```rust /// let mut v = Vec::::new(); - /// # #[allow(unused)] /// v.iter_mut().filter(|&ref a| a.is_empty()); + /// + /// if let &[ref first, ref second] = v.as_slice() {} /// ``` /// /// Use instead: /// ```rust /// let mut v = Vec::::new(); - /// # #[allow(unused)] /// v.iter_mut().filter(|a| a.is_empty()); + /// + /// if let [first, second] = v.as_slice() {} /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_BORROWED_REFERENCE, @@ -54,34 +42,83 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { return; } - if_chain! { - // Only lint immutable refs, because `&mut ref T` may be useful. - if let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind; + // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms + for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) { + let Node::Pat(pat) = node else { break }; - // Check sub_pat got a `ref` keyword (excluding `ref mut`). - if let PatKind::Binding(BindingAnnotation::REF, .., spanned_name, _) = sub_pat.kind; - let parent_id = cx.tcx.hir().get_parent_node(pat.hir_id); - if let Some(parent_node) = cx.tcx.hir().find(parent_id); - then { - // do not recurse within patterns, as they may have other references - // XXXManishearth we can relax this constraint if we only check patterns - // with a single ref pattern inside them - if let Node::Pat(_) = parent_node { - return; - } - let mut applicability = Applicability::MachineApplicable; - span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span, - "this pattern takes a reference on something that is being de-referenced", - |diag| { - let hint = snippet_with_applicability(cx, spanned_name.span, "..", &mut applicability).into_owned(); - diag.span_suggestion( - pat.span, - "try removing the `&ref` part and just keep", - hint, - applicability, - ); - }); + if matches!(pat.kind, PatKind::Or(_)) { + return; } } + + // Only lint immutable refs, because `&mut ref T` may be useful. + let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return }; + + match sub_pat.kind { + // Check sub_pat got a `ref` keyword (excluding `ref mut`). + PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { + span_lint_and_then( + cx, + NEEDLESS_BORROWED_REFERENCE, + pat.span, + "this pattern takes a reference on something that is being dereferenced", + |diag| { + // `&ref ident` + // ^^^^^ + let span = pat.span.until(ident.span); + diag.span_suggestion_verbose( + span, + "try removing the `&ref` part", + String::new(), + Applicability::MachineApplicable, + ); + }, + ); + }, + // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]` + PatKind::Slice( + before, + None + | Some(Pat { + kind: PatKind::Wild, .. + }), + after, + ) => { + let mut suggestions = Vec::new(); + + for element_pat in itertools::chain(before, after) { + if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind { + // `&[..., ref ident, ...]` + // ^^^^ + let span = element_pat.span.until(ident.span); + suggestions.push((span, String::new())); + } else { + return; + } + } + + if !suggestions.is_empty() { + span_lint_and_then( + cx, + NEEDLESS_BORROWED_REFERENCE, + pat.span, + "dereferencing a slice pattern where every element takes a reference", + |diag| { + // `&[...]` + // ^ + let span = pat.span.until(sub_pat.span); + suggestions.push((span, String::new())); + + diag.multipart_suggestion( + "try removing the `&` and `ref` parts", + suggestions, + Applicability::MachineApplicable, + ); + }, + ); + } + }, + _ => {}, + } } } diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 98a3bce1ff38..6f0e755466e5 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -309,7 +309,7 @@ fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, expr.span, message, None, - &format!("{}\n{}", header, snip), + &format!("{header}\n{snip}"), ); } @@ -322,10 +322,7 @@ fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &' let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); format!( - "{indent}if {} {}\n{indent}{}", - cond_code, - continue_code, - else_code, + "{indent}if {cond_code} {continue_code}\n{indent}{else_code}", indent = " ".repeat(indent_if), ) } @@ -349,7 +346,7 @@ fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); let snip = snippet_block(cx, span, "..", None).into_owned(); snip.lines() - .map(|line| format!("{}{}", " ".repeat(indent), line)) + .map(|line| format!("{}{line}", " ".repeat(indent))) .collect::>() .join("\n") }) @@ -358,10 +355,7 @@ fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); format!( - "{indent_if}if {} {}\n{indent}// merged code follows:\n{}\n{indent_if}}}", - cond_code, - block_code, - to_annex, + "{indent_if}if {cond_code} {block_code}\n{indent}// merged code follows:\n{to_annex}\n{indent_if}}}", indent = " ".repeat(indent), indent_if = " ".repeat(indent_if), ) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index de99f1d7078e..9d26e5900866 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -2,9 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_opt; use clippy_utils::ty::needs_ordered_drop; -use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used}; +use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_local_used}; +use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::intravisit::Visitor; use rustc_hir::{ BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind, @@ -64,31 +64,25 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]); fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool { - let mut seen = false; - expr_visitor(cx, |expr| { - if let ExprKind::Assign(..) = expr.kind { - seen = true; + for_each_expr_with_closures(cx, stmt, |e| { + if matches!(e.kind, ExprKind::Assign(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - - !seen }) - .visit_stmt(stmt); - - seen + .is_some() } fn contains_let(cond: &Expr<'_>) -> bool { - let mut seen = false; - expr_visitor_no_bodies(|expr| { - if let ExprKind::Let(_) = expr.kind { - seen = true; + for_each_expr(cond, |e| { + if matches!(e.kind, ExprKind::Let(_)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - - !seen }) - .visit_expr(cond); - - seen + .is_some() } fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { @@ -287,7 +281,7 @@ fn check<'tcx>( diag.span_suggestion( assign.lhs_span, - &format!("declare `{}` here", binding_name), + &format!("declare `{binding_name}` here"), let_snippet, Applicability::MachineApplicable, ); @@ -307,8 +301,8 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{}` here", binding_name), - format!("{} = ", let_snippet), + &format!("declare `{binding_name}` here"), + format!("{let_snippet} = "), applicability, ); @@ -338,8 +332,8 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{}` here", binding_name), - format!("{} = ", let_snippet), + &format!("declare `{binding_name}` here"), + format!("{let_snippet} = "), applicability, ); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 4f46872439c3..178c973981b1 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -12,17 +12,17 @@ use rustc_hir::{ BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind, }; use rustc_hir::{HirIdMap, HirIdSet}; +use rustc_hir_analysis::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::kw; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::can_type_implement_copy; -use rustc_hir_analysis::expr_use_visitor as euv; use std::borrow::Cow; declare_clippy_lint! { @@ -186,6 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { if !is_self(arg); if !ty.is_mutable_ptr(); if !is_copy(cx, ty); + if ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env); if !allowed_traits.iter().any(|&t| implements_trait(cx, ty, t, &[])); if !implements_borrow_trait; if !all_borrowable_trait; @@ -236,7 +237,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { snippet_opt(cx, span) .map_or( "change the call to".into(), - |x| Cow::from(format!("change `{}` to", x)), + |x| Cow::from(format!("change `{x}` to")), ) .as_ref(), suggestion, @@ -266,7 +267,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { snippet_opt(cx, span) .map_or( "change the call to".into(), - |x| Cow::from(format!("change `{}` to", x)) + |x| Cow::from(format!("change `{x}` to")) ) .as_ref(), suggestion, @@ -341,5 +342,11 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 8f85b00596c0..97c8cfbd3eb7 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_lang_ctor; +use clippy_utils::path_res; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionSome, ResultOk}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{AsyncGeneratorKind, Block, Body, Expr, ExprKind, GeneratorKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::DefIdTree; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -112,11 +113,12 @@ impl LateLintPass<'_> for NeedlessQuestionMark { fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Call(path, [arg]) = &expr.kind; - if let ExprKind::Path(ref qpath) = &path.kind; - let sugg_remove = if is_lang_ctor(cx, qpath, OptionSome) { + if let ExprKind::Call(path, [arg]) = expr.kind; + if let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path); + if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); + let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { "Some()" - } else if is_lang_ctor(cx, qpath, ResultOk) { + } else if cx.tcx.lang_items().result_ok_variant() == Some(variant_id) { "Ok()" } else { return; @@ -134,7 +136,7 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { NEEDLESS_QUESTION_MARK, expr.span, "question mark operator is useless here", - &format!("try removing question mark and `{}`", sugg_remove), + &format!("try removing question mark and `{sugg_remove}`"), format!("{}", snippet(cx, inner_expr.span, r#""...""#)), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index b087cfb36b11..fb9a4abd0b4b 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -62,9 +62,9 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { let mut applicability = Applicability::MachineApplicable; let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability); let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { - format!("-({})", snip) + format!("-({snip})") } else { - format!("-{}", snip) + format!("-{snip}") }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 357a71693d2c..6017117e1ecc 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -136,8 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { id, impl_item.span, &format!( - "you should consider adding a `Default` implementation for `{}`", - self_type_snip + "you should consider adding a `Default` implementation for `{self_type_snip}`" ), |diag| { diag.suggest_prepend_item( @@ -161,9 +160,9 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { fn create_new_without_default_suggest_msg(self_type_snip: &str, generics_sugg: &str) -> String { #[rustfmt::skip] format!( -"impl{} Default for {} {{ +"impl{generics_sugg} Default for {self_type_snip} {{ fn default() -> Self {{ Self::new() }} -}}", generics_sugg, self_type_snip) +}}") } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 48ff737dae7b..2c839d029c6f 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{ BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, }; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::mir; use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; @@ -20,7 +21,6 @@ use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, InnerSpan, Span, DUMMY_SP}; -use rustc_hir_analysis::hir_ty_to_ty; // FIXME: this is a correctness problem but there's no suitable // warn-by-default category. @@ -149,6 +149,9 @@ fn is_value_unfrozen_raw<'tcx>( // the fact that we have to dig into every structs to search enums // leads us to the point checking `UnsafeCell` directly is the only option. ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true, + // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the + // contained value. + ty::Adt(def, ..) if def.is_union() => false, ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => { let val = cx.tcx.destructure_mir_constant(cx.param_env, val); val.fields.iter().any(|field| inner(cx, *field)) diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index a7cd1f6d0652..9f6917c146f6 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -112,10 +112,7 @@ impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> { self.cx, MANY_SINGLE_CHAR_NAMES, span, - &format!( - "{} bindings with single-character names in scope", - num_single_char_names - ), + &format!("{num_single_char_names} bindings with single-character names in scope"), ); } } diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 25fb4f0f4cff..1a765b14892f 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use clippy_utils::ty::match_type; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; 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::sym; declare_clippy_lint! { /// ### What it does @@ -49,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { if_chain! { if (path.ident.name == sym!(mode) && (match_type(cx, obj_ty, &paths::OPEN_OPTIONS) - || match_type(cx, obj_ty, &paths::DIR_BUILDER))) + || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder))) || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS)); if let ExprKind::Lit(_) = param.kind; diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index f86dfb6b8df8..0ca0befc1351 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -3,16 +3,17 @@ use std::{ hash::{Hash, Hasher}, }; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; use serde::{de, Deserialize}; declare_clippy_lint! { @@ -39,8 +40,8 @@ declare_clippy_lint! { const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")]; -/// The (name, (open brace, close brace), source snippet) -type MacroInfo<'a> = (Symbol, &'a (String, String), String); +/// The (callsite span, (open brace, close brace), source snippet) +type MacroInfo<'a> = (Span, &'a (String, String), String); #[derive(Clone, Debug, Default)] pub struct MacroBraces { @@ -62,33 +63,29 @@ impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]); impl EarlyLintPass for MacroBraces { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let Some((name, braces, snip)) = is_offending_macro(cx, item.span, self) { - let span = item.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, item.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { - if let Some((name, braces, snip)) = is_offending_macro(cx, stmt.span, self) { - let span = stmt.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, stmt.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let Some((name, braces, snip)) = is_offending_macro(cx, expr.span, self) { - let span = expr.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, expr.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { - if let Some((name, braces, snip)) = is_offending_macro(cx, ty.span, self) { - let span = ty.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, ty.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } @@ -102,48 +99,44 @@ fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a Mac .last() .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local)) }; + let span_call_site = span.ctxt().outer_expn_data().call_site; if_chain! { if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind; let name = mac_name.as_str(); if let Some(braces) = mac_braces.macro_braces.get(name); - if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site); + if let Some(snip) = snippet_opt(cx, span_call_site); // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 - if snip.starts_with(&format!("{}!", name)); + if snip.starts_with(&format!("{name}!")); if unnested_or_local(); // make formatting consistent let c = snip.replace(' ', ""); - if !c.starts_with(&format!("{}!{}", name, braces.0)); - if !mac_braces.done.contains(&span.ctxt().outer_expn_data().call_site); + if !c.starts_with(&format!("{name}!{}", braces.0)); + if !mac_braces.done.contains(&span_call_site); then { - Some((mac_name, braces, snip)) + Some((span_call_site, braces, snip)) } else { None } } } -fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: Symbol, span: Span) { - let with_space = &format!("! {}", braces.0); - let without_space = &format!("!{}", braces.0); - let mut help = snip; - for b in BRACES.iter().filter(|b| b.0 != braces.0) { - help = help.replace(b.0, &braces.0).replace(b.1, &braces.1); - // Only `{` traditionally has space before the brace - if braces.0 != "{" && help.contains(with_space) { - help = help.replace(with_space, without_space); - } else if braces.0 == "{" && help.contains(without_space) { - help = help.replace(without_space, with_space); - } +fn emit_help(cx: &EarlyContext<'_>, snip: &str, braces: &(String, String), span: Span) { + if let Some((macro_name, macro_args_str)) = snip.split_once('!') { + let mut macro_args = macro_args_str.trim().to_string(); + // now remove the wrong braces + macro_args.remove(0); + macro_args.pop(); + span_lint_and_sugg( + cx, + NONSTANDARD_MACRO_BRACES, + span, + &format!("use of irregular braces for `{macro_name}!` macro"), + "consider writing", + format!("{macro_name}!{}{macro_args}{}", braces.0, braces.1), + Applicability::MachineApplicable, + ); } - span_lint_and_help( - cx, - NONSTANDARD_MACRO_BRACES, - span, - &format!("use of irregular braces for `{}!` macro", name), - Some(span), - &format!("consider writing `{}`", help), - ); } fn macro_braces(conf: FxHashSet) -> FxHashMap { @@ -273,9 +266,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { .iter() .find(|b| b.0 == brace) .map(|(o, c)| ((*o).to_owned(), (*c).to_owned())) - .ok_or_else(|| { - de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{}`", brace)) - })?, + .ok_or_else(|| de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, }) } } diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index bffbf20b4d28..f380a5065827 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -102,7 +102,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { // construct a replacement escape // the maximum value is \077, or \x3f, so u8 is sufficient here if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) { - write!(suggest_1, "\\x{:02x}", n).unwrap(); + write!(suggest_1, "\\x{n:02x}").unwrap(); } // append the null byte as \x00 and the following digits literally diff --git a/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs index 1ec4240afefe..d29ca37eaeb8 100644 --- a/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -34,13 +34,12 @@ pub(super) fn check<'tcx>( }; let help = format!( - "because `{}` is the {} value for this type, {}", + "because `{}` is the {} value for this type, {conclusion}", snippet(cx, culprit.expr.span, "x"), match culprit.which { ExtremeType::Minimum => "minimum", ExtremeType::Maximum => "maximum", - }, - conclusion + } ); span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help); diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index d24c57c0a4b8..8827daaa3ee7 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,8 +1,3 @@ -#![allow( - // False positive - clippy::match_same_arms -)] - use super::ARITHMETIC_SIDE_EFFECTS; use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; use rustc_ast as ast; @@ -14,11 +9,12 @@ use rustc_session::impl_lint_pass; use rustc_span::source_map::{Span, Spanned}; const HARD_CODED_ALLOWED: &[&str] = &[ + "&str", "f32", "f64", "std::num::Saturating", - "std::string::String", "std::num::Wrapping", + "std::string::String", ]; #[derive(Debug)] @@ -45,61 +41,59 @@ impl ArithmeticSideEffects { /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a /// non-constant environment that won't overflow. fn has_valid_op(op: &Spanned, expr: &hir::Expr<'_>) -> bool { - if let hir::BinOpKind::Add | hir::BinOpKind::Sub = op.node - && let hir::ExprKind::Lit(ref lit) = expr.kind - && let ast::LitKind::Int(0, _) = lit.node + if let hir::ExprKind::Lit(ref lit) = expr.kind && + let ast::LitKind::Int(value, _) = lit.node { - return true; + match (&op.node, value) { + (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, + (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) + | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) + | (hir::BinOpKind::Mul, 0 | 1) => true, + _ => false, + } + } else { + false } - if let hir::BinOpKind::Div | hir::BinOpKind::Rem = op.node - && let hir::ExprKind::Lit(ref lit) = expr.kind - && !matches!(lit.node, ast::LitKind::Int(0, _)) - { - return true; - } - if let hir::BinOpKind::Mul = op.node - && let hir::ExprKind::Lit(ref lit) = expr.kind - && let ast::LitKind::Int(0 | 1, _) = lit.node - { - return true; - } - false } /// Checks if the given `expr` has any of the inner `allowed` elements. - fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - self.allowed.contains( - cx.typeck_results() - .expr_ty(expr) - .to_string() - .split('<') - .next() - .unwrap_or_default(), - ) + fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { + self.allowed + .contains(ty.to_string().split('<').next().unwrap_or_default()) } - /// Explicit integers like `1` or `i32::MAX`. Does not take into consideration references. - fn is_literal_integer(expr: &hir::Expr<'_>, expr_refs: Ty<'_>) -> bool { - let is_integral = expr_refs.is_integral(); - let is_literal = matches!(expr.kind, hir::ExprKind::Lit(_)); - is_integral && is_literal + // For example, 8i32 or &i64::MAX. + fn is_integral(ty: Ty<'_>) -> bool { + ty.peel_refs().is_integral() } + // Common entry-point to avoid code duplication. fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { let msg = "arithmetic operation that can potentially result in unexpected side-effects"; span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg); self.expr_span = Some(expr.span); } + /// If `expr` does not match any variant of `LiteralIntegerTy`, returns `None`. + fn literal_integer<'expr, 'tcx>(expr: &'expr hir::Expr<'tcx>) -> Option> { + if matches!(expr.kind, hir::ExprKind::Lit(_)) { + return Some(LiteralIntegerTy::Value(expr)); + } + if let hir::ExprKind::AddrOf(.., inn) = expr.kind && let hir::ExprKind::Lit(_) = inn.kind { + return Some(LiteralIntegerTy::Ref(inn)); + } + None + } + /// Manages when the lint should be triggered. Operations in constant environments, hard coded /// types, custom allowed types and non-constant operations that won't overflow are ignored. - fn manage_bin_ops( + fn manage_bin_ops<'tcx>( &mut self, - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, + cx: &LateContext<'tcx>, + expr: &hir::Expr<'tcx>, op: &Spanned, - lhs: &hir::Expr<'_>, - rhs: &hir::Expr<'_>, + lhs: &hir::Expr<'tcx>, + rhs: &hir::Expr<'tcx>, ) { if constant_simple(cx, cx.typeck_results(), expr).is_some() { return; @@ -116,17 +110,20 @@ impl ArithmeticSideEffects { ) { return; }; - if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) { + let lhs_ty = cx.typeck_results().expr_ty(lhs); + let rhs_ty = cx.typeck_results().expr_ty(rhs); + let lhs_and_rhs_have_the_same_ty = lhs_ty == rhs_ty; + if lhs_and_rhs_have_the_same_ty && self.is_allowed_ty(lhs_ty) && self.is_allowed_ty(rhs_ty) { return; } - let has_valid_op = match ( - Self::is_literal_integer(lhs, cx.typeck_results().expr_ty(lhs).peel_refs()), - Self::is_literal_integer(rhs, cx.typeck_results().expr_ty(rhs).peel_refs()), - ) { - (true, true) => true, - (true, false) => Self::has_valid_op(op, lhs), - (false, true) => Self::has_valid_op(op, rhs), - (false, false) => false, + let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { + match (Self::literal_integer(lhs), Self::literal_integer(rhs)) { + (None, Some(lit_int_ty)) | (Some(lit_int_ty), None) => Self::has_valid_op(op, lit_int_ty.into()), + (Some(LiteralIntegerTy::Value(_)), Some(LiteralIntegerTy::Value(_))) => true, + (None, None) | (Some(_), Some(_)) => false, + } + } else { + false }; if !has_valid_op { self.issue_lint(cx, expr); @@ -135,7 +132,7 @@ impl ArithmeticSideEffects { } impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) { return; } @@ -180,3 +177,22 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { } } } + +/// Tells if an expression is a integer declared by value or by reference. +/// +/// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather +/// than `hirExprKind::Addr`. +enum LiteralIntegerTy<'expr, 'tcx> { + /// For example, `&199` + Ref(&'expr hir::Expr<'tcx>), + /// For example, `1` or `i32::MAX` + Value(&'expr hir::Expr<'tcx>), +} + +impl<'expr, 'tcx> From> for &'expr hir::Expr<'tcx> { + fn from(from: LiteralIntegerTy<'expr, 'tcx>) -> Self { + match from { + LiteralIntegerTy::Ref(elem) | LiteralIntegerTy::Value(elem) => elem, + } + } +} diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index f134c6c4cdba..26bca7c306a8 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -2,16 +2,17 @@ use clippy_utils::binop_traits; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{eq_expr_value, trait_ref_of_method}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::BorrowKind; use rustc_trait_selection::infer::TyCtxtInferExt; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use super::ASSIGN_OP_PATTERN; @@ -55,7 +56,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( expr.span, "replace it with", - format!("{} {}= {}", snip_a, op.node.as_str(), snip_r), + format!("{snip_a} {}= {snip_r}", op.node.as_str()), Applicability::MachineApplicable, ); } @@ -65,15 +66,19 @@ pub(super) fn check<'tcx>( } }; - let mut visitor = ExprVisitor { - assignee, - counter: 0, - cx, - }; + let mut found = false; + let found_multiple = for_each_expr(e, |e| { + if eq_expr_value(cx, assignee, e) { + if found { + return ControlFlow::Break(()); + } + found = true; + } + ControlFlow::Continue(()) + }) + .is_some(); - walk_expr(&mut visitor, e); - - if visitor.counter == 1 { + if found && !found_multiple { // a = a op b if eq_expr_value(cx, assignee, l) { lint(assignee, r); @@ -98,22 +103,6 @@ pub(super) fn check<'tcx>( } } -struct ExprVisitor<'a, 'tcx> { - assignee: &'a hir::Expr<'a>, - counter: u8, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if eq_expr_value(self.cx, self.assignee, expr) { - self.counter += 1; - } - - walk_expr(self, expr); - } -} - fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet { struct S(hir::HirIdSet); impl Delegate<'_> for S { diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs index 74387fbc87be..1369b3e74625 100644 --- a/clippy_lints/src/operators/bit_mask.rs +++ b/clippy_lints/src/operators/bit_mask.rs @@ -64,10 +64,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` can never be equal to `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` can never be equal to `{cmp_value}`"), ); } } else if mask_value == 0 { @@ -80,10 +77,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` can never be equal to `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` can never be equal to `{cmp_value}`"), ); } }, @@ -96,10 +90,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` will always be lower than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` will always be lower than `{cmp_value}`"), ); } else if mask_value == 0 { span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); @@ -111,10 +102,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` will never be lower than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` will never be lower than `{cmp_value}`"), ); } else { check_ineffective_lt(cx, span, mask_value, cmp_value, "|"); @@ -130,10 +118,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` will never be higher than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` will never be higher than `{cmp_value}`"), ); } else if mask_value == 0 { span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); @@ -145,10 +130,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` will always be higher than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` will always be higher than `{cmp_value}`"), ); } else { check_ineffective_gt(cx, span, mask_value, cmp_value, "|"); @@ -167,10 +149,7 @@ fn check_ineffective_lt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: cx, INEFFECTIVE_BIT_MASK, span, - &format!( - "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly", - op, m, c - ), + &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), ); } } @@ -181,10 +160,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: cx, INEFFECTIVE_BIT_MASK, span, - &format!( - "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly", - op, m, c - ), + &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), ); } } diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index 638a514ff9b3..c9c777f1bd8d 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -99,7 +99,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) let expr_snip; let eq_impl; if with_deref.is_implemented() { - expr_snip = format!("*{}", arg_snip); + expr_snip = format!("*{arg_snip}"); eq_impl = with_deref; } else { expr_snip = arg_snip.to_string(); @@ -121,17 +121,15 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) }; if eq_impl.ty_eq_other { hint = format!( - "{}{}{}", - expr_snip, + "{expr_snip}{}{}", snippet(cx, cmp_span, ".."), snippet(cx, other.span, "..") ); } else { hint = format!( - "{}{}{}", + "{}{}{expr_snip}", snippet(cx, other.span, ".."), - snippet(cx, cmp_span, ".."), - expr_snip + snippet(cx, cmp_span, "..") ); } } diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index 827a2b267093..49e662cacb0c 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -31,12 +31,11 @@ pub(crate) fn check<'tcx>( cx, DURATION_SUBSEC, expr.span, - &format!("calling `{}()` is more concise than this calculation", suggested_fn), + &format!("calling `{suggested_fn}()` is more concise than this calculation"), "try", format!( - "{}.{}()", - snippet_with_applicability(cx, self_arg.span, "_", &mut applicability), - suggested_fn + "{}.{suggested_fn}()", + snippet_with_applicability(cx, self_arg.span, "_", &mut applicability) ), applicability, ); diff --git a/clippy_lints/src/operators/eq_op.rs b/clippy_lints/src/operators/eq_op.rs index 44cf0bb06120..67913f7392c0 100644 --- a/clippy_lints/src/operators/eq_op.rs +++ b/clippy_lints/src/operators/eq_op.rs @@ -22,7 +22,7 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { cx, EQ_OP, lhs.span.to(rhs.span), - &format!("identical args used in this `{}!` macro call", macro_name), + &format!("identical args used in this `{macro_name}!` macro call"), ); } } diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index 0024384d9278..ae805147f07a 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -47,18 +47,14 @@ fn lint_misrefactored_assign_op( if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) { let a = &sugg::Sugg::hir(cx, assignee, ".."); let r = &sugg::Sugg::hir(cx, rhs, ".."); - let long = format!("{} = {}", snip_a, sugg::make_binop(op.into(), a, r)); + let long = format!("{snip_a} = {}", sugg::make_binop(op.into(), a, r)); diag.span_suggestion( expr.span, &format!( - "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with", - snip_a, - snip_a, - op.as_str(), - snip_r, - long + "did you mean `{snip_a} = {snip_a} {} {snip_r}` or `{long}`? Consider replacing it with", + op.as_str() ), - format!("{} {}= {}", snip_a, op.as_str(), snip_r), + format!("{snip_a} {}= {snip_r}", op.as_str()), Applicability::MaybeIncorrect, ); diag.span_suggestion( diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index c32b4df4f75c..b8a20d5ebe9b 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -67,7 +67,7 @@ declare_clippy_lint! { /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), /// or can panic (`/`, `%`). /// - /// Known safe built-in types like `Wrapping` or `Saturing`, floats, operations in constant + /// Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant /// environments, allowed types and non-constant operations that won't overflow are ignored. /// /// ### Why is this bad? diff --git a/clippy_lints/src/operators/needless_bitwise_bool.rs b/clippy_lints/src/operators/needless_bitwise_bool.rs index e902235a014e..ab5fb1787004 100644 --- a/clippy_lints/src/operators/needless_bitwise_bool.rs +++ b/clippy_lints/src/operators/needless_bitwise_bool.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Exp if let Some(lhs_snip) = snippet_opt(cx, lhs.span) && let Some(rhs_snip) = snippet_opt(cx, rhs.span) { - let sugg = format!("{} {} {}", lhs_snip, op_str, rhs_snip); + let sugg = format!("{lhs_snip} {op_str} {rhs_snip}"); diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index b6097710dc68..0830a106f556 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::constant_simple; use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_integer_literal; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; @@ -50,11 +51,9 @@ impl Context { hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { hir::ExprKind::Lit(_lit) => (), hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { - if let hir::ExprKind::Lit(lit) = &expr.kind { - if let rustc_ast::ast::LitKind::Int(1, _) = lit.node { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - } + if is_integer_literal(expr, 1) { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_id = Some(expr.hir_id); } }, _ => { diff --git a/clippy_lints/src/operators/ptr_eq.rs b/clippy_lints/src/operators/ptr_eq.rs index 1aefc2741c21..1229c202f5a0 100644 --- a/clippy_lints/src/operators/ptr_eq.rs +++ b/clippy_lints/src/operators/ptr_eq.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( expr.span, LINT_MSG, "try", - format!("std::ptr::eq({}, {})", left_snip, right_snip), + format!("std::ptr::eq({left_snip}, {right_snip})"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/operators/self_assignment.rs b/clippy_lints/src/operators/self_assignment.rs index 9d6bec05bf09..7c9d5320a3a8 100644 --- a/clippy_lints/src/operators/self_assignment.rs +++ b/clippy_lints/src/operators/self_assignment.rs @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, lhs: &'tcx cx, SELF_ASSIGNMENT, e.span, - &format!("self-assignment of `{}` to `{}`", rhs, lhs), + &format!("self-assignment of `{rhs}` to `{lhs}`"), ); } } diff --git a/clippy_lints/src/operators/verbose_bit_mask.rs b/clippy_lints/src/operators/verbose_bit_mask.rs index ff85fd554298..fbf65e92b322 100644 --- a/clippy_lints/src/operators/verbose_bit_mask.rs +++ b/clippy_lints/src/operators/verbose_bit_mask.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "try", - format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()), + format!("{sugg}.trailing_zeros() >= {}", n.count_ones()), Applicability::MaybeIncorrect, ); }, diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 0315678bf97a..4eb42da1fed0 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::{ - can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks, + can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, CaptureKind, }; use if_chain::if_chain; @@ -88,7 +88,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); /// None/_ => {..} /// } /// ``` -struct OptionOccurence { +struct OptionOccurrence { option: String, method_sugg: String, some_expr: String, @@ -109,13 +109,13 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo ) } -fn try_get_option_occurence<'tcx>( +fn try_get_option_occurrence<'tcx>( cx: &LateContext<'tcx>, pat: &Pat<'tcx>, expr: &Expr<'_>, if_then: &'tcx Expr<'_>, if_else: &'tcx Expr<'_>, -) -> Option { +) -> Option { let cond_expr = match expr.kind { ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr, _ => expr, @@ -160,10 +160,10 @@ fn try_get_option_occurence<'tcx>( } } - return Some(OptionOccurence { + return Some(OptionOccurrence { option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), - some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")), + some_expr: format!("|{capture_mut}{capture_name}| {}", Sugg::hir_with_macro_callsite(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")), }); } @@ -174,7 +174,8 @@ fn try_get_option_occurence<'tcx>( fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk) { + let res = cx.qpath_res(qpath, pat.hir_id); + if is_res_lang_ctor(cx, res, OptionSome) || is_res_lang_ctor(cx, res, ResultOk) { return Some(inner_pat); } } @@ -182,9 +183,9 @@ fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&' } /// If this expression is the option if let/else construct we're detecting, then -/// this function returns an `OptionOccurence` struct with details if +/// this function returns an `OptionOccurrence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { +fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if let Some(higher::IfLet { let_pat, let_expr, @@ -193,16 +194,16 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> }) = higher::IfLet::hir(cx, expr) { if !is_else_clause(cx.tcx, expr) { - return try_get_option_occurence(cx, let_pat, let_expr, if_then, if_else); + return try_get_option_occurrence(cx, let_pat, let_expr, if_then, if_else); } } None } -fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { +fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind { if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) { - return try_get_option_occurence(cx, let_pat, ex, if_then, if_else); + return try_get_option_occurrence(cx, let_pat, ex, if_then, if_else); } } None @@ -226,9 +227,10 @@ fn try_convert_match<'tcx>( fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { match arm.pat.kind { - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [first_pat], _) => { - is_lang_ctor(cx, qpath, ResultErr) && matches!(first_pat.kind, PatKind::Wild) + is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) + && matches!(first_pat.kind, PatKind::Wild) }, PatKind::Wild => true, _ => false, diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 4aa0d9227aba..efec12489a9b 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -2,9 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::return_ty; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::expr_visitor_no_bodies; +use clippy_utils::visitors::{for_each_expr, Descend}; +use core::ops::ControlFlow; use rustc_hir as hir; -use rustc_hir::intravisit::{FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; @@ -58,18 +59,20 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { let mut panics = Vec::new(); - expr_visitor_no_bodies(|expr| { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true }; + let _: Option = for_each_expr(body.value, |e| { + let Some(macro_call) = root_macro_call_first_node(cx, e) else { + return ControlFlow::Continue(Descend::Yes); + }; if matches!( cx.tcx.item_name(macro_call.def_id).as_str(), "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne" ) { panics.push(macro_call.span); - return false; + ControlFlow::Continue(Descend::No) + } else { + ControlFlow::Continue(Descend::Yes) } - true - }) - .visit_expr(body.value); + }); if !panics.is_empty() { span_lint_and_then( cx, diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 000b0ba7a148..6810a2431758 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -1,5 +1,5 @@ use clippy_utils::{ - diagnostics::span_lint_and_sugg, is_lang_ctor, peel_hir_expr_refs, peel_ref_operators, sugg, + diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg, ty::is_type_diagnostic_item, }; use rustc_errors::Applicability; @@ -54,8 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { !expr.span.from_expansion() - && matches!(&peel_hir_expr_refs(expr).0.kind, - ExprKind::Path(p) if is_lang_ctor(cx, p, LangItem::OptionNone)) + && is_res_lang_ctor(cx, path_res(cx, peel_hir_expr_refs(expr).0), LangItem::OptionNone) }; let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 6b2eea489322..45e98de10ace 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -209,7 +209,7 @@ impl<'tcx> PassByRefOrValue { cx, TRIVIALLY_COPY_PASS_BY_REF, input.span, - &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size), + &format!("this argument ({size} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", self.ref_min_size), "consider passing by value instead", value_type, Applicability::Unspecified, @@ -237,7 +237,7 @@ impl<'tcx> PassByRefOrValue { cx, LARGE_TYPES_PASSED_BY_VALUE, input.span, - &format!("this argument ({} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", size, self.value_max_size), + &format!("this argument ({size} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", self.value_max_size), "consider passing by reference instead", format!("&{}", snippet(cx, input.span, "_")), Applicability::MaybeIncorrect, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 41d1baba64f8..d296a150b46d 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -463,7 +463,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( diag.span_suggestion( hir_ty.span, "change this to", - format!("&{}{}", mutability.prefix_str(), ty_name), + format!("&{}{ty_name}", mutability.prefix_str()), Applicability::Unspecified, ); } @@ -669,7 +669,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: } fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { - if let TyKind::Rptr(ref lt, ref m) = ty.kind { + if let TyKind::Rptr(lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) } else { None diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 4dc65da3ea1f..b0a5d1a67582 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { None => return, }; - let msg = format!("use of `{}` with a `usize` casted to an `isize`", method); + let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) { span_lint_and_sugg( cx, @@ -124,7 +124,7 @@ fn build_suggestion<'tcx>( ) -> Option { let receiver = snippet_opt(cx, receiver_expr.span)?; let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?; - Some(format!("{}.{}({})", receiver, method.suggestion(), cast_lhs)) + Some(format!("{receiver}.{}({cast_lhs})", method.suggestion())) } #[derive(Copy, Clone)] diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 569870ab2b7f..328371fd602f 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -3,11 +3,12 @@ use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ - eq_expr_value, get_parent_node, is_else_clause, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, + eq_expr_value, get_parent_node, in_constant, is_else_clause, is_res_lang_ctor, path_to_local, path_to_local_id, + peel_blocks, peel_blocks_with_stmt, }; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, Node, PatKind, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -58,7 +59,7 @@ enum IfBlockType<'hir> { /// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c), /// if_else (d) IfLet( - &'hir QPath<'hir>, + Res, Ty<'hir>, Symbol, &'hir Expr<'hir>, @@ -97,12 +98,12 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); let sugg = if let Some(else_inner) = r#else { if eq_expr_value(cx, caller, peel_blocks(else_inner)) { - format!("Some({}?)", receiver_str) + format!("Some({receiver_str}?)") } else { return; } } else { - format!("{}{}?;", receiver_str, if by_ref { ".as_ref()" } else { "" }) + format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" }) }; span_lint_and_sugg( @@ -126,7 +127,14 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if ddpos.as_opt_usize().is_none(); if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind; let caller_ty = cx.typeck_results().expr_ty(let_expr); - let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else); + let if_block = IfBlockType::IfLet( + cx.qpath_res(path1, let_pat.hir_id), + caller_ty, + ident.name, + let_expr, + if_then, + if_else + ); if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) || is_early_return(sym::Result, cx, &if_block); if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none(); @@ -135,8 +143,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_))); let sugg = format!( - "{}{}?{}", - receiver_str, + "{receiver_str}{}?{}", if by_ref == ByRef::Yes { ".as_ref()" } else { "" }, if requires_semi { ";" } else { "" } ); @@ -166,21 +173,21 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ _ => false, } }, - IfBlockType::IfLet(qpath, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => { + IfBlockType::IfLet(res, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => { is_type_diagnostic_item(cx, let_expr_ty, smbl) && match smbl { sym::Option => { // We only need to check `if let Some(x) = option` not `if let None = option`, // because the later one will be suggested as `if option.is_none()` thus causing conflict. - is_lang_ctor(cx, qpath, OptionSome) + is_res_lang_ctor(cx, res, OptionSome) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None) }, sym::Result => { - (is_lang_ctor(cx, qpath, ResultOk) + (is_res_lang_ctor(cx, res, ResultOk) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym))) - || is_lang_ctor(cx, qpath, ResultErr) + || is_res_lang_ctor(cx, res, ResultErr) && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym)) }, _ => false, @@ -199,7 +206,7 @@ fn expr_return_none_or_err( match peel_blocks_with_stmt(expr).kind { ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym), ExprKind::Path(ref qpath) => match smbl { - sym::Option => is_lang_ctor(cx, qpath, OptionNone), + sym::Option => is_res_lang_ctor(cx, cx.qpath_res(qpath, expr.hir_id), OptionNone), sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr), _ => false, }, @@ -224,7 +231,9 @@ fn expr_return_none_or_err( impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - check_is_none_or_err_and_early_return(cx, expr); - check_if_let_some_or_err_and_early_return(cx, expr); + if !in_constant(cx, expr.hir_id) { + check_is_none_or_err_and_early_return(cx, expr); + check_if_let_some_or_err_and_early_return(cx, expr); + } } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 918d624eec6f..c6fbb5e805ab 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -243,9 +243,9 @@ fn check_possible_range_contains( cx, MANUAL_RANGE_CONTAINS, span, - &format!("manual `{}::contains` implementation", range_type), + &format!("manual `{range_type}::contains` implementation"), "use", - format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), + format!("({lo}{space}{range_op}{hi}).contains(&{name})"), applicability, ); } else if !combine_and && ord == Some(l.ord) { @@ -273,9 +273,9 @@ fn check_possible_range_contains( cx, MANUAL_RANGE_CONTAINS, span, - &format!("manual `!{}::contains` implementation", range_type), + &format!("manual `!{range_type}::contains` implementation"), "use", - format!("!({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), + format!("!({lo}{space}{range_op}{hi}).contains(&{name})"), applicability, ); } @@ -372,14 +372,14 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { diag.span_suggestion( span, "use", - format!("({}..={})", start, end), + format!("({start}..={end})"), Applicability::MaybeIncorrect, ); } else { diag.span_suggestion( span, "use", - format!("{}..={}", start, end), + format!("{start}..={end}"), Applicability::MachineApplicable, // snippet ); } @@ -408,7 +408,7 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { diag.span_suggestion( expr.span, "use", - format!("{}..{}", start, end), + format!("{start}..{end}"), Applicability::MachineApplicable, // snippet ); }, @@ -486,7 +486,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "consider using the following if you are attempting to iterate over this \ range in reverse", - format!("({}{}{}).rev()", end_snippet, dots, start_snippet), + format!("({end_snippet}{dots}{start_snippet}).rev()"), Applicability::MaybeIncorrect, ); } diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index 94dec191103c..fa107858863a 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -2,9 +2,10 @@ use clippy_utils::{ diagnostics::{span_lint, span_lint_and_sugg}, higher::{get_vec_init_kind, VecInitKind}, source::snippet, - visitors::expr_visitor_no_bodies, + visitors::for_each_expr, }; -use hir::{intravisit::Visitor, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind}; +use core::ops::ControlFlow; +use hir::{Expr, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -58,10 +59,8 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { && let PatKind::Binding(_, _, ident, _) = pat.kind && let Some(vec_init_kind) = get_vec_init_kind(cx, init) { - // finds use of `_.read(&mut v)` - let mut read_found = false; - let mut visitor = expr_visitor_no_bodies(|expr| { - if let ExprKind::MethodCall(path, _self, [arg], _) = expr.kind + let visitor = |expr: &Expr<'_>| { + if let ExprKind::MethodCall(path, _, [arg], _) = expr.kind && let PathSegment { ident: read_or_read_exact, .. } = *path && matches!(read_or_read_exact.as_str(), "read" | "read_exact") && let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind @@ -69,27 +68,22 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { && let [inner_seg] = inner_path.segments && ident.name == inner_seg.ident.name { - read_found = true; - } - !read_found - }); - - let next_stmt_span; - if idx == block.stmts.len() - 1 { - // case { .. stmt; expr } - if let Some(e) = block.expr { - visitor.visit_expr(e); - next_stmt_span = e.span; + ControlFlow::Break(()) } else { - return; + ControlFlow::Continue(()) } - } else { + }; + + let (read_found, next_stmt_span) = + if let Some(next_stmt) = block.stmts.get(idx + 1) { // case { .. stmt; stmt; .. } - let next_stmt = &block.stmts[idx + 1]; - visitor.visit_stmt(next_stmt); - next_stmt_span = next_stmt.span; - } - drop(visitor); + (for_each_expr(next_stmt, visitor).is_some(), next_stmt.span) + } else if let Some(e) = block.expr { + // case { .. stmt; expr } + (for_each_expr(e, visitor).is_some(), e.span) + } else { + return + }; if read_found && !next_stmt_span.from_expansion() { let applicability = Applicability::MaybeIncorrect; @@ -101,9 +95,8 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { next_stmt_span, "reading zero byte data to `Vec`", "try", - format!("{}.resize({}, 0); {}", + format!("{}.resize({len}, 0); {}", ident.as_str(), - len, snippet(cx, next_stmt_span, "..") ), applicability, diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 3c6ca9d98975..464f6827e1d5 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { cx, REDUNDANT_PUB_CRATE, span, - &format!("pub(crate) {} inside private module", descr), + &format!("pub(crate) {descr} inside private module"), |diag| { diag.span_suggestion( item.vis_span, diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 8693ca9af830..245a02ea26e6 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -127,9 +127,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix { - format!("({}{}{})", reborrow_str, "*".repeat(deref_count), snip) + format!("({reborrow_str}{}{snip})", "*".repeat(deref_count)) } else { - format!("{}{}{}", reborrow_str, "*".repeat(deref_count), snip) + format!("{reborrow_str}{}{snip}", "*".repeat(deref_count)) }; (lint, help_str, sugg) @@ -141,9 +141,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { if deref_ty == expr_ty { let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; let sugg = if needs_parens_for_prefix { - format!("(&{}{}*{})", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip) + format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) } else { - format!("&{}{}*{}", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip) + format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) }; (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg) } else { diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 2d751c274679..60ba62c4a433 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -67,7 +67,7 @@ impl RedundantStaticLifetimes { TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => { if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime { let snip = snippet(cx, borrow_type.ty.span, ""); - let sugg = format!("&{}", snip); + let sugg = format!("&{snip}"); span_lint_and_then( cx, REDUNDANT_STATIC_LIFETIMES, diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 6bcae0da8f48..1fda58fa54de 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -172,7 +172,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { ); }, Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e)); + span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); }, } } @@ -200,7 +200,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { ); }, Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e)); + span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); }, } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 91553240e3c9..2b2a41d16011 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,9 +1,11 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; +use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::{fn_def_id, path_to_local_id}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -72,6 +74,27 @@ enum RetReplacement { Unit, } +impl RetReplacement { + fn sugg_help(self) -> &'static str { + match self { + Self::Empty => "remove `return`", + Self::Block => "replace `return` with an empty block", + Self::Unit => "replace `return` with a unit value", + } + } +} + +impl ToString for RetReplacement { + fn to_string(&self) -> String { + match *self { + Self::Empty => "", + Self::Block => "{}", + Self::Unit => "()", + } + .to_string() + } +} + declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]); impl<'tcx> LateLintPass<'tcx> for Return { @@ -139,26 +162,35 @@ impl<'tcx> LateLintPass<'tcx> for Return { } else { RetReplacement::Empty }; - check_final_expr(cx, body.value, Some(body.value.span), replacement); + check_final_expr(cx, body.value, vec![], replacement); }, FnKind::ItemFn(..) | FnKind::Method(..) => { - if let ExprKind::Block(block, _) = body.value.kind { - check_block_return(cx, block); - } + check_block_return(cx, &body.value.kind, vec![]); }, } } } -fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { - if let Some(expr) = block.expr { - check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); - } else if let Some(stmt) = block.stmts.iter().last() { - match stmt.kind { - StmtKind::Expr(expr) | StmtKind::Semi(expr) => { - check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - }, - _ => (), +// if `expr` is a block, check if there are needless returns in it +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, semi_spans: Vec) { + if let ExprKind::Block(block, _) = expr_kind { + if let Some(block_expr) = block.expr { + check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty); + } else if let Some(stmt) = block.stmts.iter().last() { + match stmt.kind { + StmtKind::Expr(expr) => { + check_final_expr(cx, expr, semi_spans, RetReplacement::Empty); + }, + StmtKind::Semi(semi_expr) => { + let mut semi_spans_and_this_one = semi_spans; + // we only want the span containing the semicolon so we can remove it later. From `entry.rs:382` + if let Some(semicolon_span) = stmt.span.trim_start(semi_expr.span) { + semi_spans_and_this_one.push(semicolon_span); + check_final_expr(cx, semi_expr, semi_spans_and_this_one, RetReplacement::Empty); + } + }, + _ => (), + } } } } @@ -166,10 +198,12 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { fn check_final_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, - span: Option, + semi_spans: Vec, /* containing all the places where we would need to remove semicolons if finding an + * needless return */ replacement: RetReplacement, ) { - match expr.kind { + let peeled_drop_expr = expr.peel_drop_temps(); + match &peeled_drop_expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { if cx.tcx.hir().attrs(expr.hir_id).is_empty() { @@ -177,24 +211,18 @@ fn check_final_expr<'tcx>( if !borrows { emit_return_lint( cx, - inner.map_or(expr.hir_id, |inner| inner.hir_id), - span.expect("`else return` is not possible"), + peeled_drop_expr.span, + semi_spans, inner.as_ref().map(|i| i.span), replacement, ); } } }, - // a whole block? check it! - ExprKind::Block(block, _) => { - check_block_return(cx, block); - }, ExprKind::If(_, then, else_clause_opt) => { - if let ExprKind::Block(ifblock, _) = then.kind { - check_block_return(cx, ifblock); - } + check_block_return(cx, &then.kind, semi_spans.clone()); if let Some(else_clause) = else_clause_opt { - check_final_expr(cx, else_clause, None, RetReplacement::Empty); + check_block_return(cx, &else_clause.kind, semi_spans); } }, // a match expr, check all arms @@ -203,123 +231,61 @@ fn check_final_expr<'tcx>( // (except for unit type functions) so we don't match it ExprKind::Match(_, arms, MatchSource::Normal) => { for arm in arms.iter() { - check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Unit); + check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit); } }, - ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty), - _ => (), + // if it's a whole block, check it + other_expr_kind => check_block_return(cx, other_expr_kind, semi_spans), } } fn emit_return_lint( cx: &LateContext<'_>, - emission_place: HirId, ret_span: Span, + semi_spans: Vec, inner_span: Option, replacement: RetReplacement, ) { if ret_span.from_expansion() { return; } - match inner_span { - Some(inner_span) => { - let mut applicability = Applicability::MachineApplicable; - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability); - diag.span_suggestion(ret_span, "remove `return`", snippet, applicability); - }, - ); + let mut applicability = Applicability::MachineApplicable; + let return_replacement = inner_span.map_or_else( + || replacement.to_string(), + |inner_span| { + let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability); + snippet.to_string() }, - None => match replacement { - RetReplacement::Empty => { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - diag.span_suggestion( - ret_span, - "remove `return`", - String::new(), - Applicability::MachineApplicable, - ); - }, - ); - }, - RetReplacement::Block => { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - diag.span_suggestion( - ret_span, - "replace `return` with an empty block", - "{}".to_string(), - Applicability::MachineApplicable, - ); - }, - ); - }, - RetReplacement::Unit => { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - diag.span_suggestion( - ret_span, - "replace `return` with a unit value", - "()".to_string(), - Applicability::MachineApplicable, - ); - }, - ); - }, - }, - } + ); + let sugg_help = if inner_span.is_some() { + "remove `return`" + } else { + replacement.sugg_help() + }; + span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { + diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability); + // for each parent statement, we need to remove the semicolon + for semi_stmt_span in semi_spans { + diag.tool_only_span_suggestion(semi_stmt_span, "remove this semicolon", "", applicability); + } + }); } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let mut visitor = BorrowVisitor { cx, borrows: false }; - walk_expr(&mut visitor, expr); - visitor.borrows -} - -struct BorrowVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - borrows: bool, -} - -impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows || expr.span.from_expansion() { - return; - } - - if let Some(def_id) = fn_def_id(self.cx, expr) { - self.borrows = self - .cx + for_each_expr(expr, |e| { + if let Some(def_id) = fn_def_id(cx, e) + && cx .tcx .fn_sig(def_id) - .output() .skip_binder() + .output() .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::from(!expr.span.from_expansion())) } - - walk_expr(self, expr); - } + }) + .is_some() } diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 20184d54b76e..4249063d2d47 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { |diag| { diag.span_note( trait_method_span, - &format!("existing `{}` defined here", method_name), + &format!("existing `{method_name}` defined here"), ); }, ); @@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { // iterate on trait_spans? diag.span_note( trait_spans[0], - &format!("existing `{}` defined here", method_name), + &format!("existing `{method_name}` defined here"), ); }, ); diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 729694da46d5..66638eed9983 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { } let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, ".."); - let suggestion = format!("{0};", sugg); + let suggestion = format!("{sugg};"); span_lint_and_sugg( cx, SEMICOLON_IF_NOTHING_RETURNED, diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index c07aa00a1278..760399231513 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq}; +use clippy_utils::{ + get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, SpanlessEq, +}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind}; @@ -174,7 +175,7 @@ impl SlowVectorInit { diag.span_suggestion( vec_alloc.allocation_expr.span, "consider replace allocation with", - format!("vec![0; {}]", len_expr), + format!("vec![0; {len_expr}]"), Applicability::Unspecified, ); }); @@ -219,8 +220,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { && path_to_local_id(self_arg, self.vec_alloc.local_id) && path.ident.name == sym!(resize) // Check that is filled with 0 - && let ExprKind::Lit(ref lit) = fill_arg.kind - && let LitKind::Int(0, _) = lit.node { + && is_integer_literal(fill_arg, 0) { // Check that len expression is equals to `with_capacity` expression if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) { self.slow_expression = Some(InitializationType::Resize(expr)); @@ -254,10 +254,8 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { if_chain! { if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind; - if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT); - if let ExprKind::Lit(ref lit) = repeat_arg.kind; - if let LitKind::Int(0, _) = lit.node; - + if is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat); + if is_integer_literal(repeat_arg, 0); then { true } else { diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index ffd63cc687a1..d6b336bef943 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -1,6 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::def_id::DefId; use rustc_hir::{def::Res, HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::DefIdTree; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, symbol::kw, Span}; @@ -94,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { if let Res::Def(_, def_id) = path.res && let Some(first_segment) = get_first_segment(path) + && is_stable(cx, def_id) { let (lint, msg, help) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { @@ -146,3 +149,22 @@ fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> _ => None, } } + +/// Checks if all ancestors of `def_id` are stable, to avoid linting +/// [unstable moves](https://github.com/rust-lang/rust/pull/95956) +fn is_stable(cx: &LateContext<'_>, mut def_id: DefId) -> bool { + loop { + if cx + .tcx + .lookup_stability(def_id) + .map_or(false, |stability| stability.is_unstable()) + { + return false; + } + + match cx.tcx.opt_parent(def_id) { + Some(parent) => def_id = parent, + None => return true, + } + } +} diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 662d399ca538..d356c99c8fc4 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -284,7 +284,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { e.span, "calling a slice of `as_bytes()` with `from_utf8` should be not necessary", "try", - format!("Some(&{}[{}])", snippet_app, snippet(cx, right.span, "..")), + format!("Some(&{snippet_app}[{}])", snippet(cx, right.span, "..")), applicability ) } @@ -500,8 +500,8 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { cx, TRIM_SPLIT_WHITESPACE, trim_span.with_hi(split_ws_span.lo()), - &format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name), - &format!("remove `{}()`", trim_fn_name), + &format!("found call to `str::{trim_fn_name}` before `str::split_whitespace`"), + &format!("remove `{trim_fn_name}()`"), String::new(), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index 78403d9fdb7e..03324c66e8ef 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { span, "using `libc::strlen` on a `CString` or `CStr` value", "try this", - format!("{}.{}().len()", val_name, method_name), + format!("{val_name}.{method_name}().len()"), app, ); } diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 5d36f0f5ff8b..eef9bdc78494 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -326,8 +326,7 @@ fn replace_left_sugg( applicability: &mut Applicability, ) -> String { format!( - "{} {} {}", - left_suggestion, + "{left_suggestion} {} {}", binop.op.to_string(), snippet_with_applicability(cx, binop.right.span, "..", applicability), ) @@ -340,10 +339,9 @@ fn replace_right_sugg( applicability: &mut Applicability, ) -> String { format!( - "{} {} {}", + "{} {} {right_suggestion}", snippet_with_applicability(cx, binop.left.span, "..", applicability), binop.op.to_string(), - right_suggestion, ) } @@ -676,9 +674,8 @@ fn suggestion_with_swapped_ident( } Some(format!( - "{}{}{}", + "{}{new_ident}{}", snippet_with_applicability(cx, expr.span.with_hi(current_ident.span.lo()), "..", applicability), - new_ident, snippet_with_applicability(cx, expr.span.with_lo(current_ident.span.hi()), "..", applicability), )) }) diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index d47ed459387e..b57b484bdc89 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -92,25 +93,17 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { } fn count_binops(expr: &hir::Expr<'_>) -> u32 { - let mut visitor = BinaryExprVisitor::default(); - visitor.visit_expr(expr); - visitor.nb_binops -} - -#[derive(Default)] -struct BinaryExprVisitor { - nb_binops: u32, -} - -impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - match expr.kind { + let mut count = 0u32; + let _: Option = for_each_expr(expr, |e| { + if matches!( + e.kind, hir::ExprKind::Binary(..) - | hir::ExprKind::Unary(hir::UnOp::Not | hir::UnOp::Neg, _) - | hir::ExprKind::AssignOp(..) => self.nb_binops += 1, - _ => {}, + | hir::ExprKind::Unary(hir::UnOp::Not | hir::UnOp::Neg, _) + | hir::ExprKind::AssignOp(..) + ) { + count += 1; } - - walk_expr(self, expr); - } + ControlFlow::Continue(()) + }); + count } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 1885f3ca414d..f46c21e12655 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -96,7 +96,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa cx, MANUAL_SWAP, span, - &format!("this looks like you are swapping elements of `{}` manually", slice), + &format!("this looks like you are swapping elements of `{slice}` manually"), "try", format!( "{}.swap({}, {})", @@ -121,16 +121,16 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa cx, MANUAL_SWAP, span, - &format!("this looks like you are swapping `{}` and `{}` manually", first, second), + &format!("this looks like you are swapping `{first}` and `{second}` manually"), |diag| { diag.span_suggestion( span, "try", - format!("{}::mem::swap({}, {})", sugg, first.mut_addr(), second.mut_addr()), + format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()), applicability, ); if !is_xor_based { - diag.note(&format!("or maybe you should use `{}::mem::replace`?", sugg)); + diag.note(&format!("or maybe you should use `{sugg}::mem::replace`?")); } }, ); @@ -182,7 +182,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { let rhs0 = Sugg::hir_opt(cx, rhs0); let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) { ( - format!(" `{}` and `{}`", first, second), + format!(" `{first}` and `{second}`"), first.mut_addr().to_string(), second.mut_addr().to_string(), ) @@ -196,22 +196,19 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { span_lint_and_then(cx, ALMOST_SWAPPED, span, - &format!("this looks like you are trying to swap{}", what), + &format!("this looks like you are trying to swap{what}"), |diag| { if !what.is_empty() { diag.span_suggestion( span, "try", format!( - "{}::mem::swap({}, {})", - sugg, - lhs, - rhs, + "{sugg}::mem::swap({lhs}, {rhs})", ), Applicability::MaybeIncorrect, ); diag.note( - &format!("or maybe you should use `{}::mem::replace`?", sugg) + &format!("or maybe you should use `{sugg}::mem::replace`?") ); } }); diff --git a/clippy_lints/src/swap_ptr_to_ref.rs b/clippy_lints/src/swap_ptr_to_ref.rs index 3cbbda80f3a9..d085dda3582b 100644 --- a/clippy_lints/src/swap_ptr_to_ref.rs +++ b/clippy_lints/src/swap_ptr_to_ref.rs @@ -58,7 +58,7 @@ impl LateLintPass<'_> for SwapPtrToRef { let mut app = Applicability::MachineApplicable; let snip1 = snippet_with_context(cx, arg1_span.unwrap_or(arg1.span), ctxt, "..", &mut app).0; let snip2 = snippet_with_context(cx, arg2_span.unwrap_or(arg2.span), ctxt, "..", &mut app).0; - diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({}, {})", snip1, snip2), app); + diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({snip1}, {snip2})"), app); } } ); diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 651201f34ed2..2512500a6be7 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -84,9 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { "use of `.to_digit(..).is_some()`", "try this", if is_method_call { - format!("{}.is_digit({})", char_arg_snip, radix_snip) + format!("{char_arg_snip}.is_digit({radix_snip})") } else { - format!("char::is_digit({}, {})", char_arg_snip, radix_snip) + format!("char::is_digit({char_arg_snip}, {radix_snip})") }, applicability, ); diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 2be22884027e..b5f11b4acae0 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -215,9 +215,8 @@ impl TraitBounds { .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability)) .join(" + "); let hint_string = format!( - "consider combining the bounds: `{}: {}`", + "consider combining the bounds: `{}: {trait_bounds}`", snippet(cx, p.bounded_ty.span, "_"), - trait_bounds, ); span_lint_and_help( cx, diff --git a/clippy_lints/src/transmute/crosspointer_transmute.rs b/clippy_lints/src/transmute/crosspointer_transmute.rs index 25d0543c8611..c4b9d82fc735 100644 --- a/clippy_lints/src/transmute/crosspointer_transmute.rs +++ b/clippy_lints/src/transmute/crosspointer_transmute.rs @@ -13,10 +13,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, CROSSPOINTER_TRANSMUTE, e.span, - &format!( - "transmute from a type (`{}`) to the type that it points to (`{}`)", - from_ty, to_ty - ), + &format!("transmute from a type (`{from_ty}`) to the type that it points to (`{to_ty}`)"), ); true }, @@ -25,10 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, CROSSPOINTER_TRANSMUTE, e.span, - &format!( - "transmute from a type (`{}`) to a pointer to that type (`{}`)", - from_ty, to_ty - ), + &format!("transmute from a type (`{from_ty}`) to a pointer to that type (`{to_ty}`)"), ); true }, diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs index 1bde977cfa27..5ecba512b0fd 100644 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_FLOAT_TO_INT, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let mut sugg = sugg::Sugg::hir(cx, arg, ".."); @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( if let ExprKind::Lit(lit) = &arg.kind; if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node; then { - let op = format!("{}{}", sugg, float_ty.name_str()).into(); + let op = format!("{sugg}{}", float_ty.name_str()).into(); match sugg { sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op), _ => sugg = sugg::Sugg::NonParen(op) diff --git a/clippy_lints/src/transmute/transmute_int_to_bool.rs b/clippy_lints/src/transmute/transmute_int_to_bool.rs index 8c50b58ca4b8..58227c53de2f 100644 --- a/clippy_lints/src/transmute/transmute_int_to_bool.rs +++ b/clippy_lints/src/transmute/transmute_int_to_bool.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_BOOL, e.span, - &format!("transmute from a `{}` to a `bool`", from_ty), + &format!("transmute from a `{from_ty}` to a `bool`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let zero = sugg::Sugg::NonParen(Cow::from("0")); diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs index 9e1823c373bf..7d31c375f8cf 100644 --- a/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_CHAR, e.span, - &format!("transmute from a `{}` to a `char`", from_ty), + &format!("transmute from a `{from_ty}` to a `char`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let arg = if let ty::Int(_) = from_ty.kind() { @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("std::char::from_u32({}).unwrap()", arg), + format!("std::char::from_u32({arg}).unwrap()"), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_int_to_float.rs b/clippy_lints/src/transmute/transmute_int_to_float.rs index b8703052e6c8..cc3422edbbf1 100644 --- a/clippy_lints/src/transmute/transmute_int_to_float.rs +++ b/clippy_lints/src/transmute/transmute_int_to_float.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_FLOAT, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let arg = if let ty::Int(int_ty) = from_ty.kind() { @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("{}::from_bits({})", to_ty, arg), + format!("{to_ty}::from_bits({arg})"), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/clippy_lints/src/transmute/transmute_num_to_bytes.rs index 52d193d11e1a..009d5a7c8ae1 100644 --- a/clippy_lints/src/transmute/transmute_num_to_bytes.rs +++ b/clippy_lints/src/transmute/transmute_num_to_bytes.rs @@ -31,13 +31,13 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_NUM_TO_BYTES, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); diag.span_suggestion( e.span, "consider using `to_ne_bytes()`", - format!("{}.to_ne_bytes()", arg), + format!("{arg}.to_ne_bytes()"), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 5eb03275b8ec..12d0b866e1c9 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -25,10 +25,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_PTR_TO_REF, e.span, - &format!( - "transmute from a pointer type (`{}`) to a reference type (`{}`)", - from_ty, to_ty - ), + &format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let (deref, cast) = if *mutbl == Mutability::Mut { @@ -41,26 +38,25 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); if meets_msrv(msrv, msrvs::POINTER_CAST) { - format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), ty_snip) + format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par()) } else if from_ptr_ty.has_erased_regions() { - sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, ty_snip))) - .to_string() + sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() } else { - sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, ty_snip))).to_string() + sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() } } else if from_ptr_ty.ty == *to_ref_ty { if from_ptr_ty.has_erased_regions() { if meets_msrv(msrv, msrvs::POINTER_CAST) { - format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), to_ref_ty) + format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par()) } else { - sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, to_ref_ty))) + sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) .to_string() } } else { sugg::make_unop(deref, arg).to_string() } } else { - sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, to_ref_ty))).to_string() + sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string() }; diag.span_suggestion(e.span, "try", sugg, app); diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 707a11d361c0..afb7f2e13269 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_BYTES_TO_STR, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), "consider using", if const_context { format!("std::str::from_utf8_unchecked{postfix}({snippet})") diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index ae55a6bf5586..1c99a02e6c71 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -75,10 +75,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), + &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty.peel_refs() { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -89,10 +89,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute to `{}` which has an undefined layout", to_ty_orig), + &format!("transmute to `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty.peel_refs() { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } }, ); @@ -116,8 +116,7 @@ pub(super) fn check<'tcx>( TRANSMUTE_UNDEFINED_REPR, e.span, &format!( - "transmute from `{}` to `{}`, both of which have an undefined layout", - from_ty_orig, to_ty_orig + "transmute from `{from_ty_orig}` to `{to_ty_orig}`, both of which have an undefined layout" ), |diag| { if let Some(same_adt_did) = same_adt_did { @@ -127,10 +126,10 @@ pub(super) fn check<'tcx>( )); } else { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } } }, @@ -145,10 +144,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), + &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -162,10 +161,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute into `{}` which has an undefined layout", to_ty_orig), + &format!("transmute into `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } }, ); diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 626d7cd46fc4..6b444922a7cc 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -21,10 +21,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, e.span, - &format!( - "transmute from `{}` to `{}` which could be expressed as a pointer cast instead", - from_ty, to_ty - ), + &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { let sugg = arg.as_ty(&to_ty.to_string()).to_string(); diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index d8e349af7af8..19ce5ae72c24 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -1,8 +1,6 @@ use clippy_utils::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_path_diagnostic_item; -use if_chain::if_chain; -use rustc_ast::LitKind; +use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -19,37 +17,28 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); - if_chain! { - if let ExprKind::Path(ref _qpath) = arg.kind; - if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg); - if x == 0; - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); - return true; - } + if let ExprKind::Path(ref _qpath) = arg.kind && + let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) && + x == 0 + { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; } // Catching: // `std::mem::transmute(0 as *const i32)` - if_chain! { - if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind; - if let ExprKind::Lit(ref lit) = inner_expr.kind; - if let LitKind::Int(0, _) = lit.node; - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); - return true; - } + if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind && is_integer_literal(inner_expr, 0) { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; } // Catching: // `std::mem::transmute(std::ptr::null::())` - if_chain! { - if let ExprKind::Call(func1, []) = arg.kind; - if is_path_diagnostic_item(cx, func1, sym::ptr_null); - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); - return true; - } + if let ExprKind::Call(func1, []) = arg.kind && + is_path_diagnostic_item(cx, func1, sym::ptr_null) + { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; } // FIXME: diff --git a/clippy_lints/src/transmute/unsound_collection_transmute.rs b/clippy_lints/src/transmute/unsound_collection_transmute.rs index 831b0d450d20..b1445311b711 100644 --- a/clippy_lints/src/transmute/unsound_collection_transmute.rs +++ b/clippy_lints/src/transmute/unsound_collection_transmute.rs @@ -37,10 +37,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, UNSOUND_COLLECTION_TRANSMUTE, e.span, - &format!( - "transmute from `{}` to `{}` with mismatched layout is unsound", - from_ty, to_ty - ), + &format!("transmute from `{from_ty}` to `{to_ty}` with mismatched layout is unsound"), ); true } else { diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index 8122cd716e01..f919bbd5afca 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( cx, USELESS_TRANSMUTE, e.span, - &format!("transmute from a type (`{}`) to itself", from_ty), + &format!("transmute from a type (`{from_ty}`) to itself"), ); true }, diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index fdf847bf4459..b567d92230bb 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,8 +1,11 @@ use rustc_hir::Expr; +use rustc_hir_analysis::check::{ + cast::{self, CastCheckResult}, + FnCtxt, Inherited, +}; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; -use rustc_hir_analysis::check::{cast::{self, CastCheckResult}, FnCtxt, Inherited}; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment diff --git a/clippy_lints/src/transmute/wrong_transmute.rs b/clippy_lints/src/transmute/wrong_transmute.rs index 2118f3d69500..d1965565b926 100644 --- a/clippy_lints/src/transmute/wrong_transmute.rs +++ b/clippy_lints/src/transmute/wrong_transmute.rs @@ -13,7 +13,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, WRONG_TRANSMUTE, e.span, - &format!("transmute from a `{}` to a pointer", from_ty), + &format!("transmute from a `{from_ty}` to a pointer"), ); true }, diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 1268c23206a6..9c6629958401 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -49,15 +49,15 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m let inner_snippet = snippet(cx, inner.span, ".."); let suggestion = match &inner.kind { TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => { - format!("&{}({})", ltopt, &inner_snippet) + format!("&{ltopt}({})", &inner_snippet) }, TyKind::Path(qpath) if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) .map_or(false, |bounds| bounds.len() > 1) => { - format!("&{}({})", ltopt, &inner_snippet) + format!("&{ltopt}({})", &inner_snippet) }, - _ => format!("&{}{}", ltopt, &inner_snippet), + _ => format!("&{ltopt}{}", &inner_snippet), }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index ba51404d2148..08020ce66381 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -16,7 +16,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ _ => "<..>", }; - let box_content = format!("{outer}{generic}", outer = item_type); + let box_content = format!("{item_type}{generic}"); span_lint_and_help( cx, BOX_COLLECTION, diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index aca55817c525..79c31efb9fcd 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -352,8 +352,10 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { match item.kind { ImplItemKind::Const(ty, _) => { - let is_in_trait_impl = if let Some(hir::Node::Item(item)) = - cx.tcx.hir().find_by_def_id(cx.tcx.hir().get_parent_item(item.hir_id()).def_id) + let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx + .tcx + .hir() + .find_by_def_id(cx.tcx.hir().get_parent_item(item.hir_id()).def_id) { matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) } else { @@ -535,7 +537,7 @@ impl Types { QPath::LangItem(..) => {}, } }, - TyKind::Rptr(ref lt, ref mut_ty) => { + TyKind::Rptr(lt, ref mut_ty) => { context.is_nested_call = true; if !borrowed_box::check(cx, hir_ty, lt, mut_ty) { self.check_ty(cx, mut_ty.ty, context); diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index 4d72a29e8c74..6b9de64e24c9 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ hir_ty.span, "usage of `Rc` when T is a buffer type", "try", - format!("Rc<{}>", alternate), + format!("Rc<{alternate}>"), Applicability::MachineApplicable, ); } else { @@ -57,7 +57,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ hir_ty.span, "usage of `Arc` when T is a buffer type", "try", - format!("Arc<{}>", alternate), + format!("Arc<{alternate}>"), Applicability::MachineApplicable, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index d81c5c83845d..ecb672005390 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -3,9 +3,9 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use rustc_hir_analysis::hir_ty_to_ty; use super::{utils, REDUNDANT_ALLOCATION}; @@ -27,13 +27,11 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{}<{}>`", outer_sym, generic_snippet), + &format!("usage of `{outer_sym}<{generic_snippet}>`"), |diag| { - diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability); + diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability); diag.note(&format!( - "`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap", - outer = outer_sym, - generic = generic_snippet + "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap" )); }, ); @@ -72,19 +70,16 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet), + &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { diag.span_suggestion( hir_ty.span, "try", - format!("{}<{}>", outer_sym, generic_snippet), + format!("{outer_sym}<{generic_snippet}>"), applicability, ); diag.note(&format!( - "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation", - outer = outer_sym, - inner = inner_sym, - generic = generic_snippet + "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); }, ); @@ -94,19 +89,13 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet), + &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { diag.note(&format!( - "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation", - outer = outer_sym, - inner = inner_sym, - generic = generic_snippet + "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); diag.help(&format!( - "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`", - outer = outer_sym, - inner = inner_sym, - generic = generic_snippet + "consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`" )); }, ); diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 236f9955722d..6c329d8cdf19 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -4,11 +4,11 @@ use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::TypeVisitable; use rustc_span::symbol::sym; -use rustc_hir_analysis::hir_ty_to_ty; use super::VEC_BOX; diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 3f99bd3f3156..1ab0162a8813 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; -use clippy_utils::{is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq}; +use clippy_utils::{is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -211,9 +211,12 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt } }); match expr.kind { - ExprKind::MethodCall(path, self_expr, [_], _) => { + ExprKind::MethodCall(path, self_expr, [arg], _) => { let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs(); - if is_type_diagnostic_item(cx, self_type, sym::Vec) && path.ident.name.as_str() == "set_len" { + if is_type_diagnostic_item(cx, self_type, sym::Vec) + && path.ident.name.as_str() == "set_len" + && !is_integer_literal(arg, 0) + { Some((self_expr, expr.span)) } else { None diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index c0a4f3fbacd6..57aff5367dd1 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -157,8 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { span, &format!( "this closure returns \ - the unit type which also implements {}", - trait_name + the unit type which also implements {trait_name}" ), ); }, @@ -169,8 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { span, &format!( "this closure returns \ - the unit type which also implements {}", - trait_name + the unit type which also implements {trait_name}" ), Some(last_semi), "probably caused by this trailing semicolon", diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index a6f777abc6e9..f6d3fb00f4ee 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -74,7 +74,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp cx, UNIT_ARG, expr.span, - &format!("passing {}unit value{} to a function", singular, plural), + &format!("passing {singular}unit value{plural} to a function"), |db| { let mut or = ""; args_to_recover @@ -129,7 +129,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( - &format!("use {}unit literal{} instead", singular, plural), + &format!("use {singular}unit literal{plural} instead"), args_to_recover .iter() .map(|arg| (arg.span, "()".to_string())) @@ -143,8 +143,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp db.span_suggestion( expr.span, &format!( - "{}move the expression{} in front of the call and replace {} with the unit literal `()`", - or, empty_or_s, it_or_them + "{or}move the expression{empty_or_s} in front of the call and replace {it_or_them} with the unit literal `()`" ), sugg, applicability, diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index 1dd8895ebd07..226495dcbda3 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -22,7 +22,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, UNIT_CMP, macro_call.span, - &format!("`{}` of unit values detected. This will always {}", macro_name, result), + &format!("`{macro_name}` of unit values detected. This will always {result}"), ); } return; @@ -40,9 +40,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { UNIT_CMP, expr.span, &format!( - "{}-comparison of unit values detected. This will always be {}", - op.as_str(), - result + "{}-comparison of unit values detected. This will always be {result}", + op.as_str() ), ); } diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 8a4f4c0ad971..016aacbf9da3 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -3,7 +3,7 @@ use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { ); } else { if_chain! { - if match_def_path(cx, fun_def_id, &paths::FROM_FROM); + if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(fun_def_id); if let [.., last_arg] = args; if let ExprKind::Lit(spanned) = &last_arg.kind; if let LitKind::Str(symbol, _) = spanned.node; diff --git a/clippy_lints/src/unnecessary_self_imports.rs b/clippy_lints/src/unnecessary_self_imports.rs index 839a4bdab09d..bc0dd263d88a 100644 --- a/clippy_lints/src/unnecessary_self_imports.rs +++ b/clippy_lints/src/unnecessary_self_imports.rs @@ -57,7 +57,7 @@ impl EarlyLintPass for UnnecessarySelfImports { format!( "{}{};", last_segment.ident, - if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {}", alias) } else { String::new() }, + if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {alias}") } else { String::new() }, ), Applicability::MaybeIncorrect, ); diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 2c40827db0e7..7211e6864f3a 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::{contains_return, is_lang_ctor, return_ty, visitors::find_all_ret_expressions}; +use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -120,9 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if !ret_expr.span.from_expansion(); // Check if a function call. if let ExprKind::Call(func, [arg]) = ret_expr.kind; - // Check if OPTION_SOME or RESULT_OK, depending on return type. - if let ExprKind::Path(qpath) = &func.kind; - if is_lang_ctor(cx, qpath, lang_item); + if is_res_lang_ctor(cx, path_res(cx, func), lang_item); // Make sure the function argument does not contain a return expression. if !contains_return(arg); then { @@ -153,11 +151,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ) } else { ( - format!( - "this function's return value is unnecessarily wrapped by `{}`", - return_type_label - ), - format!("remove `{}` from the return type...", return_type_label), + format!("this function's return value is unnecessarily wrapped by `{return_type_label}`"), + format!("remove `{return_type_label}` from the return type..."), inner_type.to_string(), "...and then change returning expressions", ) diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 64f7a055cd9b..32cd46812014 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -65,10 +65,7 @@ fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext<'_>, cx, UNSAFE_REMOVED_FROM_NAME, span, - &format!( - "removed `unsafe` from the name of `{}` in use as `{}`", - old_str, new_str - ), + &format!("removed `unsafe` from the name of `{old_str}` in use as `{new_str}`"), ); } } diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index b38d71784fcf..8bcdff66331d 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::{is_try, match_trait_method, paths}; +use clippy_utils::{is_trait_method, is_try, match_trait_method, paths}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -116,13 +117,13 @@ fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Exp match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT) } else { - match_trait_method(cx, call, &paths::IO_READ) + is_trait_method(cx, call, sym::IoRead) }; let write_trait = if is_await { match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT) || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) } else { - match_trait_method(cx, call, &paths::IO_WRITE) + is_trait_method(cx, call, sym::IoWrite) }; match (read_trait, write_trait, symbol, is_await) { diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index b8a5d4ea8c9f..3164937293b6 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -58,8 +58,8 @@ impl EarlyLintPass for UnusedRounding { cx, UNUSED_ROUNDING, expr.span, - &format!("used the `{}` method with a whole number float", method_name), - &format!("remove the `{}` method call", method_name), + &format!("used the `{method_name}` method with a whole number float"), + &format!("remove the `{method_name}` method call"), float, Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 3ef265580797..ea878043c04e 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -257,9 +257,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { expr.hir_id, expr.span, &format!( - "called `{}` on `{}` after checking its variant with `{}`", + "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", method_name.ident.name, - unwrappable_variable_name, unwrappable.check_name.ident.as_str(), ), |diag| { @@ -268,9 +267,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), "try", format!( - "if let {} = {}", - suggested_pattern, - unwrappable_variable_name, + "if let {suggested_pattern} = {unwrappable_variable_name}", ), // We don't track how the unwrapped value is used inside the // block or suggest deleting the unwrap, so we can't offer a diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index baa53ba664f6..a69719b127b2 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{method_chain_args, return_ty}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Expr, ImplItemKind}; +use rustc_hir::ImplItemKind; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; @@ -73,51 +73,37 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { } } -struct FindExpectUnwrap<'a, 'tcx> { - lcx: &'a LateContext<'tcx>, - typeck_results: &'tcx ty::TypeckResults<'tcx>, - result: Vec, -} - -impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - // check for `expect` - if let Some(arglists) = method_chain_args(expr, &["expect"]) { - let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result) - { - self.result.push(expr.span); - } - } - - // check for `unwrap` - if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { - let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result) - { - self.result.push(expr.span); - } - } - - // and check sub-expressions - intravisit::walk_expr(self, expr); - } -} - fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { if let ImplItemKind::Fn(_, body_id) = impl_item.kind { let body = cx.tcx.hir().body(body_id); - let mut fpu = FindExpectUnwrap { - lcx: cx, - typeck_results: cx.tcx.typeck(impl_item.def_id.def_id), - result: Vec::new(), - }; - fpu.visit_expr(body.value); + let typeck = cx.tcx.typeck(impl_item.def_id.def_id); + let mut result = Vec::new(); + let _: Option = for_each_expr(body.value, |e| { + // check for `expect` + if let Some(arglists) = method_chain_args(e, &["expect"]) { + let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); + if is_type_diagnostic_item(cx, receiver_ty, sym::Option) + || is_type_diagnostic_item(cx, receiver_ty, sym::Result) + { + result.push(e.span); + } + } + + // check for `unwrap` + if let Some(arglists) = method_chain_args(e, &["unwrap"]) { + let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); + if is_type_diagnostic_item(cx, receiver_ty, sym::Option) + || is_type_diagnostic_item(cx, receiver_ty, sym::Result) + { + result.push(e.span); + } + } + + ControlFlow::Continue(()) + }); // if we've found one, lint - if !fpu.result.is_empty() { + if !result.is_empty() { span_lint_and_then( cx, UNWRAP_IN_RESULT, @@ -125,7 +111,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc "used unwrap or expect in a function that returns result or option", move |diag| { diag.help("unwrap and expect should not be used in a function that returns result or option"); - diag.span_note(fpu.result, "potential non-recoverable error(s)"); + diag.span_note(result, "potential non-recoverable error(s)"); }, ); } diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index 2c71f35d490c..654ea306793b 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -93,7 +93,7 @@ fn check_ident(cx: &LateContext<'_>, ident: &Ident, be_aggressive: bool) { cx, UPPER_CASE_ACRONYMS, span, - &format!("name `{}` contains a capitalized acronym", ident), + &format!("name `{ident}` contains a capitalized acronym"), "consider making the acronym lowercase, except the initial letter", corrected, Applicability::MaybeIncorrect, @@ -114,6 +114,7 @@ impl LateLintPass<'_> for UpperCaseAcronyms { check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive); }, ItemKind::Enum(ref enumdef, _) => { + check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive); // check enum variants separately because again we only want to lint on private enums and // the fn check_variant does not know about the vis of the enum of its variants enumdef diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 2c4f5075e980..65f1b5462081 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -12,11 +12,11 @@ use rustc_hir::{ Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, TyKind, }; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index f1b6463ad0f7..a82643a59f97 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -118,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), None, "consider removing `.try_into()`", ); @@ -146,7 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), None, &hint, ); @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } if_chain! { - if match_def_path(cx, def_id, &paths::FROM_FROM); + if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(def_id); if same_type_and_consts(a, b); then { @@ -165,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), &sugg_msg, sugg.to_string(), Applicability::MachineApplicable, // snippet diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 1df3135c962d..e069de8cb5c7 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -739,7 +739,7 @@ fn path_to_string(path: &QPath<'_>) -> String { *s += ", "; write!(s, "{:?}", segment.ident.as_str()).unwrap(); }, - other => write!(s, "/* unimplemented: {:?}*/", other).unwrap(), + other => write!(s, "/* unimplemented: {other:?}*/").unwrap(), }, QPath::LangItem(..) => panic!("path_to_string: called for lang item qpath"), } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 2be3fa99c811..668123e4d6e3 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -39,28 +39,28 @@ pub struct Rename { pub rename: String, } -/// A single disallowed method, used by the `DISALLOWED_METHODS` lint. #[derive(Clone, Debug, Deserialize)] #[serde(untagged)] -pub enum DisallowedMethod { +pub enum DisallowedPath { Simple(String), WithReason { path: String, reason: Option }, } -impl DisallowedMethod { +impl DisallowedPath { pub fn path(&self) -> &str { let (Self::Simple(path) | Self::WithReason { path, .. }) = self; path } -} -/// A single disallowed type, used by the `DISALLOWED_TYPES` lint. -#[derive(Clone, Debug, Deserialize)] -#[serde(untagged)] -pub enum DisallowedType { - Simple(String), - WithReason { path: String, reason: Option }, + pub fn reason(&self) -> Option<&str> { + match self { + Self::WithReason { + reason: Some(reason), .. + } => Some(reason), + _ => None, + } + } } /// Conf with parse errors @@ -213,7 +213,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, ERR_EXPECT, 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, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP. /// /// The minimum rust version that the project supports (msrv: Option = None), @@ -315,14 +315,18 @@ define_Conf! { /// /// Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports: bool = false), + /// Lint: DISALLOWED_MACROS. + /// + /// The list of disallowed macros, written as fully qualified paths. + (disallowed_macros: Vec = Vec::new()), /// Lint: DISALLOWED_METHODS. /// /// The list of disallowed methods, written as fully qualified paths. - (disallowed_methods: Vec = Vec::new()), + (disallowed_methods: Vec = Vec::new()), /// Lint: DISALLOWED_TYPES. /// /// The list of disallowed types, written as fully qualified paths. - (disallowed_types: Vec = Vec::new()), + (disallowed_types: Vec = Vec::new()), /// Lint: UNREADABLE_LITERAL. /// /// Should the fraction of a decimal be linted to include separators. @@ -362,7 +366,7 @@ define_Conf! { /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), /// Lint: AWAIT_HOLDING_INVALID_TYPE - (await_holding_invalid_types: Vec = Vec::new()), + (await_holding_invalid_types: Vec = Vec::new()), /// Lint: LARGE_INCLUDE_FILE. /// /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes @@ -482,16 +486,13 @@ pub fn format_error(error: Box) -> String { let field = fields.get(index).copied().unwrap_or_default(); write!( msg, - "{:separator_width$}{:field_width$}", - " ", - field, - separator_width = SEPARATOR_WIDTH, - field_width = column_width + "{:SEPARATOR_WIDTH$}{field:column_width$}", + " " ) .unwrap(); } } - write!(msg, "\n{}", suffix).unwrap(); + write!(msg, "\n{suffix}").unwrap(); msg } else { s diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 78c036186f50..85bcbc7ad236 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -2,11 +2,11 @@ use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::match_type; use clippy_utils::{ - def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, - method_calls, paths, peel_blocks_with_stmt, SpanlessEq, + def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_any_def_paths, + match_def_path, method_calls, paths, peel_blocks_with_stmt, peel_hir_expr_refs, SpanlessEq, }; use if_chain::if_chain; use rustc_ast as ast; @@ -15,26 +15,29 @@ use rustc_ast::visit::FnKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::Visitor; use rustc_hir::{ - BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, + BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, TyKind, UnOp, }; +use rustc_hir_analysis::hir_ty_to_ty; 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, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy}; +use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; +use rustc_middle::ty::{ + self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, DefIdTree, FloatTy, Ty, +}; use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{sym, BytePos, Span}; -use rustc_hir_analysis::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; +use std::str; #[cfg(feature = "internal")] pub mod metadata_collector; @@ -226,11 +229,11 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for calls to `utils::match_type()` on a type diagnostic item - /// and suggests to use `utils::is_type_diagnostic_item()` instead. + /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. /// /// ### Why is this bad? - /// `utils::is_type_diagnostic_item()` does not require hardcoded paths. + /// The path for an item is subject to change and is less efficient to look up than a + /// diagnostic item or a `LangItem`. /// /// ### Example /// ```rust,ignore @@ -241,9 +244,9 @@ declare_clippy_lint! { /// ```rust,ignore /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) /// ``` - pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + pub UNNECESSARY_DEF_PATH, internal, - "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" + "using a def path when a diagnostic item or a `LangItem` is available" } declare_clippy_lint! { @@ -530,14 +533,14 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, LINT_WITHOUT_LINT_PASS, lint_span, - &format!("the lint `{}` is not added to any `LintPass`", lint_name), + &format!("the lint `{lint_name}` is not added to any `LintPass`"), ); } } } } -fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool { +fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Rptr( _, MutTy { @@ -666,7 +669,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { path.ident.span, "usage of a compiler lint function", None, - &format!("please use the Clippy variant of this function: `{}`", sugg), + &format!("please use the Clippy variant of this function: `{sugg}`"), ); } } @@ -854,13 +857,8 @@ fn suggest_help( "this call is collapsible", "collapse into", format!( - "span_lint_and_help({}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - &option_span, - help + "span_lint_and_help({}, {}, {}, {}, {}, {help})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, ), Applicability::MachineApplicable, ); @@ -886,107 +884,238 @@ fn suggest_note( "this call is collapsible", "collapse into", format!( - "span_lint_and_note({}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - note_span, - note + "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, ), Applicability::MachineApplicable, ); } -declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]); +declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); -impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { +#[allow(clippy::too_many_lines)] +impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) { + enum Item { + LangItem(Symbol), + DiagnosticItem(Symbol), + } + static PATHS: &[&[&str]] = &[ + &["clippy_utils", "match_def_path"], + &["clippy_utils", "match_trait_method"], + &["clippy_utils", "ty", "match_type"], + &["clippy_utils", "is_expr_path_def_path"], + ]; + + if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { return; } if_chain! { - // Check if this is a call to utils::match_type() - if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; - if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]); + if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind; + if let ExprKind::Path(path) = &func.kind; + if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if let Some(which_path) = match_any_def_paths(cx, id, PATHS); + let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; // Extract the path to the matched type - if let Some(segments) = path_to_matched_type(cx, ty_path); - let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect(); - if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id(); - // Check if the matched type is a diagnostic item - if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did); + if let Some(segments) = path_to_matched_type(cx, item_arg); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(def_id) = def_path_res(cx, &segments[..], None).opt_def_id(); then { - // TODO: check paths constants from external crates. - let cx_snippet = snippet(cx, context.span, "_"); - let ty_snippet = snippet(cx, ty.span, "_"); + // def_path_res will match field names before anything else, but for this we want to match + // inherent functions first. + let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field { + let method_name = *segments.last().unwrap(); + cx.tcx.def_key(def_id).parent + .and_then(|parent_idx| + cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter() + .find_map(|impl_id| cx.tcx.associated_items(*impl_id) + .find_by_name_and_kind( + cx.tcx, + Ident::from_str(method_name), + AssocKind::Fn, + *impl_id, + ) + ) + ) + .map_or(def_id, |item| item.def_id) + } else { + def_id + }; - span_lint_and_sugg( + // Check if the target item is a diagnostic item or LangItem. + let (msg, item) = if let Some(item_name) + = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) + { + ( + "use of a def path to a diagnostic item", + Item::DiagnosticItem(*item_name), + ) + } else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { + let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); + let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name; + ( + "use of a def path to a `LangItem`", + Item::LangItem(item_name), + ) + } else { + return; + }; + + let has_ctor = match cx.tcx.def_kind(def_id) { + DefKind::Struct => { + let variant = cx.tcx.adt_def(def_id).non_enum_variant(); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + } + DefKind::Variant => { + let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + } + _ => false, + }; + + let mut app = Applicability::MachineApplicable; + let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); + let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); + let (sugg, with_note) = match (which_path, item) { + // match_def_path + (0, Item::DiagnosticItem(item)) => + (format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor), + (0, Item::LangItem(item)) => ( + format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), + has_ctor + ), + // match_trait_method + (1, Item::DiagnosticItem(item)) => + (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false), + // match_type + (2, Item::DiagnosticItem(item)) => + (format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), + (2, Item::LangItem(item)) => + (format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false), + // is_expr_path_def_path + (3, Item::DiagnosticItem(item)) if has_ctor => ( + format!( + "is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})", + ), + false, + ), + (3, Item::LangItem(item)) if has_ctor => ( + format!( + "is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})", + ), + false, + ), + (3, Item::DiagnosticItem(item)) => + (format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), + (3, Item::LangItem(item)) => ( + format!( + "path_res({cx_snip}, {def_snip}).opt_def_id()\ + .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", + ), + false, + ), + _ => return, + }; + + span_lint_and_then( cx, - MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + UNNECESSARY_DEF_PATH, expr.span, - "usage of `clippy_utils::ty::match_type()` on a type diagnostic item", - "try", - format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), - Applicability::MaybeIncorrect, + msg, + |diag| { + diag.span_suggestion(expr.span, "try", sugg, app); + if with_note { + diag.help( + "if this `DefId` came from a constructor expression or pattern then the \ + parent `DefId` should be used instead" + ); + } + }, ); } } } } -fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { - use rustc_hir::ItemKind; - - match &expr.kind { - ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr), - ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) { +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + match peel_hir_expr_refs(expr).0.kind { + ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { Res::Local(hir_id) => { let parent_id = cx.tcx.hir().get_parent_node(hir_id); - if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) { - if let Some(init) = local.init { - return path_to_matched_type(cx, init); - } - } - }, - Res::Def(DefKind::Const | DefKind::Static(..), def_id) => { - if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) { - if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind { - let body = cx.tcx.hir().body(body_id); - return path_to_matched_type(cx, body.value); - } - } - }, - _ => {}, - }, - ExprKind::Array(exprs) => { - let segments: Vec = exprs - .iter() - .filter_map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some(sym); - } - } - + if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { + path_to_matched_type(cx, init) + } else { None - }) - .collect(); - - if segments.len() == exprs.len() { - return Some(segments); - } + } + }, + Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( + cx, + cx.tcx.eval_static_initializer(def_id).ok()?.inner(), + cx.tcx.type_of(def_id), + ), + Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { + ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { + read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) + }, + _ => None, + }, + _ => None, }, - _ => {}, - } + ExprKind::Array(exprs) => exprs + .iter() + .map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some((*sym.as_str()).to_owned()); + } + } - None + None + }) + .collect(), + _ => None, + } +} + +fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { + let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { + let &alloc = alloc.provenance().values().next()?; + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + (alloc.inner(), ty) + } else { + return None; + } + } else { + (alloc, ty) + }; + + if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() + && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() + && ty.is_str() + { + alloc + .provenance() + .values() + .map(|&alloc| { + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + let alloc = alloc.inner(); + str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) + .ok().map(ToOwned::to_owned) + } else { + None + } + }) + .collect() + } else { + None + } } // This is not a complete resolver for paths. It works on all the paths currently used in the paths // module. That's all it does and all it needs to do. pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if def_path_res(cx, path) != Res::Err { + if def_path_res(cx, path, None) != Res::Err { return true; } @@ -1077,7 +1206,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { } for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = def_path_res(cx, module).opt_def_id() { + if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { for item in cx.tcx.module_children(def_id).iter() { if_chain! { if let Res::Def(DefKind::Const, item_def_id) = item.res; diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 342f627e3827..c84191bb0103 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -64,46 +64,6 @@ const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[ /// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed /// to only keep the actual lint group in the output. const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::"; - -/// This template will be used to format the configuration section in the lint documentation. -/// The `configurations` parameter will be replaced with one or multiple formatted -/// `ClippyConfiguration` instances. See `CONFIGURATION_VALUE_TEMPLATE` for further customizations -macro_rules! CONFIGURATION_SECTION_TEMPLATE { - () => { - r#" -### Configuration -This lint has the following configuration variables: - -{configurations} -"# - }; -} -/// This template will be used to format an individual `ClippyConfiguration` instance in the -/// lint documentation. -/// -/// The format function will provide strings for the following parameters: `name`, `ty`, `doc` and -/// `default` -macro_rules! CONFIGURATION_VALUE_TEMPLATE { - () => { - "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\n" - }; -} - -macro_rules! RENAMES_SECTION_TEMPLATE { - () => { - r#" -### Past names - -{names} -"# - }; -} -macro_rules! RENAME_VALUE_TEMPLATE { - () => { - "* `{name}`\n" - }; -} - const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ &["clippy_utils", "diagnostics", "span_lint"], &["clippy_utils", "diagnostics", "span_lint_and_help"], @@ -205,7 +165,16 @@ impl MetadataCollector { .filter(|config| config.lints.iter().any(|lint| lint == lint_name)) .map(ToString::to_string) .reduce(|acc, x| acc + &x) - .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations)) + .map(|configurations| { + format!( + r#" +### Configuration +This lint has the following configuration variables: + +{configurations} +"# + ) + }) } } @@ -291,16 +260,13 @@ fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Pa continue; } - panic!("lint `{}` has an unterminated code block", lint_name) + panic!("lint `{lint_name}` has an unterminated code block") } break; }, Some(line) if line.trim_start() == "{{produces}}" => { - panic!( - "lint `{}` has marker {{{{produces}}}} with an ignored or missing code block", - lint_name - ) + panic!("lint `{lint_name}` has marker {{{{produces}}}} with an ignored or missing code block") }, Some(line) => { let line = line.trim(); @@ -319,7 +285,7 @@ fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Pa match lines.next() { Some(line) if line.trim_start() == "```" => break, Some(line) => example.push(line), - None => panic!("lint `{}` has an unterminated code block", lint_name), + None => panic!("lint `{lint_name}` has an unterminated code block"), } } @@ -336,10 +302,9 @@ fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Pa Produces\n\ \n\ ```text\n\ - {}\n\ + {output}\n\ ```\n\ - ", - output + " ), ); @@ -394,7 +359,7 @@ fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy()); } - let prefixed_name = format!("{}{lint_name}", CLIPPY_LINT_GROUP_PREFIX); + let prefixed_name = format!("{CLIPPY_LINT_GROUP_PREFIX}{lint_name}"); let mut cmd = Command::new("cargo"); @@ -417,7 +382,7 @@ fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root let output = cmd .arg(file.as_path()) .output() - .unwrap_or_else(|e| panic!("failed to run `{:?}`: {e}", cmd)); + .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}")); let tmp_file_path = file.to_string_lossy(); let stderr = std::str::from_utf8(&output.stderr).unwrap(); @@ -441,8 +406,7 @@ fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect(); panic!( - "did not find lint `{}` in output of example, got:\n{}\n{}", - lint_name, + "did not find lint `{lint_name}` in output of example, got:\n{}\n{}", non_json.join("\n"), rendered.join("\n") ); @@ -588,13 +552,10 @@ fn to_kebab(config_name: &str) -> String { impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { - write!( + writeln!( f, - CONFIGURATION_VALUE_TEMPLATE!(), - name = self.name, - ty = self.config_type, - doc = self.doc, - default = self.default + "* `{}`: `{}`: {} (defaults to `{}`)", + self.name, self.config_type, self.doc, self.default ) } } @@ -811,7 +772,7 @@ fn get_lint_group_and_level_or_lint( lint_collection_error_item( cx, item, - &format!("Unable to determine lint level for found group `{}`", group), + &format!("Unable to determine lint level for found group `{group}`"), ); None } @@ -869,7 +830,7 @@ fn collect_renames(lints: &mut Vec) { if name == lint_name; if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); then { - write!(collected, RENAME_VALUE_TEMPLATE!(), name = past_name).unwrap(); + writeln!(collected, "* `{past_name}`").unwrap(); names.push(past_name.to_string()); } } @@ -882,7 +843,15 @@ fn collect_renames(lints: &mut Vec) { } if !collected.is_empty() { - write!(&mut lint.docs, RENAMES_SECTION_TEMPLATE!(), names = collected).unwrap(); + write!( + &mut lint.docs, + r#" +### Past names + +{collected} +"# + ) + .unwrap(); } } } @@ -895,7 +864,7 @@ fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &s cx, INTERNAL_METADATA_COLLECTOR, item.ident.span, - &format!("metadata collection error for `{}`: {}", item.ident.name, message), + &format!("metadata collection error for `{}`: {message}", item.ident.name), ); } diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 2604b1ee7c56..301eed9a1fbf 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -173,7 +173,7 @@ impl LateLintPass<'_> for WildcardImports { let sugg = if braced_glob { imports_string } else { - format!("{}::{}", import_source_snippet, imports_string) + format!("{import_source_snippet}::{imports_string}") }; let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res { diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 06e7d7017017..36574198f917 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{sym, BytePos}; declare_clippy_lint! { /// ### What it does @@ -475,11 +475,11 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: & value.span, "literal with an empty format string", |diag| { - if let Some(replacement) = replacement { + if let Some(replacement) = replacement // `format!("{}", "a")`, `format!("{named}", named = "b") // ~~~~~ ~~~~~~~~~~~~~ - let value_span = expand_past_previous_comma(cx, value.span); - + && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id) + { let replacement = replacement.replace('{', "{{").replace('}', "}}"); diag.multipart_suggestion( "try this", @@ -542,10 +542,3 @@ fn conservative_unescape(literal: &str) -> Result { if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) } } - -// Expand from `writeln!(o, "")` to `writeln!(o, "")` -// ^^ ^^^^ -fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { - let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); - extended.with_lo(extended.lo() - BytePos(1)) -} diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 50d3c079fe67..9b3de35dbd3c 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -57,8 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { "constant division of `0.0` with `0.0` will always result in NaN", None, &format!( - "consider using `{}::NAN` if you would like a constant representing NaN", - float_type, + "consider using `{float_type}::NAN` if you would like a constant representing NaN", ), ); } diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index 703ba2ef4b05..6cf2a955fd5c 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_hir::{self as hir, HirId, ItemKind, Node}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::{Adt, Ty, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does @@ -69,10 +69,7 @@ impl LateLintPass<'_> for ZeroSizedMapValues { fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool { let parent_id = cx.tcx.hir().get_parent_item(hir_id); - let second_parent_id = cx - .tcx - .hir() - .get_parent_item(parent_id.into()).def_id; + let second_parent_id = cx.tcx.hir().get_parent_item(parent_id.into()).def_id; if let Some(Node::Item(item)) = cx.tcx.hir().find_by_def_id(second_parent_id) { if let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind { return true; diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index c36bca06507d..83fee7bb39c2 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.65" +version = "0.1.66" edition = "2021" publish = false diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 8ab77c881663..d9b22664fd25 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -131,12 +131,12 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s match attr.style { ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()), ast::AttrStyle::Inner => { - sess.struct_span_err(attr.span, &format!("`{}` is defined multiple times", name)) + sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times")) .span_note(unique_attr.as_ref().unwrap().span, "first definition found here") .emit(); }, ast::AttrStyle::Outer => { - sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name)); + sess.span_err(attr.span, &format!("`{name}` cannot be an outer attribute")); }, } } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 78960d1ab1da..78f93755b72d 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -18,12 +18,11 @@ fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { diag.help(&format!( - "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}", + "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { // extract just major + minor version and ignore patch versions format!("rust-{}", n.rsplit_once('.').unwrap().1) - }), - lint + }) )); } } diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 91c9c382c236..8724a4cd651d 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -113,7 +113,17 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, args, ) => match self.cx.qpath_res(path, hir_id) { - Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => (), + Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => { + if self + .cx + .typeck_results() + .expr_ty(e) + .has_significant_drop(self.cx.tcx, self.cx.param_env) + { + self.eagerness = Lazy; + return; + } + }, Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (), // No need to walk the arguments here, `is_const_evaluatable` already did Res::Def(..) if is_const_evaluatable(self.cx, e) => { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 7212d9cd7445..cf24ec8b67b9 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -962,7 +962,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { mut_ty.mutbl.hash(&mut self.s); }, TyKind::Rptr(lifetime, ref mut_ty) => { - self.hash_lifetime(*lifetime); + self.hash_lifetime(lifetime); self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); }, @@ -992,7 +992,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { in_trait.hash(&mut self.s); }, TyKind::TraitObject(_, lifetime, _) => { - self.hash_lifetime(*lifetime); + self.hash_lifetime(lifetime); }, TyKind::Typeof(anon_const) => { self.hash_body(anon_const.body); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8f79c07c9772..42374fdd7baf 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -3,6 +3,7 @@ #![feature(control_flow_enum)] #![feature(let_chains)] #![feature(lint_reasons)] +#![feature(never_type)] #![feature(once_cell)] #![feature(rustc_private)] #![recursion_limit = "512"] @@ -23,6 +24,7 @@ extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; +extern crate rustc_hir_analysis; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; @@ -32,7 +34,6 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; extern crate rustc_trait_selection; -extern crate rustc_hir_analysis; #[macro_use] pub mod sym_helper; @@ -65,6 +66,7 @@ pub use self::hir_utils::{ both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash, }; +use core::ops::ControlFlow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use std::sync::OnceLock; @@ -76,7 +78,7 @@ use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; @@ -113,14 +115,14 @@ use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param}; -use crate::visitors::expr_visitor_no_bodies; +use crate::visitors::for_each_expr; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { return Some(version); } else if let Some(sess) = sess { if let Some(span) = span { - sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv)); + sess.span_err(span, &format!("`{msrv}` is not a valid Rust version")); } } None @@ -238,19 +240,69 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { } } -/// Checks if a `QPath` resolves to a constructor of a `LangItem`. +/// Checks if a `Res` refers to a constructor of a `LangItem` /// For example, use this to check whether a function call or a pattern is `Some(..)`. -pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool { +pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool { + if let Res::Def(DefKind::Ctor(..), id) = res + && let Ok(lang_id) = cx.tcx.lang_items().require(lang_item) + && let Some(id) = cx.tcx.opt_parent(id) + { + id == lang_id + } else { + false + } +} + +pub fn is_res_diagnostic_ctor(cx: &LateContext<'_>, res: Res, diag_item: Symbol) -> bool { + if let Res::Def(DefKind::Ctor(..), id) = res + && let Some(id) = cx.tcx.opt_parent(id) + { + cx.tcx.is_diagnostic_item(diag_item, id) + } else { + false + } +} + +/// Checks if a `QPath` resolves to a constructor of a diagnostic item. +pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool { if let QPath::Resolved(_, path) = qpath { if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res { - if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) { - return cx.tcx.parent(ctor_id) == item_id; - } + return cx.tcx.is_diagnostic_item(diagnostic_item, cx.tcx.parent(ctor_id)); } } false } +/// Checks if the `DefId` matches the given diagnostic item or it's constructor. +pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool { + let did = match cx.tcx.def_kind(did) { + DefKind::Ctor(..) => cx.tcx.parent(did), + // Constructors for types in external crates seem to have `DefKind::Variant` + DefKind::Variant => match cx.tcx.opt_parent(did) { + Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did, + _ => did, + }, + _ => did, + }; + + cx.tcx.is_diagnostic_item(item, did) +} + +/// Checks if the `DefId` matches the given `LangItem` or it's constructor. +pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool { + let did = match cx.tcx.def_kind(did) { + DefKind::Ctor(..) => cx.tcx.parent(did), + // Constructors for types in external crates seem to have `DefKind::Variant` + DefKind::Variant => match cx.tcx.opt_parent(did) { + Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did, + _ => did, + }, + _ => did, + }; + + cx.tcx.lang_items().require(item).map_or(false, |id| id == did) +} + pub fn is_unit_expr(expr: &Expr<'_>) -> bool { matches!( expr.kind, @@ -470,15 +522,49 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -/// Resolves a def path like `std::vec::Vec`. +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(), + } +} + +/// Resolves a def path like `std::vec::Vec`. `namespace_hint` can be supplied to disambiguate +/// between `std::vec` the module and `std::vec` the macro +/// /// This function is expensive and should be used sparingly. -pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { - fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option { +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option) -> Res { + fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str, matches_ns: impl Fn(Res) -> bool) -> Option { match tcx.def_kind(def_id) { DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx .module_children(def_id) .iter() - .find(|item| item.ident.name.as_str() == name) + .find(|item| item.ident.name.as_str() == name && matches_ns(item.res.expect_non_local())) .map(|child| child.res.expect_non_local()), DefKind::Impl => tcx .associated_item_def_ids(def_id) @@ -486,40 +572,17 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { .copied() .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name) .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)), + DefKind::Struct | DefKind::Union => tcx + .adt_def(def_id) + .non_enum_variant() + .fields + .iter() + .find(|f| f.name.as_str() == name) + .map(|f| Res::Def(DefKind::Field, f.did)), _ => 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(()) .iter() @@ -528,32 +591,45 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { .map(CrateNum::as_def_id) } - let (base, first, path) = match *path { - [base, first, ref path @ ..] => (base, first, path), + let (base, path) = match *path { [primitive] => { return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy); }, + [base, ref path @ ..] => (base, path), _ => return Res::Err, }; let tcx = cx.tcx; let starts = find_primitive(tcx, base) .chain(find_crate(tcx, base)) - .filter_map(|id| item_child_by_name(tcx, id, first)); + .map(|id| Res::Def(tcx.def_kind(id), id)); for first in starts { let last = path .iter() .copied() + .enumerate() // for each segment, find the child item - .try_fold(first, |res, segment| { + .try_fold(first, |res, (idx, segment)| { + let matches_ns = |res: Res| { + // If at the last segment in the path, respect the namespace hint + if idx == path.len() - 1 { + match namespace_hint { + Some(ns) => res.matches_ns(ns), + None => true, + } + } else { + res.matches_ns(Namespace::TypeNS) + } + }; + let def_id = res.def_id(); - if let Some(item) = item_child_by_name(tcx, def_id, segment) { + if let Some(item) = item_child_by_name(tcx, def_id, segment, matches_ns) { 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)) + .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment, matches_ns)) } else { None } @@ -569,8 +645,10 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { /// Convenience function to get the `DefId` of a trait by path. /// It could be a trait or trait alias. +/// +/// This function is expensive and should be used sparingly. pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option { - match def_path_res(cx, path) { + match def_path_res(cx, path, Some(Namespace::TypeNS)) { Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), _ => None, } @@ -738,7 +816,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { } }, ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func), - ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone), + ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), _ => false, } @@ -1136,17 +1214,14 @@ pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool { /// Returns `true` if `expr` contains a return expression pub fn contains_return(expr: &hir::Expr<'_>) -> bool { - let mut found = false; - expr_visitor_no_bodies(|expr| { - if !found { - if let hir::ExprKind::Ret(..) = &expr.kind { - found = true; - } + for_each_expr(expr, |e| { + if matches!(e.kind, hir::ExprKind::Ret(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - !found }) - .visit_expr(expr); - found + .is_some() } /// Extends the span to the beginning of the spans line, incl. whitespaces. @@ -1386,8 +1461,8 @@ pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool { /// Examples of coercions can be found in the Nomicon at /// . /// -/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for more -/// information on adjustments and coercions. +/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for +/// more information on adjustments and coercions. pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { cx.typeck_results().adjustments().get(e.hir_id).is_some() } @@ -1553,7 +1628,7 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc if_chain! { if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind; if ddpos.as_opt_usize().is_none(); - if is_lang_ctor(cx, path, ResultOk); + if is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk); if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind; if path_to_local_id(arm.body, hir_id); then { @@ -1565,7 +1640,7 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind { - is_lang_ctor(cx, path, ResultErr) + is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr) } else { false } @@ -2295,6 +2370,29 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { }); } +/// Return all the comments a given span contains +/// Comments are returned wrapped with their relevant delimiters +pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { + let snippet = sm.span_to_snippet(span).unwrap_or_default(); + let mut comments_buf: Vec = Vec::new(); + let mut index: usize = 0; + + for token in tokenize(&snippet) { + let token_range = index..(index + token.len as usize); + index += token.len as usize; + match token.kind { + TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => { + if let Some(comment) = snippet.get(token_range) { + comments_buf.push(comment.to_string()); + } + }, + _ => (), + } + } + + comments_buf.join("\n") +} + macro_rules! op_utils { ($($name:ident $assign:ident)*) => { /// Binary operation traits like `LangItem::Add` diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index a1808c097200..dd0ce1da6575 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -2,7 +2,7 @@ use crate::is_path_diagnostic_item; use crate::source::snippet_opt; -use crate::visitors::expr_visitor_no_bodies; +use crate::visitors::{for_each_expr, Descend}; use arrayvec::ArrayVec; use itertools::{izip, Either, Itertools}; @@ -16,6 +16,7 @@ use rustc_parse_format::{self as rpf, Alignment}; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol}; +use std::iter::{once, zip}; use std::ops::ControlFlow; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ @@ -270,20 +271,19 @@ fn find_assert_args_inner<'a, const N: usize>( }; let mut args = ArrayVec::new(); let mut panic_expn = None; - expr_visitor_no_bodies(|e| { + let _: Option = for_each_expr(expr, |e| { if args.is_full() { if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() { panic_expn = PanicExpn::parse(cx, e); } - panic_expn.is_none() + ControlFlow::Continue(Descend::from(panic_expn.is_none())) } else if is_assert_arg(cx, e, expn) { args.push(e); - false + ControlFlow::Continue(Descend::No) } else { - true + ControlFlow::Continue(Descend::Yes) } - }) - .visit_expr(expr); + }); let args = args.into_inner().ok()?; // if no `panic!(..)` is found, use `PanicExpn::Empty` // to indicate that the default assertion message is used @@ -297,22 +297,19 @@ fn find_assert_within_debug_assert<'a>( expn: ExpnId, assert_name: Symbol, ) -> Option<(&'a Expr<'a>, ExpnId)> { - let mut found = None; - expr_visitor_no_bodies(|e| { - if found.is_some() || !e.span.from_expansion() { - return false; + for_each_expr(expr, |e| { + if !e.span.from_expansion() { + return ControlFlow::Continue(Descend::No); } let e_expn = e.span.ctxt().outer_expn(); if e_expn == expn { - return true; + ControlFlow::Continue(Descend::Yes) + } else if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) { + ControlFlow::Break((e, e_expn)) + } else { + ControlFlow::Continue(Descend::No) } - if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) { - found = Some((e, e_expn)); - } - false }) - .visit_expr(expr); - found } fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool { @@ -392,20 +389,18 @@ impl FormatString { unescape_literal(inner, mode, &mut |_, ch| match ch { Ok(ch) => unescaped.push(ch), Err(e) if !e.is_fatal() => (), - Err(e) => panic!("{:?}", e), + Err(e) => panic!("{e:?}"), }); let mut parts = Vec::new(); - expr_visitor_no_bodies(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(symbol, _) = lit.node { - parts.push(symbol); - } + let _: Option = for_each_expr(pieces, |expr| { + if let ExprKind::Lit(lit) = &expr.kind + && let LitKind::Str(symbol, _) = lit.node + { + parts.push(symbol); } - - true - }) - .visit_expr(pieces); + ControlFlow::Continue(()) + }); Some(Self { span, @@ -418,7 +413,8 @@ impl FormatString { } struct FormatArgsValues<'tcx> { - /// See `FormatArgsExpn::value_args` + /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for + /// `format!("{x} {} {y}", 1, z + 2)`. value_args: Vec<&'tcx Expr<'tcx>>, /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in /// `value_args` @@ -431,7 +427,7 @@ impl<'tcx> FormatArgsValues<'tcx> { fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self { let mut pos_to_value_index = Vec::new(); let mut value_args = Vec::new(); - expr_visitor_no_bodies(|expr| { + let _: Option = for_each_expr(args, |expr| { if expr.span.ctxt() == args.span.ctxt() { // ArgumentV1::new_() // ArgumentV1::from_usize() @@ -453,16 +449,13 @@ impl<'tcx> FormatArgsValues<'tcx> { pos_to_value_index.push(val_idx); } - - true + ControlFlow::Continue(Descend::Yes) } else { // assume that any expr with a differing span is a value value_args.push(expr); - - false + ControlFlow::Continue(Descend::No) } - }) - .visit_expr(args); + }); Self { value_args, @@ -545,19 +538,32 @@ fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span { ) } +/// How a format parameter is used in the format string #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum FormatParamKind { /// An implicit parameter , such as `{}` or `{:?}`. Implicit, - /// A parameter with an explicit number, or an asterisk precision. e.g. `{1}`, `{0:?}`, - /// `{:.0$}` or `{:.*}`. + /// A parameter with an explicit number, e.g. `{1}`, `{0:?}`, or `{:.0$}` Numbered, + /// A parameter with an asterisk precision. e.g. `{:.*}`. + Starred, /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`. Named(Symbol), /// An implicit named parameter, such as the `y` in `format!("{y}")`. NamedInline(Symbol), } +/// Where a format parameter is being used in the format string +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum FormatParamUsage { + /// Appears as an argument, e.g. `format!("{}", foo)` + Argument, + /// Appears as a width, e.g. `format!("{:width$}", foo, width = 1)` + Width, + /// Appears as a precision, e.g. `format!("{:.precision$}", foo, precision = 1)` + Precision, +} + /// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g. /// /// ``` @@ -573,6 +579,8 @@ pub struct FormatParam<'tcx> { pub value: &'tcx Expr<'tcx>, /// How this parameter refers to its `value`. pub kind: FormatParamKind, + /// Where this format param is being used - argument/width/precision + pub usage: FormatParamUsage, /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters. /// /// ```text @@ -585,6 +593,7 @@ pub struct FormatParam<'tcx> { impl<'tcx> FormatParam<'tcx> { fn new( mut kind: FormatParamKind, + usage: FormatParamUsage, position: usize, inner: rpf::InnerSpan, values: &FormatArgsValues<'tcx>, @@ -599,7 +608,12 @@ impl<'tcx> FormatParam<'tcx> { kind = FormatParamKind::NamedInline(name); } - Some(Self { value, kind, span }) + Some(Self { + value, + kind, + usage, + span, + }) } } @@ -618,6 +632,7 @@ pub enum Count<'tcx> { impl<'tcx> Count<'tcx> { fn new( + usage: FormatParamUsage, count: rpf::Count<'_>, position: Option, inner: Option, @@ -625,15 +640,27 @@ impl<'tcx> Count<'tcx> { ) -> Option { Some(match count { rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)), - rpf::Count::CountIsName(name, span) => Self::Param(FormatParam::new( + rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new( FormatParamKind::Named(Symbol::intern(name)), + usage, position?, - span, + inner?, + values, + )?), + rpf::Count::CountIsParam(_) => Self::Param(FormatParam::new( + FormatParamKind::Numbered, + usage, + position?, + inner?, + values, + )?), + rpf::Count::CountIsStar(_) => Self::Param(FormatParam::new( + FormatParamKind::Starred, + usage, + position?, + inner?, values, )?), - rpf::Count::CountIsParam(_) | rpf::Count::CountIsStar(_) => { - Self::Param(FormatParam::new(FormatParamKind::Numbered, position?, inner?, values)?) - }, rpf::Count::CountImplied => Self::Implied, }) } @@ -676,8 +703,20 @@ impl<'tcx> FormatSpec<'tcx> { fill: spec.fill, align: spec.align, flags: spec.flags, - precision: Count::new(spec.precision, positions.precision, spec.precision_span, values)?, - width: Count::new(spec.width, positions.width, spec.width_span, values)?, + precision: Count::new( + FormatParamUsage::Precision, + spec.precision, + positions.precision, + spec.precision_span, + values, + )?, + width: Count::new( + FormatParamUsage::Width, + spec.width, + positions.width, + spec.width_span, + values, + )?, r#trait: match spec.ty { "" => sym::Display, "?" => sym::Debug, @@ -723,17 +762,87 @@ pub struct FormatArg<'tcx> { pub struct FormatArgsExpn<'tcx> { /// The format string literal. pub format_string: FormatString, - // The format arguments, such as `{:?}`. + /// The format arguments, such as `{:?}`. pub args: Vec>, /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will /// include this added newline. pub newline: bool, - /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for + /// Spans of the commas between the format string and explicit values, excluding any trailing + /// comma + /// + /// ```ignore + /// format!("..", 1, 2, 3,) + /// // ^ ^ ^ + /// ``` + comma_spans: Vec, + /// Explicit values passed after the format string, ignoring implicit captures. `[1, z + 2]` for /// `format!("{x} {} {y}", 1, z + 2)`. - value_args: Vec<&'tcx Expr<'tcx>>, + explicit_values: Vec<&'tcx Expr<'tcx>>, } impl<'tcx> FormatArgsExpn<'tcx> { + /// Gets the spans of the commas inbetween the format string and explicit args, not including + /// any trailing comma + /// + /// ```ignore + /// format!("{} {}", a, b) + /// // ^ ^ + /// ``` + /// + /// Ensures that the format string and values aren't coming from a proc macro that sets the + /// output span to that of its input + fn comma_spans(cx: &LateContext<'_>, explicit_values: &[&Expr<'_>], fmt_span: Span) -> Option> { + // `format!("{} {} {c}", "one", "two", c = "three")` + // ^^^^^ ^^^^^ ^^^^^^^ + let value_spans = explicit_values + .iter() + .map(|val| hygiene::walk_chain(val.span, fmt_span.ctxt())); + + // `format!("{} {} {c}", "one", "two", c = "three")` + // ^^ ^^ ^^^^^^ + let between_spans = once(fmt_span) + .chain(value_spans) + .tuple_windows() + .map(|(start, end)| start.between(end)); + + let mut comma_spans = Vec::new(); + for between_span in between_spans { + let mut offset = 0; + let mut seen_comma = false; + + for token in tokenize(&snippet_opt(cx, between_span)?) { + match token.kind { + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {}, + TokenKind::Comma if !seen_comma => { + seen_comma = true; + + let base = between_span.data(); + comma_spans.push(Span::new( + base.lo + BytePos(offset), + base.lo + BytePos(offset + 1), + base.ctxt, + base.parent, + )); + }, + // named arguments, `start_val, name = end_val` + // ^^^^^^^^^ between_span + TokenKind::Ident | TokenKind::Eq if seen_comma => {}, + // An unexpected token usually indicates the format string or a value came from a proc macro output + // that sets the span of its output to an input, e.g. `println!(some_proc_macro!("input"), ..)` that + // emits a string literal with the span set to that of `"input"` + _ => return None, + } + offset += token.len; + } + + if !seen_comma { + return None; + } + } + + Some(comma_spans) + } + pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option { let macro_name = macro_backtrace(expr.span) .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) @@ -797,6 +906,7 @@ impl<'tcx> FormatArgsExpn<'tcx> { // NamedInline is handled by `FormatParam::new()` rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)), }, + FormatParamUsage::Argument, position.value, parsed_arg.position_span, &values, @@ -807,11 +917,22 @@ impl<'tcx> FormatArgsExpn<'tcx> { }) .collect::>>()?; + let mut explicit_values = values.value_args; + // remove values generated for implicitly captured vars + let len = explicit_values + .iter() + .take_while(|val| !format_string.span.contains(val.span)) + .count(); + explicit_values.truncate(len); + + let comma_spans = Self::comma_spans(cx, &explicit_values, format_string.span)?; + Some(Self { format_string, args, - value_args: values.value_args, newline, + comma_spans, + explicit_values, }) } else { None @@ -819,27 +940,25 @@ impl<'tcx> FormatArgsExpn<'tcx> { } pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option { - let mut format_args = None; - expr_visitor_no_bodies(|e| { - if format_args.is_some() { - return false; - } + for_each_expr(expr, |e| { let e_ctxt = e.span.ctxt(); if e_ctxt == expr.span.ctxt() { - return true; + ControlFlow::Continue(Descend::Yes) + } else if e_ctxt.outer_expn().is_descendant_of(expn_id) { + if let Some(args) = FormatArgsExpn::parse(cx, e) { + ControlFlow::Break(args) + } else { + ControlFlow::Continue(Descend::No) + } + } else { + ControlFlow::Continue(Descend::No) } - if e_ctxt.outer_expn().is_descendant_of(expn_id) { - format_args = FormatArgsExpn::parse(cx, e); - } - false }) - .visit_expr(expr); - format_args } /// Source callsite span of all inputs pub fn inputs_span(&self) -> Span { - match *self.value_args { + match *self.explicit_values { [] => self.format_string.span, [.., last] => self .format_string @@ -848,6 +967,22 @@ impl<'tcx> FormatArgsExpn<'tcx> { } } + /// Get the span of a value expanded to the previous comma, e.g. for the value `10` + /// + /// ```ignore + /// format("{}.{}", 10, 11) + /// // ^^^^ + /// ``` + pub fn value_with_prev_comma_span(&self, value_id: HirId) -> Option { + for (comma_span, value) in zip(&self.comma_spans, &self.explicit_values) { + if value.hir_id == value_id { + return Some(comma_span.to(hygiene::walk_chain(value.span, comma_span.ctxt()))); + } + } + + None + } + /// Iterator of all format params, both values and those referenced by `width`/`precision`s. pub fn params(&'tcx self) -> impl Iterator> { self.args diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 62020e21c815..8b843732a236 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -13,10 +13,11 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,62,0 { BOOL_THEN_SOME } + 1,58,0 { FORMAT_ARGS_CAPTURE } 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR } 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST } 1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS } - 1,50,0 { BOOL_THEN } + 1,50,0 { BOOL_THEN, CLAMP } 1,47,0 { TAU } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 07170e2df12a..13938645fc3e 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -34,7 +34,6 @@ pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "defa pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; /// Preferably use the diagnostic item `sym::deref_method` where possible pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; -pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"]; pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; #[cfg(feature = "internal")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; @@ -64,8 +63,6 @@ pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; -pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; -pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs index 0226f74906b5..88837d8a143e 100644 --- a/clippy_utils/src/ptr.rs +++ b/clippy_utils/src/ptr.rs @@ -1,7 +1,7 @@ use crate::source::snippet; -use crate::visitors::expr_visitor_no_bodies; +use crate::visitors::{for_each_expr, Descend}; use crate::{path_to_local_id, strip_pat_refs}; -use rustc_hir::intravisit::Visitor; +use core::ops::ControlFlow; use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; @@ -30,28 +30,23 @@ fn extract_clone_suggestions<'tcx>( replace: &[(&'static str, &'static str)], body: &'tcx Body<'_>, ) -> Option)>> { - let mut abort = false; let mut spans = Vec::new(); - expr_visitor_no_bodies(|expr| { - if abort { - return false; - } - if let ExprKind::MethodCall(seg, recv, [], _) = expr.kind { - if path_to_local_id(recv, id) { - if seg.ident.name.as_str() == "capacity" { - abort = true; - return false; - } - for &(fn_name, suffix) in replace { - if seg.ident.name.as_str() == fn_name { - spans.push((expr.span, snippet(cx, recv.span, "_") + suffix)); - return false; - } + for_each_expr(body, |e| { + if let ExprKind::MethodCall(seg, recv, [], _) = e.kind + && path_to_local_id(recv, id) + { + if seg.ident.as_str() == "capacity" { + return ControlFlow::Break(()); + } + for &(fn_name, suffix) in replace { + if seg.ident.as_str() == fn_name { + spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); + return ControlFlow::Continue(Descend::No); } } } - !abort + ControlFlow::Continue(Descend::Yes) }) - .visit_body(body); - if abort { None } else { Some(spans) } + .is_none() + .then_some(spans) } diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index f7ce71917726..5a0721486e33 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -33,10 +33,10 @@ pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Trait(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, - ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), - ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), - ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), - ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate), + ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"), + ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"), + ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"), + ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {predicate:#?}"), } } match predicates.parent { @@ -319,8 +319,7 @@ fn check_terminator<'a, 'tcx>( span, format!( "can only call other `const fn` within a `const fn`, \ - but `{:?}` is not stable as `const fn`", - func, + but `{func:?}` is not stable as `const fn`", ) .into(), )); @@ -368,8 +367,9 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bo // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. - // HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver` doesn't accept - // the `-dev` version number so we have to strip it off. + // HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver` + // doesn't accept the `-dev` version number so we have to strip it + // off. let short_version = since .as_str() .split('-') @@ -380,8 +380,9 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bo crate::meets_msrv( msrv, - RustcVersion::parse(since.as_str()) - .unwrap_or_else(|err| panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")), + RustcVersion::parse(since.as_str()).unwrap_or_else(|err| { + panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}") + }), ) } else { // Unstable const fn with the feature enabled. diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index d85f591fb9a4..d28bd92d708b 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -25,11 +25,11 @@ pub fn expr_block<'a, T: LintContext>( if expr.span.from_expansion() { Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) } else if let ExprKind::Block(_, _) = expr.kind { - Cow::Owned(format!("{}{}", code, string)) + Cow::Owned(format!("{code}{string}")) } else if string.is_empty() { - Cow::Owned(format!("{{ {} }}", code)) + Cow::Owned(format!("{{ {code} }}")) } else { - Cow::Owned(format!("{{\n{};\n{}\n}}", code, string)) + Cow::Owned(format!("{{\n{code};\n{string}\n}}")) } } @@ -392,6 +392,16 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span { .span() } +/// Expand a span to include a preceding comma +/// ```rust,ignore +/// writeln!(o, "") -> writeln!(o, "") +/// ^^ ^^^^ +/// ``` +pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { + let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); + extended.with_lo(extended.lo() - BytePos(1)) +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; @@ -466,7 +476,7 @@ mod test { #[test] fn test_without_block_comments_lines_without_block_comments() { let result = without_block_comments(vec!["/*", "", "*/"]); - println!("result: {:?}", result); + println!("result: {result:?}"); assert!(result.is_empty()); let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index e53c40e95760..ef836e84829b 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -10,13 +10,13 @@ use rustc_ast_pretty::pprust::token_kind_to_string; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty; use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::borrow::Cow; use std::fmt::{Display, Write as _}; use std::ops::{Add, Neg, Not, Sub}; @@ -310,19 +310,19 @@ impl<'a> Sugg<'a> { /// Convenience method to transform suggestion into a return call pub fn make_return(self) -> Sugg<'static> { - Sugg::NonParen(Cow::Owned(format!("return {}", self))) + Sugg::NonParen(Cow::Owned(format!("return {self}"))) } /// Convenience method to transform suggestion into a block /// where the suggestion is a trailing expression pub fn blockify(self) -> Sugg<'static> { - Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self))) + Sugg::NonParen(Cow::Owned(format!("{{ {self} }}"))) } /// Convenience method to prefix the expression with the `async` keyword. /// Can be used after `blockify` to create an async block. pub fn asyncify(self) -> Sugg<'static> { - Sugg::NonParen(Cow::Owned(format!("async {}", self))) + Sugg::NonParen(Cow::Owned(format!("async {self}"))) } /// Convenience method to create the `..` or `...` @@ -346,12 +346,12 @@ impl<'a> Sugg<'a> { if has_enclosing_paren(&sugg) { Sugg::MaybeParen(sugg) } else { - Sugg::NonParen(format!("({})", sugg).into()) + Sugg::NonParen(format!("({sugg})").into()) } }, Sugg::BinOp(op, lhs, rhs) => { let sugg = binop_to_string(op, &lhs, &rhs); - Sugg::NonParen(format!("({})", sugg).into()) + Sugg::NonParen(format!("({sugg})").into()) }, } } @@ -379,20 +379,18 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { | AssocOp::Greater | AssocOp::GreaterEqual => { format!( - "{} {} {}", - lhs, - op.to_ast_binop().expect("Those are AST ops").to_string(), - rhs + "{lhs} {} {rhs}", + op.to_ast_binop().expect("Those are AST ops").to_string() ) }, - AssocOp::Assign => format!("{} = {}", lhs, rhs), + AssocOp::Assign => format!("{lhs} = {rhs}"), AssocOp::AssignOp(op) => { - format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs) + format!("{lhs} {}= {rhs}", token_kind_to_string(&token::BinOp(op))) }, - AssocOp::As => format!("{} as {}", lhs, rhs), - AssocOp::DotDot => format!("{}..{}", lhs, rhs), - AssocOp::DotDotEq => format!("{}..={}", lhs, rhs), - AssocOp::Colon => format!("{}: {}", lhs, rhs), + AssocOp::As => format!("{lhs} as {rhs}"), + AssocOp::DotDot => format!("{lhs}..{rhs}"), + AssocOp::DotDotEq => format!("{lhs}..={rhs}"), + AssocOp::Colon => format!("{lhs}: {rhs}"), } } @@ -523,7 +521,7 @@ impl Display for ParenHelper { /// operators have the same /// precedence. pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> { - Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into()) + Sugg::MaybeParen(format!("{op}{}", expr.maybe_par()).into()) } /// Builds the string for ` ` adding parenthesis when necessary. @@ -744,7 +742,7 @@ impl DiagnosticExt for rustc_errors::Diagnostic { if let Some(indent) = indentation(cx, item) { let span = item.with_hi(item.lo()); - self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability); + self.span_suggestion(span, msg, format!("{attr}\n{indent}"), applicability); } } @@ -758,14 +756,14 @@ impl DiagnosticExt for rustc_errors::Diagnostic { .map(|l| { if first { first = false; - format!("{}\n", l) + format!("{l}\n") } else { - format!("{}{}\n", indent, l) + format!("{indent}{l}\n") } }) .collect::(); - self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability); + self.span_suggestion(span, msg, format!("{new_item}\n{indent}"), applicability); } } @@ -863,7 +861,7 @@ impl<'tcx> DerefDelegate<'_, 'tcx> { pub fn finish(&mut self) -> String { let end_span = Span::new(self.next_pos, self.closure_span.hi(), self.closure_span.ctxt(), None); let end_snip = snippet_with_applicability(self.cx, end_span, "..", &mut self.applicability); - let sugg = format!("{}{}", self.suggestion_start, end_snip); + let sugg = format!("{}{end_snip}", self.suggestion_start); if self.closure_arg_is_type_annotated_double_ref { sugg.replacen('&', "", 1) } else { @@ -925,7 +923,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { if cmt.place.projections.is_empty() { // handle item without any projection, that needs an explicit borrowing // i.e.: suggest `&x` instead of `x` - let _ = write!(self.suggestion_start, "{}&{}", start_snip, ident_str); + let _ = write!(self.suggestion_start, "{start_snip}&{ident_str}"); } else { // cases where a parent `Call` or `MethodCall` is using the item // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()` @@ -940,7 +938,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // given expression is the self argument and will be handled completely by the compiler // i.e.: `|x| x.is_something()` ExprKind::MethodCall(_, self_expr, ..) if self_expr.hir_id == cmt.hir_id => { - let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj); + let _ = write!(self.suggestion_start, "{start_snip}{ident_str_with_proj}"); self.next_pos = span.hi(); return; }, @@ -973,9 +971,9 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { } else { ident_str }; - format!("{}{}", start_snip, ident) + format!("{start_snip}{ident}") } else { - format!("{}&{}", start_snip, ident_str) + format!("{start_snip}&{ident_str}") }; self.suggestion_start.push_str(&ident_sugg); self.next_pos = span.hi(); @@ -1042,13 +1040,13 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { for item in projections { if item.kind == ProjectionKind::Deref { - replacement_str = format!("*{}", replacement_str); + replacement_str = format!("*{replacement_str}"); } } } } - let _ = write!(self.suggestion_start, "{}{}", start_snip, replacement_str); + let _ = write!(self.suggestion_start, "{start_snip}{replacement_str}"); } self.next_pos = span.hi(); } @@ -1056,7 +1054,13 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } #[cfg(test)] diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 56343880320d..934470bd135b 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -12,11 +12,11 @@ use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; -use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_middle::ty::{ self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy, Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; +use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Size, VariantIdx}; diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 76bfec75726d..b5ec3fef3e0b 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,15 +1,16 @@ use crate as utils; -use crate::visitors::{expr_visitor, expr_visitor_no_bodies}; +use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend}; +use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirIdSet; use rustc_hir::{Expr, ExprKind, HirId, Node}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined. pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option { @@ -73,7 +74,13 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { self.update(cmt); } - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } pub struct ParamBindingIdCollector { @@ -142,28 +149,17 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { } pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { - let mut seen_return_break_continue = false; - expr_visitor_no_bodies(|ex| { - if seen_return_break_continue { - return false; - } - match &ex.kind { - ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { - seen_return_break_continue = true; - }, + for_each_expr(expression, |e| { + match e.kind { + ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()), // Something special could be done here to handle while or for loop // desugaring, as this will detect a break if there's a while loop // or a for loop inside the expression. - _ => { - if ex.span.from_expansion() { - seen_return_break_continue = true; - } - }, + _ if e.span.from_expansion() => ControlFlow::Break(()), + _ => ControlFlow::Continue(()), } - !seen_return_break_continue }) - .visit_expr(expression); - seen_return_break_continue + .is_some() } pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool { @@ -194,23 +190,16 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr return true; } - let mut used_after_expr = false; let mut past_expr = false; - expr_visitor(cx, |expr| { - if used_after_expr { - return false; - } - - if expr.hir_id == after.hir_id { + for_each_expr_with_closures(cx, block, |e| { + if e.hir_id == after.hir_id { past_expr = true; - return false; + ControlFlow::Continue(Descend::No) + } else if past_expr && utils::path_to_local_id(e, local_id) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::Yes) } - - if past_expr && utils::path_to_local_id(expr, local_id) { - used_after_expr = true; - } - !used_after_expr }) - .visit_block(block); - used_after_expr + .is_some() } diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 232d571902b6..d4294f18fd50 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -5,14 +5,13 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; use rustc_hir::{ - Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, Stmt, UnOp, - UnsafeSource, Unsafety, + AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, + Stmt, UnOp, UnsafeSource, Unsafety, }; use rustc_lint::LateContext; -use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, Ty, TypeckResults}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults}; use rustc_span::Span; mod internal { @@ -48,6 +47,26 @@ impl Continue for Descend { } } +/// A type which can be visited. +pub trait Visitable<'tcx> { + /// Calls the corresponding `visit_*` function on the visitor. + fn visit>(self, visitor: &mut V); +} +macro_rules! visitable_ref { + ($t:ident, $f:ident) => { + impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { + fn visit>(self, visitor: &mut V) { + visitor.$f(self); + } + } + }; +} +visitable_ref!(Arm, visit_arm); +visitable_ref!(Block, visit_block); +visitable_ref!(Body, visit_body); +visitable_ref!(Expr, visit_expr); +visitable_ref!(Stmt, visit_stmt); + /// Calls the given function once for each expression contained. This does not enter any bodies or /// nested items. pub fn for_each_expr<'tcx, B, C: Continue>( @@ -82,57 +101,63 @@ pub fn for_each_expr<'tcx, B, C: Continue>( v.res } -/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested -/// bodies (i.e. closures) are visited. -/// If the callback returns `true`, the expr just provided to the callback is walked. -#[must_use] -pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> { - struct V<'tcx, F> { - hir: Map<'tcx>, +/// Calls the given function once for each expression contained. This will enter bodies, but not +/// nested items. +pub fn for_each_expr_with_closures<'tcx, B, C: Continue>( + cx: &LateContext<'tcx>, + node: impl Visitable<'tcx>, + f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, +) -> Option { + struct V<'tcx, B, F> { + tcx: TyCtxt<'tcx>, f: F, + res: Option, } - impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> { + impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow> Visitor<'tcx> for V<'tcx, B, F> { type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { - self.hir + self.tcx.hir() } - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if (self.f)(expr) { - walk_expr(self, expr); + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + if self.res.is_some() { + return; + } + match (self.f)(e) { + ControlFlow::Continue(c) if c.descend() => walk_expr(self, e), + ControlFlow::Break(b) => self.res = Some(b), + ControlFlow::Continue(_) => (), } } - } - V { hir: cx.tcx.hir(), f } -} -/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested -/// bodies (i.e. closures) are not visited. -/// If the callback returns `true`, the expr just provided to the callback is walked. -#[must_use] -pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> { - struct V(F); - impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V { - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if (self.0)(e) { - walk_expr(self, e); - } - } + // Only walk closures + fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} + // Avoid unnecessary `walk_*` calls. + fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {} + fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {} + fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {} + // Avoid monomorphising all `visit_*` functions. + fn visit_nested_item(&mut self, _: ItemId) {} } - V(f) + let mut v = V { + tcx: cx.tcx, + f, + res: None, + }; + node.visit(&mut v); + v.res } /// returns `true` if expr contains match expr desugared from try fn contains_try(expr: &hir::Expr<'_>) -> bool { - let mut found = false; - expr_visitor_no_bodies(|e| { - if !found { - found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)); + for_each_expr(expr, |e| { + if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - !found }) - .visit_expr(expr); - found + .is_some() } pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool @@ -228,68 +253,29 @@ where } } -/// A type which can be visited. -pub trait Visitable<'tcx> { - /// Calls the corresponding `visit_*` function on the visitor. - fn visit>(self, visitor: &mut V); -} -macro_rules! visitable_ref { - ($t:ident, $f:ident) => { - impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { - fn visit>(self, visitor: &mut V) { - visitor.$f(self); - } - } - }; -} -visitable_ref!(Arm, visit_arm); -visitable_ref!(Block, visit_block); -visitable_ref!(Body, visit_body); -visitable_ref!(Expr, visit_expr); -visitable_ref!(Stmt, visit_stmt); - -// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I -// where -// I::Item: Visitable<'tcx>, -// { -// fn visit>(self, visitor: &mut V) { -// for x in self { -// x.visit(visitor); -// } -// } -// } - /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { - let mut found = false; - expr_visitor(cx, |e| { - if found { - return false; - } - + for_each_expr_with_closures(cx, cx.tcx.hir().body(body).value, |e| { if let ExprKind::Path(p) = &e.kind { if cx.qpath_res(p, e.hir_id) == res { - found = true; + return ControlFlow::Break(()); } } - !found + ControlFlow::Continue(()) }) - .visit_expr(cx.tcx.hir().body(body).value); - found + .is_some() } /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { - let mut is_used = false; - let mut visitor = expr_visitor(cx, |expr| { - if !is_used { - is_used = path_to_local_id(expr, id); + for_each_expr_with_closures(cx, visitable, |e| { + if path_to_local_id(e, id) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - !is_used - }); - visitable.visit(&mut visitor); - drop(visitor); - is_used + }) + .is_some() } /// Checks if the given expression is a constant. diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 737c845c0451..de31c16b819e 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -12,9 +12,11 @@ publish = false [dependencies] cargo_metadata = "0.14" clap = "3.2" +crossbeam-channel = "0.5.6" flate2 = "1.0" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.85" tar = "0.4" toml = "0.5" ureq = "2.2" diff --git a/lintcheck/README.md b/lintcheck/README.md index 6f3d23382ce1..6142de5e3130 100644 --- a/lintcheck/README.md +++ b/lintcheck/README.md @@ -69,9 +69,27 @@ is checked. is explicitly specified in the options. ### Fix mode -You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `--fix` and +You can run `cargo lintcheck --fix` which will run Clippy with `--fix` and print a warning if Clippy's suggestions fail to apply (if the resulting code does not build). This lets us spot bad suggestions or false positives automatically in some cases. Please note that the target dir should be cleaned afterwards since clippy will modify the downloaded sources which can lead to unexpected results when running lintcheck again afterwards. + +### Recursive mode +You can run `cargo lintcheck --recursive` to also run Clippy on the dependencies +of the crates listed in the crates source `.toml`. e.g. adding `rand 0.8.5` +would also lint `rand_core`, `rand_chacha`, etc. + +Particularly slow crates in the dependency graph can be ignored using +`recursive.ignore`: + +```toml +[crates] +cargo = {name = "cargo", versions = ['0.64.0']} + +[recursive] +ignore = [ + "unicode-normalization", +] +``` diff --git a/lintcheck/lintcheck_crates.toml b/lintcheck/lintcheck_crates.toml index ebbe9c9ae675..52f7fee47b61 100644 --- a/lintcheck/lintcheck_crates.toml +++ b/lintcheck/lintcheck_crates.toml @@ -33,3 +33,11 @@ cfg-expr = {name = "cfg-expr", versions = ['0.7.1']} puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"} rpmalloc = {name = "rpmalloc", versions = ['0.2.0']} tame-oidc = {name = "tame-oidc", versions = ['0.1.0']} + +[recursive] +ignore = [ + # Takes ~30s to lint + "combine", + # Has 1.2 million `clippy::match_same_arms`s + "unicode-normalization", +] diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index 1742cf677c0f..b344db634f61 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -34,11 +34,16 @@ fn get_clap_config() -> ArgMatches { Arg::new("markdown") .long("markdown") .help("Change the reports table to use markdown links"), + Arg::new("recursive") + .long("--recursive") + .help("Run clippy on the dependencies of crates specified in crates-toml") + .conflicts_with("threads") + .conflicts_with("fix"), ]) .get_matches() } -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct LintcheckConfig { /// max number of jobs to spawn (default 1) pub max_jobs: usize, @@ -54,6 +59,8 @@ pub(crate) struct LintcheckConfig { pub lint_filter: Vec, /// Indicate if the output should support markdown syntax pub markdown: bool, + /// Run clippy on the dependencies of crates + pub recursive: bool, } impl LintcheckConfig { @@ -119,6 +126,7 @@ impl LintcheckConfig { fix: clap_config.contains_id("fix"), lint_filter, markdown, + recursive: clap_config.contains_id("recursive"), } } } diff --git a/lintcheck/src/driver.rs b/lintcheck/src/driver.rs new file mode 100644 index 000000000000..63221bab32d3 --- /dev/null +++ b/lintcheck/src/driver.rs @@ -0,0 +1,67 @@ +use crate::recursive::{deserialize_line, serialize_line, DriverInfo}; + +use std::io::{self, BufReader, Write}; +use std::net::TcpStream; +use std::process::{self, Command, Stdio}; +use std::{env, mem}; + +/// 1. Sends [DriverInfo] to the [crate::recursive::LintcheckServer] running on `addr` +/// 2. Receives [bool] from the server, if `false` returns `None` +/// 3. Otherwise sends the stderr of running `clippy-driver` to the server +fn run_clippy(addr: &str) -> Option { + let driver_info = DriverInfo { + package_name: env::var("CARGO_PKG_NAME").ok()?, + crate_name: env::var("CARGO_CRATE_NAME").ok()?, + version: env::var("CARGO_PKG_VERSION").ok()?, + }; + + let mut stream = BufReader::new(TcpStream::connect(addr).unwrap()); + + serialize_line(&driver_info, stream.get_mut()); + + let should_run = deserialize_line::(&mut stream); + if !should_run { + return None; + } + + // Remove --cap-lints allow so that clippy runs and lints are emitted + let mut include_next = true; + let args = env::args().skip(1).filter(|arg| match arg.as_str() { + "--cap-lints=allow" => false, + "--cap-lints" => { + include_next = false; + false + }, + _ => mem::replace(&mut include_next, true), + }); + + let output = Command::new(env::var("CLIPPY_DRIVER").expect("missing env CLIPPY_DRIVER")) + .args(args) + .stdout(Stdio::inherit()) + .output() + .expect("failed to run clippy-driver"); + + stream + .get_mut() + .write_all(&output.stderr) + .unwrap_or_else(|e| panic!("{e:?} in {driver_info:?}")); + + match output.status.code() { + Some(0) => Some(0), + code => { + io::stderr().write_all(&output.stderr).unwrap(); + Some(code.expect("killed by signal")) + }, + } +} + +pub fn drive(addr: &str) { + process::exit(run_clippy(addr).unwrap_or_else(|| { + Command::new("rustc") + .args(env::args_os().skip(2)) + .status() + .unwrap() + .code() + .unwrap() + })) +} diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 9ee25280f046..cc2b3e1acec7 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -8,13 +8,17 @@ #![allow(clippy::collapsible_else_if)] mod config; +mod driver; +mod recursive; -use config::LintcheckConfig; +use crate::config::LintcheckConfig; +use crate::recursive::LintcheckServer; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::env; +use std::env::consts::EXE_SUFFIX; use std::fmt::Write as _; -use std::fs::write; +use std::fs; use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::process::Command; @@ -22,22 +26,12 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; use std::time::Duration; -use cargo_metadata::diagnostic::DiagnosticLevel; +use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel}; use cargo_metadata::Message; use rayon::prelude::*; use serde::{Deserialize, Serialize}; use walkdir::{DirEntry, WalkDir}; -#[cfg(not(windows))] -const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver"; -#[cfg(not(windows))] -const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy"; - -#[cfg(windows)] -const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe"; -#[cfg(windows)] -const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe"; - const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads"; const LINTCHECK_SOURCES: &str = "target/lintcheck/sources"; @@ -45,6 +39,13 @@ const LINTCHECK_SOURCES: &str = "target/lintcheck/sources"; #[derive(Debug, Serialize, Deserialize)] struct SourceList { crates: HashMap, + #[serde(default)] + recursive: RecursiveOptions, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +struct RecursiveOptions { + ignore: HashSet, } /// A crate source stored inside the .toml @@ -105,12 +106,7 @@ struct ClippyWarning { #[allow(unused)] impl ClippyWarning { - fn new(cargo_message: Message, krate: &Crate) -> Option { - let diag = match cargo_message { - Message::CompilerMessage(message) => message.message, - _ => return None, - }; - + fn new(diag: Diagnostic, crate_name: &str, crate_version: &str) -> Option { let lint_type = diag.code?.code; if !(lint_type.contains("clippy") || diag.message.contains("clippy")) || diag.message.contains("could not read cargo metadata") @@ -124,12 +120,12 @@ impl ClippyWarning { Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), Err(_) => format!( "target/lintcheck/sources/{}-{}/{}", - krate.name, krate.version, span.file_name + crate_name, crate_version, span.file_name ), }; Some(Self { - crate_name: krate.name.clone(), + crate_name: crate_name.to_owned(), file, line: span.line_start, column: span.column_start, @@ -142,8 +138,6 @@ impl ClippyWarning { fn to_output(&self, markdown: bool) -> String { let file_with_pos = format!("{}:{}:{}", &self.file, &self.line, &self.column); if markdown { - let lint = format!("`{}`", self.lint_type); - let mut file = self.file.clone(); if !file.starts_with('$') { file.insert_str(0, "../"); @@ -151,7 +145,7 @@ impl ClippyWarning { let mut output = String::from("| "); let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); - let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message); + let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message); output.push('\n'); output } else { @@ -243,6 +237,7 @@ impl CrateSource { } // check out the commit/branch/whatever if !Command::new("git") + .args(["-c", "advice.detachedHead=false"]) .arg("checkout") .arg(commit) .current_dir(&repo_path) @@ -309,10 +304,12 @@ impl Crate { fn run_clippy_lints( &self, cargo_clippy_path: &Path, + clippy_driver_path: &Path, target_dir_index: &AtomicUsize, total_crates_to_lint: usize, config: &LintcheckConfig, lint_filter: &Vec, + server: &Option, ) -> Vec { // advance the atomic index by one let index = target_dir_index.fetch_add(1, Ordering::SeqCst); @@ -336,36 +333,67 @@ impl Crate { let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); - let mut args = if config.fix { + let mut cargo_clippy_args = if config.fix { vec!["--fix", "--"] } else { vec!["--", "--message-format=json", "--"] }; + let mut clippy_args = Vec::<&str>::new(); if let Some(options) = &self.options { for opt in options { - args.push(opt); + clippy_args.push(opt); } } else { - args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) + clippy_args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) } if lint_filter.is_empty() { - args.push("--cap-lints=warn"); + clippy_args.push("--cap-lints=warn"); } else { - args.push("--cap-lints=allow"); - args.extend(lint_filter.iter().map(|filter| filter.as_str())) + clippy_args.push("--cap-lints=allow"); + clippy_args.extend(lint_filter.iter().map(|filter| filter.as_str())) } - let all_output = std::process::Command::new(&cargo_clippy_path) + if let Some(server) = server { + let target = shared_target_dir.join("recursive"); + + // `cargo clippy` is a wrapper around `cargo check` that mainly sets `RUSTC_WORKSPACE_WRAPPER` to + // `clippy-driver`. We do the same thing here with a couple changes: + // + // `RUSTC_WRAPPER` is used instead of `RUSTC_WORKSPACE_WRAPPER` so that we can lint all crate + // dependencies rather than only workspace members + // + // The wrapper is set to the `lintcheck` so we can force enable linting and ignore certain crates + // (see `crate::driver`) + let status = Command::new("cargo") + .arg("check") + .arg("--quiet") + .current_dir(&self.path) + .env("CLIPPY_ARGS", clippy_args.join("__CLIPPY_HACKERY__")) + .env("CARGO_TARGET_DIR", target) + .env("RUSTC_WRAPPER", env::current_exe().unwrap()) + // Pass the absolute path so `crate::driver` can find `clippy-driver`, as it's executed in various + // different working directories + .env("CLIPPY_DRIVER", clippy_driver_path) + .env("LINTCHECK_SERVER", server.local_addr.to_string()) + .status() + .expect("failed to run cargo"); + + assert_eq!(status.code(), Some(0)); + + return Vec::new(); + } + + cargo_clippy_args.extend(clippy_args); + + let all_output = Command::new(&cargo_clippy_path) // use the looping index to create individual target dirs .env( "CARGO_TARGET_DIR", shared_target_dir.join(format!("_{:?}", thread_index)), ) - // lint warnings will look like this: - // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter` - .args(&args) + .args(&cargo_clippy_args) .current_dir(&self.path) .output() .unwrap_or_else(|error| { @@ -404,7 +432,10 @@ impl Crate { // get all clippy warnings and ICEs let warnings: Vec = Message::parse_stream(stdout.as_bytes()) - .filter_map(|msg| ClippyWarning::new(msg.unwrap(), &self)) + .filter_map(|msg| match msg { + Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version), + _ => None, + }) .collect(); warnings @@ -423,8 +454,8 @@ fn build_clippy() { } } -/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy -fn read_crates(toml_path: &Path) -> Vec { +/// Read a `lintcheck_crates.toml` file +fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = @@ -484,7 +515,7 @@ fn read_crates(toml_path: &Path) -> Vec { // sort the crates crate_sources.sort(); - crate_sources + (crate_sources, crate_list.recursive) } /// Generate a short list of occurring lints-types and their count @@ -516,20 +547,20 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, /// check if the latest modification of the logfile is older than the modification date of the /// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck -fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { +fn lintcheck_needs_rerun(lintcheck_logs_path: &Path, paths: [&Path; 2]) -> bool { if !lintcheck_logs_path.exists() { return true; } let clippy_modified: std::time::SystemTime = { - let mut times = [CLIPPY_DRIVER_PATH, CARGO_CLIPPY_PATH].iter().map(|p| { + let [cargo, driver] = paths.map(|p| { std::fs::metadata(p) .expect("failed to get metadata of file") .modified() .expect("failed to get modification date") }); // the oldest modification of either of the binaries - std::cmp::max(times.next().unwrap(), times.next().unwrap()) + std::cmp::max(cargo, driver) }; let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_logs_path) @@ -543,6 +574,11 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { } fn main() { + // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive` + if let Ok(addr) = env::var("LINTCHECK_SERVER") { + driver::drive(&addr); + } + // assert that we launch lintcheck from the repo root (via cargo lintcheck) if std::fs::metadata("lintcheck/Cargo.toml").is_err() { eprintln!("lintcheck needs to be run from clippy's repo root!\nUse `cargo lintcheck` alternatively."); @@ -555,9 +591,15 @@ fn main() { build_clippy(); println!("Done compiling"); + let cargo_clippy_path = fs::canonicalize(format!("target/debug/cargo-clippy{EXE_SUFFIX}")).unwrap(); + let clippy_driver_path = fs::canonicalize(format!("target/debug/clippy-driver{EXE_SUFFIX}")).unwrap(); + // if the clippy bin is newer than our logs, throw away target dirs to force clippy to // refresh the logs - if lintcheck_needs_rerun(&config.lintcheck_results_path) { + if lintcheck_needs_rerun( + &config.lintcheck_results_path, + [&cargo_clippy_path, &clippy_driver_path], + ) { let shared_target_dir = "target/lintcheck/shared_target_dir"; // if we get an Err here, the shared target dir probably does simply not exist if let Ok(metadata) = std::fs::metadata(&shared_target_dir) { @@ -569,10 +611,6 @@ fn main() { } } - let cargo_clippy_path: PathBuf = PathBuf::from(CARGO_CLIPPY_PATH) - .canonicalize() - .expect("failed to canonicalize path to clippy binary"); - // assert that clippy is found assert!( cargo_clippy_path.is_file(), @@ -580,7 +618,7 @@ fn main() { cargo_clippy_path.display() ); - let clippy_ver = std::process::Command::new(CARGO_CLIPPY_PATH) + let clippy_ver = std::process::Command::new(&cargo_clippy_path) .arg("--version") .output() .map(|o| String::from_utf8_lossy(&o.stdout).into_owned()) @@ -589,7 +627,7 @@ fn main() { // download and extract the crates, then run clippy on them and collect clippy's warnings // flatten into one big list of warnings - let crates = read_crates(&config.sources_toml_path); + let (crates, recursive_options) = read_crates(&config.sources_toml_path); let old_stats = read_stats_from_file(&config.lintcheck_results_path); let counter = AtomicUsize::new(1); @@ -639,11 +677,31 @@ fn main() { .build_global() .unwrap(); - let clippy_warnings: Vec = crates + let server = config.recursive.then(|| { + let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); + + LintcheckServer::spawn(recursive_options) + }); + + let mut clippy_warnings: Vec = crates .par_iter() - .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, crates.len(), &config, &lint_filter)) + .flat_map(|krate| { + krate.run_clippy_lints( + &cargo_clippy_path, + &clippy_driver_path, + &counter, + crates.len(), + &config, + &lint_filter, + &server, + ) + }) .collect(); + if let Some(server) = server { + clippy_warnings.extend(server.warnings()); + } + // if we are in --fix mode, don't change the log files, terminate here if config.fix { return; @@ -681,8 +739,8 @@ fn main() { } println!("Writing logs to {}", config.lintcheck_results_path.display()); - std::fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap(); - write(&config.lintcheck_results_path, text).unwrap(); + fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap(); + fs::write(&config.lintcheck_results_path, text).unwrap(); print_stats(old_stats, new_stats, &config.lint_filter); } diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs new file mode 100644 index 000000000000..67dcfc2b199c --- /dev/null +++ b/lintcheck/src/recursive.rs @@ -0,0 +1,123 @@ +//! In `--recursive` mode we set the `lintcheck` binary as the `RUSTC_WRAPPER` of `cargo check`, +//! this allows [crate::driver] to be run for every dependency. The driver connects to +//! [LintcheckServer] to ask if it should be skipped, and if not sends the stderr of running clippy +//! on the crate to the server + +use crate::ClippyWarning; +use crate::RecursiveOptions; + +use std::collections::HashSet; +use std::io::{BufRead, BufReader, Read, Write}; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::sync::{Arc, Mutex}; +use std::thread; + +use cargo_metadata::diagnostic::Diagnostic; +use crossbeam_channel::{Receiver, Sender}; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)] +pub(crate) struct DriverInfo { + pub package_name: String, + pub crate_name: String, + pub version: String, +} + +pub(crate) fn serialize_line(value: &T, writer: &mut W) +where + T: Serialize, + W: Write, +{ + let mut buf = serde_json::to_vec(&value).expect("failed to serialize"); + buf.push(b'\n'); + writer.write_all(&buf).expect("write_all failed"); +} + +pub(crate) fn deserialize_line(reader: &mut R) -> T +where + T: DeserializeOwned, + R: BufRead, +{ + let mut string = String::new(); + reader.read_line(&mut string).expect("read_line failed"); + serde_json::from_str(&string).expect("failed to deserialize") +} + +fn process_stream( + stream: TcpStream, + sender: &Sender, + options: &RecursiveOptions, + seen: &Mutex>, +) { + let mut stream = BufReader::new(stream); + + let driver_info: DriverInfo = deserialize_line(&mut stream); + + let unseen = seen.lock().unwrap().insert(driver_info.clone()); + let ignored = options.ignore.contains(&driver_info.package_name); + let should_run = unseen && !ignored; + + serialize_line(&should_run, stream.get_mut()); + + let mut stderr = String::new(); + stream.read_to_string(&mut stderr).unwrap(); + + let messages = stderr + .lines() + .filter_map(|json_msg| serde_json::from_str::(json_msg).ok()) + .filter_map(|diag| ClippyWarning::new(diag, &driver_info.package_name, &driver_info.version)); + + for message in messages { + sender.send(message).unwrap(); + } +} + +pub(crate) struct LintcheckServer { + pub local_addr: SocketAddr, + receiver: Receiver, + sender: Arc>, +} + +impl LintcheckServer { + pub fn spawn(options: RecursiveOptions) -> Self { + let listener = TcpListener::bind("localhost:0").unwrap(); + let local_addr = listener.local_addr().unwrap(); + + let (sender, receiver) = crossbeam_channel::unbounded::(); + let sender = Arc::new(sender); + // The spawned threads hold a `Weak` so that they don't keep the channel connected + // indefinitely + let sender_weak = Arc::downgrade(&sender); + + // Ignore dependencies multiple times, e.g. for when it's both checked and compiled for a + // build dependency + let seen = Mutex::default(); + + thread::spawn(move || { + thread::scope(|s| { + s.spawn(|| { + while let Ok((stream, _)) = listener.accept() { + let sender = sender_weak.upgrade().expect("received connection after server closed"); + let options = &options; + let seen = &seen; + s.spawn(move || process_stream(stream, &sender, options, seen)); + } + }); + }); + }); + + Self { + local_addr, + sender, + receiver, + } + } + + pub fn warnings(self) -> impl Iterator { + // causes the channel to become disconnected so that the receiver iterator ends + drop(self.sender); + + self.receiver.into_iter() + } +} diff --git a/rust-toolchain b/rust-toolchain index b6976366dafc..49b13cb54e71 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-09-08" +channel = "nightly-2022-10-06" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/rustc_tools_util/Cargo.toml b/rustc_tools_util/Cargo.toml index 9554d4d6c003..89c3d6aaa89e 100644 --- a/rustc_tools_util/Cargo.toml +++ b/rustc_tools_util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustc_tools_util" -version = "0.2.0" +version = "0.2.1" description = "small helper to generate version information for git packages" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index 01891b51d3b0..e947f9c7e66e 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -6,17 +6,17 @@ for packages installed from a git repo ## Usage Add a `build.rs` file to your repo and list it in `Cargo.toml` -```` +````toml build = "build.rs" ```` List rustc_tools_util as regular AND build dependency. -```` +````toml [dependencies] -rustc_tools_util = "0.1" +rustc_tools_util = "0.2.1" [build-dependencies] -rustc_tools_util = "0.1" +rustc_tools_util = "0.2.1" ```` In `build.rs`, generate the data in your `main()` diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs index 429dddc42ea9..01d25c53126f 100644 --- a/rustc_tools_util/src/lib.rs +++ b/rustc_tools_util/src/lib.rs @@ -48,8 +48,8 @@ impl std::fmt::Display for VersionInfo { if (hash_trimmed.len() + date_trimmed.len()) > 0 { write!( f, - "{} {}.{}.{} ({} {})", - self.crate_name, self.major, self.minor, self.patch, hash_trimmed, date_trimmed, + "{} {}.{}.{} ({hash_trimmed} {date_trimmed})", + self.crate_name, self.major, self.minor, self.patch, )?; } else { write!(f, "{} {}.{}.{}", self.crate_name, self.major, self.minor, self.patch)?; @@ -137,7 +137,7 @@ mod test { let vi = get_version_info!(); assert_eq!(vi.major, 0); assert_eq!(vi.minor, 2); - assert_eq!(vi.patch, 0); + assert_eq!(vi.patch, 1); assert_eq!(vi.crate_name, "rustc_tools_util"); // hard to make positive tests for these since they will always change assert!(vi.commit_hash.is_none()); @@ -147,16 +147,16 @@ mod test { #[test] fn test_display_local() { let vi = get_version_info!(); - assert_eq!(vi.to_string(), "rustc_tools_util 0.2.0"); + assert_eq!(vi.to_string(), "rustc_tools_util 0.2.1"); } #[test] fn test_debug_local() { let vi = get_version_info!(); - let s = format!("{:?}", vi); + let s = format!("{vi:?}"); assert_eq!( s, - "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 0 }" + "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 1 }" ); } } diff --git a/src/docs.rs b/src/docs.rs index 6c89b4dde372..3bf488ab4779 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -48,6 +48,7 @@ docs! { "borrow_interior_mutable_const", "borrowed_box", "box_collection", + "box_default", "boxed_local", "branches_sharing_code", "builtin_type_shadow", @@ -105,6 +106,7 @@ docs! { "derive_hash_xor_eq", "derive_ord_xor_partial_ord", "derive_partial_eq_without_eq", + "disallowed_macros", "disallowed_methods", "disallowed_names", "disallowed_script_idents", @@ -190,6 +192,7 @@ docs! { "implicit_clone", "implicit_hasher", "implicit_return", + "implicit_saturating_add", "implicit_saturating_sub", "imprecise_flops", "inconsistent_digit_grouping", @@ -254,6 +257,7 @@ docs! { "manual_assert", "manual_async_fn", "manual_bits", + "manual_clamp", "manual_filter_map", "manual_find", "manual_find_map", @@ -521,6 +525,7 @@ docs! { "unimplemented", "uninit_assumed_init", "uninit_vec", + "uninlined_format_args", "unit_arg", "unit_cmp", "unit_hash", diff --git a/src/docs/arithmetic_side_effects.txt b/src/docs/arithmetic_side_effects.txt index 6c7d51a4989e..4ae8bce88ad5 100644 --- a/src/docs/arithmetic_side_effects.txt +++ b/src/docs/arithmetic_side_effects.txt @@ -5,7 +5,7 @@ Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing accordin Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), or can panic (`/`, `%`). -Known safe built-in types like `Wrapping` or `Saturing`, floats, operations in constant +Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant environments, allowed types and non-constant operations that won't overflow are ignored. ### Why is this bad? diff --git a/src/docs/box_default.txt b/src/docs/box_default.txt new file mode 100644 index 000000000000..ffac894d0c50 --- /dev/null +++ b/src/docs/box_default.txt @@ -0,0 +1,23 @@ +### What it does +checks for `Box::new(T::default())`, which is better written as +`Box::::default()`. + +### Why is this bad? +First, it's more complex, involving two calls instead of one. +Second, `Box::default()` can be faster +[in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). + +### Known problems +The lint may miss some cases (e.g. Box::new(String::from(""))). +On the other hand, it will trigger on cases where the `default` +code comes from a macro that does something different based on +e.g. target operating system. + +### Example +``` +let x: Box = Box::new(Default::default()); +``` +Use instead: +``` +let x: Box = Box::default(); +``` \ No newline at end of file diff --git a/src/docs/disallowed_macros.txt b/src/docs/disallowed_macros.txt new file mode 100644 index 000000000000..96fa15afabfd --- /dev/null +++ b/src/docs/disallowed_macros.txt @@ -0,0 +1,36 @@ +### What it does +Denies the configured macros in clippy.toml + +Note: Even though this lint is warn-by-default, it will only trigger if +macros are defined in the clippy.toml file. + +### Why is this bad? +Some macros are undesirable in certain contexts, and it's beneficial to +lint for them as needed. + +### Example +An example clippy.toml configuration: +``` +disallowed-macros = [ + # Can use a string as the path of the disallowed macro. + "std::print", + # Can also use an inline table with a `path` key. + { path = "std::println" }, + # When using an inline table, can add a `reason` for why the macro + # is disallowed. + { path = "serde::Serialize", reason = "no serializing" }, +] +``` +``` +use serde::Serialize; + +// Example code where clippy issues a warning +println!("warns"); + +// The diagnostic will contain the message "no serializing" +#[derive(Serialize)] +struct Data { + name: String, + value: usize, +} +``` \ No newline at end of file diff --git a/src/docs/implicit_saturating_add.txt b/src/docs/implicit_saturating_add.txt new file mode 100644 index 000000000000..5883a5363e2b --- /dev/null +++ b/src/docs/implicit_saturating_add.txt @@ -0,0 +1,20 @@ +### What it does +Checks for implicit saturating addition. + +### Why is this bad? +The built-in function is more readable and may be faster. + +### Example +``` +let mut u:u32 = 7000; + +if u != u32::MAX { + u += 1; +} +``` +Use instead: +``` +let mut u:u32 = 7000; + +u = u.saturating_add(1); +``` \ No newline at end of file diff --git a/src/docs/manual_clamp.txt b/src/docs/manual_clamp.txt new file mode 100644 index 000000000000..8993f6683adf --- /dev/null +++ b/src/docs/manual_clamp.txt @@ -0,0 +1,46 @@ +### What it does +Identifies good opportunities for a clamp function from std or core, and suggests using it. + +### Why is this bad? +clamp is much shorter, easier to read, and doesn't use any control flow. + +### Known issue(s) +If the clamped variable is NaN this suggestion will cause the code to propagate NaN +rather than returning either `max` or `min`. + +`clamp` functions will panic if `max < min`, `max.is_nan()`, or `min.is_nan()`. +Some may consider panicking in these situations to be desirable, but it also may +introduce panicking where there wasn't any before. + +### Examples +``` +if input > max { + max +} else if input < min { + min +} else { + input +} +``` + +``` +input.max(min).min(max) +``` + +``` +match input { + x if x > max => max, + x if x < min => min, + x => x, +} +``` + +``` +let mut x = input; +if x < min { x = min; } +if x > max { x = max; } +``` +Use instead: +``` +input.clamp(min, max) +``` \ No newline at end of file diff --git a/src/docs/needless_borrowed_reference.txt b/src/docs/needless_borrowed_reference.txt index 55faa0cf5719..152459ba1c9d 100644 --- a/src/docs/needless_borrowed_reference.txt +++ b/src/docs/needless_borrowed_reference.txt @@ -1,30 +1,22 @@ ### What it does -Checks for bindings that destructure a reference and borrow the inner +Checks for bindings that needlessly destructure a reference and borrow the inner value with `&ref`. ### Why is this bad? This pattern has no effect in almost all cases. -### Known problems -In some cases, `&ref` is needed to avoid a lifetime mismatch error. -Example: -``` -fn foo(a: &Option, b: &Option) { - match (a, b) { - (None, &ref c) | (&ref c, None) => (), - (&Some(ref c), _) => (), - }; -} -``` - ### Example ``` let mut v = Vec::::new(); v.iter_mut().filter(|&ref a| a.is_empty()); + +if let &[ref first, ref second] = v.as_slice() {} ``` Use instead: ``` let mut v = Vec::::new(); v.iter_mut().filter(|a| a.is_empty()); + +if let [first, second] = v.as_slice() {} ``` \ No newline at end of file diff --git a/src/docs/similar_names.txt b/src/docs/similar_names.txt index 13aca9c0bb75..f9eff21b6793 100644 --- a/src/docs/similar_names.txt +++ b/src/docs/similar_names.txt @@ -1,6 +1,10 @@ ### What it does Checks for names that are very similar and thus confusing. +Note: this lint looks for similar names throughout each +scope. To allow it, you need to allow it on the scope +level, not on the name that is reported. + ### Why is this bad? It's hard to distinguish between names that differ only by a single character. diff --git a/src/docs/uninlined_format_args.txt b/src/docs/uninlined_format_args.txt new file mode 100644 index 000000000000..3d2966c84dbe --- /dev/null +++ b/src/docs/uninlined_format_args.txt @@ -0,0 +1,36 @@ +### What it does +Detect when a variable is not inlined in a format string, +and suggests to inline it. + +### Why is this bad? +Non-inlined code is slightly more difficult to read and understand, +as it requires arguments to be matched against the format string. +The inlined syntax, where allowed, is simpler. + +### Example +``` +format!("{}", var); +format!("{v:?}", v = var); +format!("{0} {0}", var); +format!("{0:1$}", var, width); +format!("{:.*}", prec, var); +``` +Use instead: +``` +format!("{var}"); +format!("{var:?}"); +format!("{var} {var}"); +format!("{var:width$}"); +format!("{var:.prec$}"); +``` + +### Known Problems + +There may be a false positive if the format string is expanded from certain proc macros: + +``` +println!(indoc!("{}"), var); +``` + +If a format string contains a numbered argument that cannot be inlined +nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. \ No newline at end of file diff --git a/src/driver.rs b/src/driver.rs index 235eae5af1ec..b12208ac62a8 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -193,8 +193,8 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { let xs: Vec> = vec![ "the compiler unexpectedly panicked. this is a bug.".into(), - format!("we would appreciate a bug report: {}", bug_report_url).into(), - format!("Clippy version: {}", version_info).into(), + format!("we would appreciate a bug report: {bug_report_url}").into(), + format!("Clippy version: {version_info}").into(), ]; for note in &xs { @@ -290,7 +290,7 @@ pub fn main() { if orig_args.iter().any(|a| a == "--version" || a == "-V") { let version_info = rustc_tools_util::get_version_info!(); - println!("{}", version_info); + println!("{version_info}"); exit(0); } diff --git a/src/main.rs b/src/main.rs index 4a32e0e54a81..fce3cdfc462e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,12 +37,12 @@ You can use tool lints to allow or deny lints from your code, eg.: "#; fn show_help() { - println!("{}", CARGO_CLIPPY_HELP); + println!("{CARGO_CLIPPY_HELP}"); } fn show_version() { let version_info = rustc_tools_util::get_version_info!(); - println!("{}", version_info); + println!("{version_info}"); } pub fn main() { @@ -133,7 +133,7 @@ impl ClippyCmd { let clippy_args: String = self .clippy_args .iter() - .map(|arg| format!("{}__CLIPPY_HACKERY__", arg)) + .map(|arg| format!("{arg}__CLIPPY_HACKERY__")) .collect(); // Currently, `CLIPPY_TERMINAL_WIDTH` is used only to format "unknown field" error messages. diff --git a/tests/compile-test.rs b/tests/compile-test.rs index ba6186e599e9..fa769222d1af 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -111,15 +111,14 @@ static EXTERN_FLAGS: LazyLock = LazyLock::new(|| { .collect(); assert!( not_found.is_empty(), - "dependencies not found in depinfo: {:?}\n\ + "dependencies not found in depinfo: {not_found:?}\n\ help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\ help: Try adding to dev-dependencies in Cargo.toml\n\ help: Be sure to also add `extern crate ...;` to tests/compile-test.rs", - not_found, ); crates .into_iter() - .map(|(name, path)| format!(" --extern {}={}", name, path)) + .map(|(name, path)| format!(" --extern {name}={path}")) .collect() }); @@ -150,9 +149,8 @@ fn base_config(test_dir: &str) -> compiletest::Config { .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display())) .unwrap_or_default(); config.target_rustcflags = Some(format!( - "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}", + "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{host_libs}{}", deps_path.display(), - host_libs, &*EXTERN_FLAGS, )); @@ -239,7 +237,7 @@ fn run_ui_toml() { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), Err(e) => { - panic!("I/O failure during tests: {:?}", e); + panic!("I/O failure during tests: {e:?}"); }, } } @@ -348,7 +346,7 @@ fn run_ui_cargo() { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), Err(e) => { - panic!("I/O failure during tests: {:?}", e); + panic!("I/O failure during tests: {e:?}"); }, } } @@ -419,16 +417,15 @@ fn check_rustfix_coverage() { if rs_path.starts_with("tests/ui/crashes") { continue; } - assert!(rs_path.starts_with("tests/ui/"), "{:?}", rs_file); + assert!(rs_path.starts_with("tests/ui/"), "{rs_file:?}"); let filename = rs_path.strip_prefix("tests/ui/").unwrap(); assert!( RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS .binary_search_by_key(&filename, Path::new) .is_ok(), - "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \ + "`{rs_file}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \ Please either add `// run-rustfix` at the top of the file or add the file to \ `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.", - rs_file, ); } } @@ -478,15 +475,13 @@ fn ui_cargo_toml_metadata() { .map(|component| component.as_os_str().to_string_lossy().replace('-', "_")) .any(|s| *s == name) || path.starts_with(&cargo_common_metadata_path), - "{:?} has incorrect package name", - path + "{path:?} has incorrect package name" ); let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true); assert!( !publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()), - "{:?} lacks `publish = false`", - path + "{path:?} lacks `publish = false`" ); } } diff --git a/tests/integration.rs b/tests/integration.rs index 23a9bef3ccce..818ff70b33f4 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -6,10 +6,15 @@ use std::env; use std::ffi::OsStr; use std::process::Command; +#[cfg(not(windows))] +const CARGO_CLIPPY: &str = "cargo-clippy"; +#[cfg(windows)] +const CARGO_CLIPPY: &str = "cargo-clippy.exe"; + #[cfg_attr(feature = "integration", test)] fn integration_test() { let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set"); - let repo_url = format!("https://github.com/{}", repo_name); + let repo_url = format!("https://github.com/{repo_name}"); let crate_name = repo_name .split('/') .nth(1) @@ -31,7 +36,7 @@ fn integration_test() { let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let target_dir = std::path::Path::new(&root_dir).join("target"); - let clippy_binary = target_dir.join(env!("PROFILE")).join("cargo-clippy"); + let clippy_binary = target_dir.join(env!("PROFILE")).join(CARGO_CLIPPY); let output = Command::new(clippy_binary) .current_dir(repo_dir) @@ -51,17 +56,15 @@ fn integration_test() { .expect("unable to run clippy"); let stderr = String::from_utf8_lossy(&output.stderr); - if stderr.contains("internal compiler error") { - let backtrace_start = stderr - .find("thread 'rustc' panicked at") - .expect("start of backtrace not found"); - let backtrace_end = stderr - .rfind("error: internal compiler error") + if let Some(backtrace_start) = stderr.find("error: internal compiler error") { + static BACKTRACE_END_MSG: &str = "end of query stack"; + let backtrace_end = stderr[backtrace_start..] + .find(BACKTRACE_END_MSG) .expect("end of backtrace not found"); panic!( "internal compiler error\nBacktrace:\n\n{}", - &stderr[backtrace_start..backtrace_end] + &stderr[backtrace_start..backtrace_start + backtrace_end + BACKTRACE_END_MSG.len()] ); } else if stderr.contains("query stack during panic") { panic!("query stack during panic in the output"); @@ -83,7 +86,7 @@ fn integration_test() { match output.status.code() { Some(0) => println!("Compilation successful"), - Some(code) => eprintln!("Compilation failed. Exit code: {}", code), + Some(code) => eprintln!("Compilation failed. Exit code: {code}"), None => panic!("Process terminated by signal"), } } diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 2e0f4e76075b..abd0d1bc5934 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -102,7 +102,7 @@ fn lint_message_convention() { "error: the test '{}' contained the following nonconforming lines :", message.path.display() ); - message.bad_lines.iter().for_each(|line| eprintln!("{}", line)); + message.bad_lines.iter().for_each(|line| eprintln!("{line}")); eprintln!("\n\n"); } diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 7d6edc2b1e09..caedd5d76cd6 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -17,7 +17,7 @@ fn test_missing_tests() { "Didn't see a test file for the following files:\n\n{}\n", missing_files .iter() - .map(|s| format!("\t{}", s)) + .map(|s| format!("\t{s}")) .collect::>() .join("\n") ); diff --git a/tests/ui-cargo/duplicate_mod/fail/src/main.stderr b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr index b450a2b18f25..3b80d89a6865 100644 --- a/tests/ui-cargo/duplicate_mod/fail/src/main.stderr +++ b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr @@ -7,8 +7,8 @@ LL | / #[path = "b.rs"] LL | | mod b2; | |_______^ loaded again here | - = note: `-D clippy::duplicate-mod` implied by `-D warnings` = help: replace all but one `mod` item with `use` items + = note: `-D clippy::duplicate-mod` implied by `-D warnings` error: file is loaded as a module multiple times: `$DIR/c.rs` --> $DIR/main.rs:9:1 diff --git a/tests/ui-cargo/feature_name/fail/src/main.stderr b/tests/ui-cargo/feature_name/fail/src/main.stderr index b9e6cb49bc98..c6a11fa93eb5 100644 --- a/tests/ui-cargo/feature_name/fail/src/main.stderr +++ b/tests/ui-cargo/feature_name/fail/src/main.stderr @@ -1,7 +1,7 @@ error: the "no-" prefix in the feature name "no-qaq" is negative | - = note: `-D clippy::negative-feature-names` implied by `-D warnings` = help: consider renaming the feature to "qaq", but make sure the feature adds functionality + = note: `-D clippy::negative-feature-names` implied by `-D warnings` error: the "no_" prefix in the feature name "no_qaq" is negative | @@ -17,8 +17,8 @@ error: the "not_" prefix in the feature name "not_orz" is negative error: the "-support" suffix in the feature name "qvq-support" is redundant | - = note: `-D clippy::redundant-feature-names` implied by `-D warnings` = help: consider renaming the feature to "qvq" + = note: `-D clippy::redundant-feature-names` implied by `-D warnings` error: the "_support" suffix in the feature name "qvq_support" is redundant | 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 e2010e998131..697c8b57c4a2 100644 --- a/tests/ui-cargo/module_style/fail_mod/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_mod/src/main.stderr @@ -4,8 +4,8 @@ error: `mod.rs` files are required, found `bad/inner.rs` 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` + = note: `-D clippy::self-named-module-files` implied by `-D warnings` error: `mod.rs` files are required, found `bad/inner/stuff.rs` --> $DIR/bad/inner/stuff.rs:1:1 diff --git a/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr b/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr index 46991ff662e5..ea6ea98064a7 100644 --- a/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr @@ -4,8 +4,8 @@ error: `mod.rs` files are required, found `bad.rs` LL | pub mod inner; | ^ | - = note: `-D clippy::self-named-module-files` implied by `-D warnings` = help: move `bad.rs` to `bad/mod.rs` + = note: `-D clippy::self-named-module-files` implied by `-D warnings` error: aborting due to previous error 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 f91940209383..f40ceea234b9 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 @@ -4,8 +4,8 @@ error: `mod.rs` files are not allowed, found `bad/mod.rs` LL | pub struct Thing; | ^ | - = note: `-D clippy::mod-module-files` implied by `-D warnings` = help: move `bad/mod.rs` to `bad.rs` + = note: `-D clippy::mod-module-files` implied by `-D warnings` error: aborting due to previous error diff --git a/tests/ui-internal/auxiliary/paths.rs b/tests/ui-internal/auxiliary/paths.rs new file mode 100644 index 000000000000..52fcaec4df32 --- /dev/null +++ b/tests/ui-internal/auxiliary/paths.rs @@ -0,0 +1,2 @@ +pub static OPTION: [&str; 3] = ["core", "option", "Option"]; +pub const RESULT: &[&str] = &["core", "result", "Result"]; diff --git a/tests/ui-internal/check_clippy_version_attribute.stderr b/tests/ui-internal/check_clippy_version_attribute.stderr index 2aa4de490bcf..fd8c8379f5bf 100644 --- a/tests/ui-internal/check_clippy_version_attribute.stderr +++ b/tests/ui-internal/check_clippy_version_attribute.stderr @@ -10,13 +10,13 @@ LL | | report_in_external_macro: true LL | | } | |_^ | + = help: please use a valid semantic version, see `doc/adding_lints.md` note: the lint level is defined here --> $DIR/check_clippy_version_attribute.rs:1:9 | LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` - = help: please use a valid semantic version, see `doc/adding_lints.md` = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this item has an invalid `clippy::version` attribute @@ -46,8 +46,8 @@ LL | | report_in_external_macro: true LL | | } | |_^ | - = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` = help: please use a `clippy::version` attribute, see `doc/adding_lints.md` + = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value diff --git a/tests/ui-internal/if_chain_style.stderr b/tests/ui-internal/if_chain_style.stderr index 24106510e73b..d8f1ffb21ba6 100644 --- a/tests/ui-internal/if_chain_style.stderr +++ b/tests/ui-internal/if_chain_style.stderr @@ -10,12 +10,12 @@ LL | | } LL | | } | |_____^ | - = note: `-D clippy::if-chain-style` implied by `-D warnings` help: this `let` statement can also be in the `if_chain!` --> $DIR/if_chain_style.rs:10:9 | LL | let x = ""; | ^^^^^^^^^^^ + = note: `-D clippy::if-chain-style` implied by `-D warnings` error: `if a && b;` should be `if a; if b;` --> $DIR/if_chain_style.rs:19:12 diff --git a/tests/ui-internal/match_type_on_diag_item.rs b/tests/ui-internal/match_type_on_diag_item.rs deleted file mode 100644 index 4b41ff15e80f..000000000000 --- a/tests/ui-internal/match_type_on_diag_item.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] -#![feature(rustc_private)] - -extern crate clippy_utils; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; - -#[macro_use] -extern crate rustc_session; -use clippy_utils::{paths, ty::match_type}; -use rustc_hir::Expr; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; - -declare_lint! { - pub TEST_LINT, - Warn, - "" -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -static OPTION: [&str; 3] = ["core", "option", "Option"]; - -impl<'tcx> LateLintPass<'tcx> for Pass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) { - let ty = cx.typeck_results().expr_ty(expr); - - let _ = match_type(cx, ty, &OPTION); - let _ = match_type(cx, ty, &["core", "result", "Result"]); - - let rc_path = &["alloc", "rc", "Rc"]; - let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - } -} - -fn main() {} diff --git a/tests/ui-internal/match_type_on_diag_item.stderr b/tests/ui-internal/match_type_on_diag_item.stderr deleted file mode 100644 index e3cb6b6c22ea..000000000000 --- a/tests/ui-internal/match_type_on_diag_item.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:31:17 - | -LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Option)` - | -note: the lint level is defined here - --> $DIR/match_type_on_diag_item.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` - -error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:32:17 - | -LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Result)` - -error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:35:17 - | -LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)` - -error: aborting due to 3 previous errors - diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed new file mode 100644 index 000000000000..4c050332f2cc --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -0,0 +1,62 @@ +// run-rustfix +// aux-build:paths.rs +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate clippy_utils; +extern crate paths; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_span; + +#[allow(unused)] +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; +#[allow(unused)] +use clippy_utils::{ + is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method, + match_def_path, match_trait_method, path_res, +}; + +#[allow(unused)] +use rustc_hir::LangItem; +#[allow(unused)] +use rustc_span::sym; + +use rustc_hir::def_id::DefId; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +#[allow(unused)] +static OPTION: [&str; 3] = ["core", "option", "Option"]; +#[allow(unused)] +const RESULT: &[&str] = &["core", "result", "Result"]; + +fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { + let _ = is_type_diagnostic_item(cx, ty, sym::Option); + let _ = is_type_diagnostic_item(cx, ty, sym::Result); + let _ = is_type_diagnostic_item(cx, ty, sym::Result); + + #[allow(unused)] + let rc_path = &["alloc", "rc", "Rc"]; + let _ = is_type_diagnostic_item(cx, ty, sym::Rc); + + let _ = is_type_diagnostic_item(cx, ty, sym::Option); + let _ = is_type_diagnostic_item(cx, ty, sym::Result); + + let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); + let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); + + let _ = cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did); + let _ = cx.tcx.is_diagnostic_item(sym::Option, did); + let _ = cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did); + + let _ = is_trait_method(cx, expr, sym::AsRef); + + let _ = is_path_diagnostic_item(cx, expr, sym::Option); + let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id)); + let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); +} + +fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs new file mode 100644 index 000000000000..6506f1f164ac --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -0,0 +1,62 @@ +// run-rustfix +// aux-build:paths.rs +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate clippy_utils; +extern crate paths; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_span; + +#[allow(unused)] +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; +#[allow(unused)] +use clippy_utils::{ + is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method, + match_def_path, match_trait_method, path_res, +}; + +#[allow(unused)] +use rustc_hir::LangItem; +#[allow(unused)] +use rustc_span::sym; + +use rustc_hir::def_id::DefId; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +#[allow(unused)] +static OPTION: [&str; 3] = ["core", "option", "Option"]; +#[allow(unused)] +const RESULT: &[&str] = &["core", "result", "Result"]; + +fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { + let _ = match_type(cx, ty, &OPTION); + let _ = match_type(cx, ty, RESULT); + let _ = match_type(cx, ty, &["core", "result", "Result"]); + + #[allow(unused)] + let rc_path = &["alloc", "rc", "Rc"]; + let _ = clippy_utils::ty::match_type(cx, ty, rc_path); + + let _ = match_type(cx, ty, &paths::OPTION); + let _ = match_type(cx, ty, paths::RESULT); + + let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); + let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); + + let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); + let _ = match_def_path(cx, did, &["core", "option", "Option"]); + let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); + + let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); + + let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); + let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); + let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); +} + +fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr new file mode 100644 index 000000000000..a99a8f71fa6a --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path.stderr @@ -0,0 +1,101 @@ +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:37:13 + | +LL | let _ = match_type(cx, ty, &OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` + | +note: the lint level is defined here + --> $DIR/unnecessary_def_path.rs:3:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:38:13 + | +LL | let _ = match_type(cx, ty, RESULT); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:39:13 + | +LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:43:13 + | +LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:45:13 + | +LL | let _ = match_type(cx, ty, &paths::OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:46:13 + | +LL | let _ = match_type(cx, ty, paths::RESULT); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:48:13 + | +LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:49:13 + | +LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:51:13 + | +LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:52:13 + | +LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:53:13 + | +LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did)` + | + = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:55:13 + | +LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:57:13 + | +LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:58:13 + | +LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id))` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:59:13 + | +LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)` + +error: aborting due to 15 previous errors + diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs index b4e677ea124b..7f1c512d7c97 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + fn main() {} #[warn(clippy::cognitive_complexity)] diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index b2b57bdde89c..630bad07c5b7 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -3,7 +3,7 @@ warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecate warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `blacklisted-names`. Please use `disallowed-names` instead error: the function has a cognitive complexity of (3/2) - --> $DIR/conf_deprecated_key.rs:4:4 + --> $DIR/conf_deprecated_key.rs:6:4 | LL | fn cognitive_complexity() { | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-toml/disallowed_macros/auxiliary/macros.rs b/tests/ui-toml/disallowed_macros/auxiliary/macros.rs new file mode 100644 index 000000000000..fcaeace0e989 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/auxiliary/macros.rs @@ -0,0 +1,32 @@ +#[macro_export] +macro_rules! expr { + () => { + 1 + }; +} + +#[macro_export] +macro_rules! stmt { + () => { + let _x = (); + }; +} + +#[macro_export] +macro_rules! ty { + () => { &'static str }; +} + +#[macro_export] +macro_rules! pat { + () => { + _ + }; +} + +#[macro_export] +macro_rules! item { + () => { + const ITEM: usize = 1; + }; +} diff --git a/tests/ui-toml/disallowed_macros/clippy.toml b/tests/ui-toml/disallowed_macros/clippy.toml new file mode 100644 index 000000000000..c8fe8be9a770 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/clippy.toml @@ -0,0 +1,11 @@ +disallowed-macros = [ + "std::println", + "std::vec", + { path = "std::cfg" }, + { path = "serde::Serialize", reason = "no serializing" }, + "macros::expr", + "macros::stmt", + "macros::ty", + "macros::pat", + "macros::item", +] diff --git a/tests/ui-toml/disallowed_macros/disallowed_macros.rs b/tests/ui-toml/disallowed_macros/disallowed_macros.rs new file mode 100644 index 000000000000..2bb5376076e2 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/disallowed_macros.rs @@ -0,0 +1,39 @@ +// aux-build:macros.rs + +#![allow(unused)] + +extern crate macros; + +use serde::Serialize; + +fn main() { + println!("one"); + println!("two"); + cfg!(unix); + vec![1, 2, 3]; + + #[derive(Serialize)] + struct Derive; + + let _ = macros::expr!(); + macros::stmt!(); + let macros::pat!() = 1; + let _: macros::ty!() = ""; + macros::item!(); + + eprintln!("allowed"); +} + +struct S; + +impl S { + macros::item!(); +} + +trait Y { + macros::item!(); +} + +impl Y for S { + macros::item!(); +} diff --git a/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr new file mode 100644 index 000000000000..aed9feb6f796 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr @@ -0,0 +1,84 @@ +error: use of a disallowed macro `std::println` + --> $DIR/disallowed_macros.rs:10:5 + | +LL | println!("one"); + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-macros` implied by `-D warnings` + +error: use of a disallowed macro `std::println` + --> $DIR/disallowed_macros.rs:11:5 + | +LL | println!("two"); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `std::cfg` + --> $DIR/disallowed_macros.rs:12:5 + | +LL | cfg!(unix); + | ^^^^^^^^^^ + +error: use of a disallowed macro `std::vec` + --> $DIR/disallowed_macros.rs:13:5 + | +LL | vec![1, 2, 3]; + | ^^^^^^^^^^^^^ + +error: use of a disallowed macro `serde::Serialize` + --> $DIR/disallowed_macros.rs:15:14 + | +LL | #[derive(Serialize)] + | ^^^^^^^^^ + | + = note: no serializing (from clippy.toml) + +error: use of a disallowed macro `macros::expr` + --> $DIR/disallowed_macros.rs:18:13 + | +LL | let _ = macros::expr!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::stmt` + --> $DIR/disallowed_macros.rs:19:5 + | +LL | macros::stmt!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::pat` + --> $DIR/disallowed_macros.rs:20:9 + | +LL | let macros::pat!() = 1; + | ^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::ty` + --> $DIR/disallowed_macros.rs:21:12 + | +LL | let _: macros::ty!() = ""; + | ^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:22:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:30:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:34:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:38:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors + diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed new file mode 100644 index 000000000000..01d135764dff --- /dev/null +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed @@ -0,0 +1,62 @@ +// aux-build:proc_macro_derive.rs +// run-rustfix + +#![warn(clippy::nonstandard_macro_braces)] + +extern crate proc_macro_derive; +extern crate quote; + +use quote::quote; + +#[derive(proc_macro_derive::DeriveSomething)] +pub struct S; + +proc_macro_derive::foo_bar!(); + +#[rustfmt::skip] +macro_rules! test { + () => { + vec![0, 0, 0] + }; +} + +#[rustfmt::skip] +macro_rules! test2 { + ($($arg:tt)*) => { + format_args!($($arg)*) + }; +} + +macro_rules! type_pos { + ($what:ty) => { + Vec<$what> + }; +} + +macro_rules! printlnfoo { + ($thing:expr) => { + println!("{}", $thing) + }; +} + +#[rustfmt::skip] +fn main() { + let _ = vec![1, 2, 3]; + let _ = format!("ugh {} stop being such a good compiler", "hello"); + let _ = matches!({}, ()); + let _ = quote!{let x = 1;}; + let _ = quote::quote!{match match match}; + let _ = test!(); // trigger when macro def is inside our own crate + let _ = vec![1,2,3]; + + let _ = quote::quote! {true || false}; + let _ = vec! [0 ,0 ,0]; + let _ = format!("fds{}fds", 10); + let _ = test2!["{}{}{}", 1, 2, 3]; + + let _: type_pos![usize] = vec![]; + + eprint!["test if user config overrides defaults"]; + + printlnfoo!["test if printlnfoo is triggered by println"]; +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs index ed8161acc0ef..72883e8270c3 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -1,4 +1,5 @@ // aux-build:proc_macro_derive.rs +// run-rustfix #![warn(clippy::nonstandard_macro_braces)] diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index 15fa4f42f9ba..7ae3815978c7 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -1,106 +1,57 @@ error: use of irregular braces for `vec!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:43:13 + --> $DIR/conf_nonstandard_macro_braces.rs:44:13 | LL | let _ = vec! {1, 2, 3}; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: consider writing: `vec![1, 2, 3]` | -help: consider writing `vec![1, 2, 3]` - --> $DIR/conf_nonstandard_macro_braces.rs:43:13 - | -LL | let _ = vec! {1, 2, 3}; - | ^^^^^^^^^^^^^^ = note: `-D clippy::nonstandard-macro-braces` implied by `-D warnings` error: use of irregular braces for `format!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:44:13 + --> $DIR/conf_nonstandard_macro_braces.rs:45:13 | LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider writing `format!("ugh () stop being such a good compiler", "hello")` - --> $DIR/conf_nonstandard_macro_braces.rs:44:13 - | -LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `format!("ugh {} stop being such a good compiler", "hello")` error: use of irregular braces for `matches!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:45:13 + --> $DIR/conf_nonstandard_macro_braces.rs:46:13 | LL | let _ = matches!{{}, ()}; - | ^^^^^^^^^^^^^^^^ - | -help: consider writing `matches!((), ())` - --> $DIR/conf_nonstandard_macro_braces.rs:45:13 - | -LL | let _ = matches!{{}, ()}; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: consider writing: `matches!({}, ())` error: use of irregular braces for `quote!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:46:13 + --> $DIR/conf_nonstandard_macro_braces.rs:47:13 | LL | let _ = quote!(let x = 1;); - | ^^^^^^^^^^^^^^^^^^ - | -help: consider writing `quote! {let x = 1;}` - --> $DIR/conf_nonstandard_macro_braces.rs:46:13 - | -LL | let _ = quote!(let x = 1;); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: consider writing: `quote!{let x = 1;}` error: use of irregular braces for `quote::quote!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:47:13 + --> $DIR/conf_nonstandard_macro_braces.rs:48:13 | LL | let _ = quote::quote!(match match match); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider writing `quote::quote! {match match match}` - --> $DIR/conf_nonstandard_macro_braces.rs:47:13 - | -LL | let _ = quote::quote!(match match match); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `quote::quote!{match match match}` error: use of irregular braces for `vec!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:18:9 + --> $DIR/conf_nonstandard_macro_braces.rs:19:9 | LL | vec!{0, 0, 0} - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: consider writing: `vec![0, 0, 0]` ... LL | let _ = test!(); // trigger when macro def is inside our own crate | ------- in this macro invocation | -help: consider writing `vec![0, 0, 0]` - --> $DIR/conf_nonstandard_macro_braces.rs:18:9 - | -LL | vec!{0, 0, 0} - | ^^^^^^^^^^^^^ -... -LL | let _ = test!(); // trigger when macro def is inside our own crate - | ------- in this macro invocation = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: use of irregular braces for `type_pos!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:56:12 + --> $DIR/conf_nonstandard_macro_braces.rs:57:12 | LL | let _: type_pos!(usize) = vec![]; - | ^^^^^^^^^^^^^^^^ - | -help: consider writing `type_pos![usize]` - --> $DIR/conf_nonstandard_macro_braces.rs:56:12 - | -LL | let _: type_pos!(usize) = vec![]; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: consider writing: `type_pos![usize]` error: use of irregular braces for `eprint!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:58:5 + --> $DIR/conf_nonstandard_macro_braces.rs:59:5 | LL | eprint!("test if user config overrides defaults"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider writing `eprint!["test if user config overrides defaults"]` - --> $DIR/conf_nonstandard_macro_braces.rs:58:5 - | -LL | eprint!("test if user config overrides defaults"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `eprint!["test if user config overrides defaults"]` error: aborting due to 8 previous errors diff --git a/tests/ui-toml/toml_disallowed_methods/clippy.toml b/tests/ui-toml/toml_disallowed_methods/clippy.toml index c902d21123dc..28774db625bb 100644 --- a/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -3,6 +3,7 @@ disallowed-methods = [ "std::iter::Iterator::sum", "f32::clamp", "slice::sort_unstable", + "futures::stream::select_all", # can give path and reason with an inline table { path = "regex::Regex::is_match", reason = "no matching allowed" }, # can use an inline table but omit reason 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 3397fa1ec692..b483f1600289 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -1,6 +1,9 @@ #![warn(clippy::disallowed_methods)] +extern crate futures; extern crate regex; + +use futures::stream::{empty, select_all}; use regex::Regex; fn main() { @@ -20,4 +23,7 @@ fn main() { let in_call = Box::new(f32::clamp); let in_method_call = ["^", "$"].into_iter().map(Regex::new); + + // resolve ambiguity between `futures::stream::select_all` the module and the function + let same_name_as_module = select_all(vec![empty::<()>()]); } 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 5cbb567546c0..6d78c32e127e 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:7:14 + --> $DIR/conf_disallowed_methods.rs:10:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); | ^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap(); = note: `-D clippy::disallowed-methods` implied by `-D warnings` error: use of a disallowed method `regex::Regex::is_match` - --> $DIR/conf_disallowed_methods.rs:8:5 + --> $DIR/conf_disallowed_methods.rs:11:5 | LL | re.is_match("abc"); | ^^^^^^^^^^^^^^^^^^ @@ -15,40 +15,46 @@ LL | re.is_match("abc"); = note: no matching allowed (from clippy.toml) error: use of a disallowed method `std::iter::Iterator::sum` - --> $DIR/conf_disallowed_methods.rs:11:5 + --> $DIR/conf_disallowed_methods.rs:14:5 | LL | a.iter().sum::(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `slice::sort_unstable` - --> $DIR/conf_disallowed_methods.rs:13:5 + --> $DIR/conf_disallowed_methods.rs:16:5 | LL | a.sort_unstable(); | ^^^^^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:15:13 + --> $DIR/conf_disallowed_methods.rs:18:13 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:18:61 + --> $DIR/conf_disallowed_methods.rs:21:61 | LL | let indirect: fn(&str) -> Result = Regex::new; | ^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:21:28 + --> $DIR/conf_disallowed_methods.rs:24:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:22:53 + --> $DIR/conf_disallowed_methods.rs:25:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ -error: aborting due to 8 previous errors +error: use of a disallowed method `futures::stream::select_all` + --> $DIR/conf_disallowed_methods.rs:28:31 + | +LL | let same_name_as_module = select_all(vec![empty::<()>()]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index f27f78d15d3a..82ee80541321 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -11,6 +11,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie cargo-ignore-publish cognitive-complexity-threshold cyclomatic-complexity-threshold + disallowed-macros disallowed-methods disallowed-names disallowed-types diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index dd24f5aa592b..b25e68f13061 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -2,15 +2,41 @@ clippy::assign_op_pattern, clippy::erasing_op, clippy::identity_op, + clippy::op_ref, clippy::unnecessary_owned_empty_strings, arithmetic_overflow, unconditional_panic )] -#![feature(inline_const, saturating_int_impl)] +#![feature(const_mut_refs, inline_const, saturating_int_impl)] #![warn(clippy::arithmetic_side_effects)] use core::num::{Saturating, Wrapping}; +pub struct Custom; + +macro_rules! impl_arith { + ( $( $_trait:ident, $ty:ty, $method:ident; )* ) => { + $( + impl core::ops::$_trait<$ty> for Custom { + type Output = Self; + fn $method(self, _: $ty) -> Self::Output { Self } + } + )* + } +} + +impl_arith!( + Add, i32, add; + Div, i32, div; + Mul, i32, mul; + Sub, i32, sub; + + Add, f64, add; + Div, f64, div; + Mul, f64, mul; + Sub, f64, sub; +); + pub fn association_with_structures_should_not_trigger_the_lint() { enum Foo { Bar = -2, @@ -79,32 +105,48 @@ pub fn const_ops_should_not_trigger_the_lint() { const _: i32 = 1 + 1; let _ = const { 1 + 1 }; - const _: i32 = { let mut n = -1; n = -(-1); n = -n; n }; - let _ = const { let mut n = -1; n = -(-1); n = -n; n }; + const _: i32 = { let mut n = 1; n = -1; n = -(-1); n = -n; n }; + let _ = const { let mut n = 1; n = -1; n = -(-1); n = -n; n }; } -pub fn non_overflowing_runtime_ops_or_ops_already_handled_by_the_compiler() { +pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_trigger_the_lint() { let mut _n = i32::MAX; // Assign _n += 0; + _n += &0; _n -= 0; + _n -= &0; _n /= 99; + _n /= &99; _n %= 99; + _n %= &99; _n *= 0; + _n *= &0; _n *= 1; + _n *= &1; // Binary _n = _n + 0; + _n = _n + &0; _n = 0 + _n; + _n = &0 + _n; _n = _n - 0; + _n = _n - &0; _n = 0 - _n; + _n = &0 - _n; _n = _n / 99; + _n = _n / &99; _n = _n % 99; + _n = _n % &99; _n = _n * 0; + _n = _n * &0; _n = 0 * _n; + _n = &0 * _n; _n = _n * 1; + _n = _n * &1; _n = 1 * _n; + _n = &1 * _n; _n = 23 + 85; // Unary @@ -112,28 +154,71 @@ pub fn non_overflowing_runtime_ops_or_ops_already_handled_by_the_compiler() { _n = -(-1); } -pub fn overflowing_runtime_ops() { +pub fn runtime_ops() { let mut _n = i32::MAX; // Assign _n += 1; + _n += &1; _n -= 1; + _n -= &1; _n /= 0; + _n /= &0; _n %= 0; + _n %= &0; _n *= 2; + _n *= &2; // Binary _n = _n + 1; + _n = _n + &1; _n = 1 + _n; + _n = &1 + _n; _n = _n - 1; + _n = _n - &1; _n = 1 - _n; + _n = &1 - _n; _n = _n / 0; + _n = _n / &0; _n = _n % 0; + _n = _n % &0; _n = _n * 2; + _n = _n * &2; _n = 2 * _n; + _n = &2 * _n; + _n = 23 + &85; + _n = &23 + 85; + _n = &23 + &85; + + // Custom + let _ = Custom + 0; + let _ = Custom + 1; + let _ = Custom + 2; + let _ = Custom + 0.0; + let _ = Custom + 1.0; + let _ = Custom + 2.0; + let _ = Custom - 0; + let _ = Custom - 1; + let _ = Custom - 2; + let _ = Custom - 0.0; + let _ = Custom - 1.0; + let _ = Custom - 2.0; + let _ = Custom / 0; + let _ = Custom / 1; + let _ = Custom / 2; + let _ = Custom / 0.0; + let _ = Custom / 1.0; + let _ = Custom / 2.0; + let _ = Custom * 0; + let _ = Custom * 1; + let _ = Custom * 2; + let _ = Custom * 0.0; + let _ = Custom * 1.0; + let _ = Custom * 2.0; // Unary _n = -_n; + _n = -&_n; } fn main() {} diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index a2a856efbffc..0f06e22bae96 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -1,88 +1,352 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:119:5 + --> $DIR/arithmetic_side_effects.rs:78:13 | -LL | _n += 1; - | ^^^^^^^ +LL | let _ = String::new() + ""; + | ^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:120:5 + --> $DIR/arithmetic_side_effects.rs:86:27 + | +LL | let inferred_string = string + ""; + | ^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:90:13 + | +LL | let _ = inferred_string + ""; + | ^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:161:5 + | +LL | _n += 1; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:162:5 + | +LL | _n += &1; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:163:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:121:5 + --> $DIR/arithmetic_side_effects.rs:164:5 + | +LL | _n -= &1; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:165:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:122:5 + --> $DIR/arithmetic_side_effects.rs:166:5 + | +LL | _n /= &0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:167:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:123:5 + --> $DIR/arithmetic_side_effects.rs:168:5 + | +LL | _n %= &0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:169:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:126:10 + --> $DIR/arithmetic_side_effects.rs:170:5 + | +LL | _n *= &2; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:173:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:127:10 + --> $DIR/arithmetic_side_effects.rs:174:10 + | +LL | _n = _n + &1; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:175:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:128:10 + --> $DIR/arithmetic_side_effects.rs:176:10 + | +LL | _n = &1 + _n; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:177:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:129:10 + --> $DIR/arithmetic_side_effects.rs:178:10 + | +LL | _n = _n - &1; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:179:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:130:10 + --> $DIR/arithmetic_side_effects.rs:180:10 + | +LL | _n = &1 - _n; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:181:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:131:10 + --> $DIR/arithmetic_side_effects.rs:182:10 + | +LL | _n = _n / &0; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:183:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:132:10 + --> $DIR/arithmetic_side_effects.rs:184:10 + | +LL | _n = _n % &0; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:185:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:133:10 + --> $DIR/arithmetic_side_effects.rs:186:10 + | +LL | _n = _n * &2; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:187:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:136:10 + --> $DIR/arithmetic_side_effects.rs:188:10 + | +LL | _n = &2 * _n; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:189:10 + | +LL | _n = 23 + &85; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:190:10 + | +LL | _n = &23 + 85; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:191:10 + | +LL | _n = &23 + &85; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:194:13 + | +LL | let _ = Custom + 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:195:13 + | +LL | let _ = Custom + 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:196:13 + | +LL | let _ = Custom + 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:197:13 + | +LL | let _ = Custom + 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:198:13 + | +LL | let _ = Custom + 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:199:13 + | +LL | let _ = Custom + 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:200:13 + | +LL | let _ = Custom - 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:201:13 + | +LL | let _ = Custom - 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:202:13 + | +LL | let _ = Custom - 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:203:13 + | +LL | let _ = Custom - 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:204:13 + | +LL | let _ = Custom - 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:205:13 + | +LL | let _ = Custom - 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:206:13 + | +LL | let _ = Custom / 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:207:13 + | +LL | let _ = Custom / 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:208:13 + | +LL | let _ = Custom / 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:209:13 + | +LL | let _ = Custom / 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:210:13 + | +LL | let _ = Custom / 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:211:13 + | +LL | let _ = Custom / 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:212:13 + | +LL | let _ = Custom * 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:213:13 + | +LL | let _ = Custom * 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:214:13 + | +LL | let _ = Custom * 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:215:13 + | +LL | let _ = Custom * 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:216:13 + | +LL | let _ = Custom * 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:217:13 + | +LL | let _ = Custom * 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:220:10 | LL | _n = -_n; | ^^^ -error: aborting due to 14 previous errors +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:221:10 + | +LL | _n = -&_n; + | ^^^^ + +error: aborting due to 58 previous errors diff --git a/tests/ui/assign_ops2.rs b/tests/ui/assign_ops2.rs index f6d3a8fa3f0d..2c876a96c55d 100644 --- a/tests/ui/assign_ops2.rs +++ b/tests/ui/assign_ops2.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + #[allow(unused_assignments)] #[warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { diff --git a/tests/ui/assign_ops2.stderr b/tests/ui/assign_ops2.stderr index 04b1dc93d4a4..25e74602244d 100644 --- a/tests/ui/assign_ops2.stderr +++ b/tests/ui/assign_ops2.stderr @@ -1,5 +1,5 @@ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:5:5 + --> $DIR/assign_ops2.rs:7:5 | LL | a += a + 1; | ^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | a = a + a + 1; | ~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:6:5 + --> $DIR/assign_ops2.rs:8:5 | LL | a += 1 + a; | ^^^^^^^^^^ @@ -30,7 +30,7 @@ LL | a = a + 1 + a; | ~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:7:5 + --> $DIR/assign_ops2.rs:9:5 | LL | a -= a - 1; | ^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | a = a - (a - 1); | ~~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:8:5 + --> $DIR/assign_ops2.rs:10:5 | LL | a *= a * 99; | ^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | a = a * a * 99; | ~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:9:5 + --> $DIR/assign_ops2.rs:11:5 | LL | a *= 42 * a; | ^^^^^^^^^^^ @@ -75,7 +75,7 @@ LL | a = a * 42 * a; | ~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:10:5 + --> $DIR/assign_ops2.rs:12:5 | LL | a /= a / 2; | ^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | a = a / (a / 2); | ~~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:11:5 + --> $DIR/assign_ops2.rs:13:5 | LL | a %= a % 5; | ^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | a = a % (a % 5); | ~~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:12:5 + --> $DIR/assign_ops2.rs:14:5 | LL | a &= a & 1; | ^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | a = a & a & 1; | ~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:13:5 + --> $DIR/assign_ops2.rs:15:5 | LL | a *= a * a; | ^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | a = a * a * a; | ~~~~~~~~~~~~~ error: manual implementation of an assign operation - --> $DIR/assign_ops2.rs:50:5 + --> $DIR/assign_ops2.rs:52:5 | LL | buf = buf + cows.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index ae2cc2492f41..4914f14b58ff 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -4,7 +4,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)] #![allow(incomplete_features)] -#![allow(clippy::useless_conversion)] +#![allow(clippy::useless_conversion, clippy::uninlined_format_args)] extern crate proc_macro; extern crate quote; diff --git a/tests/ui/bind_instead_of_map.fixed b/tests/ui/bind_instead_of_map.fixed index 5815550d7a6a..d94e2ac6072d 100644 --- a/tests/ui/bind_instead_of_map.fixed +++ b/tests/ui/bind_instead_of_map.fixed @@ -1,5 +1,6 @@ // run-rustfix #![deny(clippy::bind_instead_of_map)] +#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.rs b/tests/ui/bind_instead_of_map.rs index 623b100a4ce7..86f31f58284a 100644 --- a/tests/ui/bind_instead_of_map.rs +++ b/tests/ui/bind_instead_of_map.rs @@ -1,5 +1,6 @@ // run-rustfix #![deny(clippy::bind_instead_of_map)] +#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.stderr b/tests/ui/bind_instead_of_map.stderr index 24c6b7f9ef32..b6a81d21bb20 100644 --- a/tests/ui/bind_instead_of_map.stderr +++ b/tests/ui/bind_instead_of_map.stderr @@ -1,5 +1,5 @@ error: using `Option.and_then(Some)`, which is a no-op - --> $DIR/bind_instead_of_map.rs:8:13 + --> $DIR/bind_instead_of_map.rs:9:13 | LL | let _ = x.and_then(Some); | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` @@ -11,13 +11,13 @@ LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map.rs:9:13 + --> $DIR/bind_instead_of_map.rs:10:13 | LL | let _ = x.and_then(|o| Some(o + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)` error: using `Result.and_then(Ok)`, which is a no-op - --> $DIR/bind_instead_of_map.rs:15:13 + --> $DIR/bind_instead_of_map.rs:16:13 | LL | let _ = x.and_then(Ok); | ^^^^^^^^^^^^^^ help: use the expression directly: `x` diff --git a/tests/ui/borrow_box.rs b/tests/ui/borrow_box.rs index 35ed87b0f182..3b5b6bf4c950 100644 --- a/tests/ui/borrow_box.rs +++ b/tests/ui/borrow_box.rs @@ -1,7 +1,6 @@ #![deny(clippy::borrowed_box)] -#![allow(clippy::disallowed_names)] -#![allow(unused_variables)] -#![allow(dead_code)] +#![allow(dead_code, unused_variables)] +#![allow(clippy::uninlined_format_args, clippy::disallowed_names)] use std::fmt::Display; diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index 3eac32815be3..99cb60a1ead9 100644 --- a/tests/ui/borrow_box.stderr +++ b/tests/ui/borrow_box.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:21:14 + --> $DIR/borrow_box.rs:20:14 | LL | let foo: &Box; | ^^^^^^^^^^ help: try: `&bool` @@ -11,55 +11,55 @@ LL | #![deny(clippy::borrowed_box)] | ^^^^^^^^^^^^^^^^^^^^ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:25:10 + --> $DIR/borrow_box.rs:24:10 | LL | foo: &'a Box, | ^^^^^^^^^^^^^ help: try: `&'a bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:29:17 + --> $DIR/borrow_box.rs:28:17 | LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:95:25 + --> $DIR/borrow_box.rs:94:25 | LL | pub fn test14(_display: &Box) {} | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:96:25 + --> $DIR/borrow_box.rs:95:25 | LL | pub fn test15(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:97:29 + --> $DIR/borrow_box.rs:96:29 | LL | pub fn test16<'a>(_display: &'a Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:99:25 + --> $DIR/borrow_box.rs:98:25 | LL | pub fn test17(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:100:25 + --> $DIR/borrow_box.rs:99:25 | LL | pub fn test18(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:101:29 + --> $DIR/borrow_box.rs:100:29 | LL | pub fn test19<'a>(_display: &'a Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:106:25 + --> $DIR/borrow_box.rs:105:25 | LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` diff --git a/tests/ui/box_collection.rs b/tests/ui/box_collection.rs index 0780c8f0586e..4c9947b9ae72 100644 --- a/tests/ui/box_collection.rs +++ b/tests/ui/box_collection.rs @@ -15,7 +15,7 @@ macro_rules! boxit { } fn test_macro() { - boxit!(Vec::new(), Vec); + boxit!(vec![1], Vec); } fn test1(foo: Box>) {} @@ -50,7 +50,7 @@ fn test_local_not_linted() { pub fn pub_test(foo: Box>) {} pub fn pub_test_ret() -> Box> { - Box::new(Vec::new()) + Box::default() } fn main() {} diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs new file mode 100644 index 000000000000..dc522705bc62 --- /dev/null +++ b/tests/ui/box_default.rs @@ -0,0 +1,31 @@ +#![warn(clippy::box_default)] + +#[derive(Default)] +struct ImplementsDefault; + +struct OwnDefault; + +impl OwnDefault { + fn default() -> Self { + Self + } +} + +macro_rules! outer { + ($e: expr) => { + $e + }; +} + +fn main() { + let _string: Box = Box::new(Default::default()); + let _byte = Box::new(u8::default()); + let _vec = Box::new(Vec::::new()); + let _impl = Box::new(ImplementsDefault::default()); + let _impl2 = Box::new(::default()); + let _impl3: Box = Box::new(Default::default()); + let _own = Box::new(OwnDefault::default()); // should not lint + let _in_macro = outer!(Box::new(String::new())); + // false negative: default is from different expansion + let _vec2: Box> = Box::new(vec![]); +} diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr new file mode 100644 index 000000000000..b2030e95acb1 --- /dev/null +++ b/tests/ui/box_default.stderr @@ -0,0 +1,59 @@ +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:21:32 + | +LL | let _string: Box = Box::new(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + = note: `-D clippy::box-default` implied by `-D warnings` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:22:17 + | +LL | let _byte = Box::new(u8::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:23:16 + | +LL | let _vec = Box::new(Vec::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:24:17 + | +LL | let _impl = Box::new(ImplementsDefault::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:25:18 + | +LL | let _impl2 = Box::new(::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:26:42 + | +LL | let _impl3: Box = Box::new(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:28:28 + | +LL | let _in_macro = outer!(Box::new(String::new())); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: aborting due to 7 previous errors + diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.rs b/tests/ui/branches_sharing_code/shared_at_bottom.rs index 12f550d9c9a8..6a63008b5a74 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_bottom.rs @@ -1,5 +1,6 @@ -#![allow(dead_code, clippy::equatable_if_let)] #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![allow(dead_code)] +#![allow(clippy::equatable_if_let, clippy::uninlined_format_args)] // This tests the branches_sharing_code lint at the end of blocks diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_bottom.stderr index b919812e098a..b9b113dc0c6a 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_bottom.stderr @@ -1,5 +1,5 @@ error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:30:5 + --> $DIR/shared_at_bottom.rs:31:5 | LL | / let result = false; LL | | println!("Block end!"); @@ -9,7 +9,7 @@ LL | | }; | = note: the end suggestion probably needs some adjustments to use the expression result correctly note: the lint level is defined here - --> $DIR/shared_at_bottom.rs:2:36 + --> $DIR/shared_at_bottom.rs:1:36 | LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL ~ result; | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:48:5 + --> $DIR/shared_at_bottom.rs:49:5 | LL | / println!("Same end of block"); LL | | } @@ -35,7 +35,7 @@ LL + println!("Same end of block"); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:65:5 + --> $DIR/shared_at_bottom.rs:66:5 | LL | / println!( LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", @@ -54,7 +54,7 @@ LL + ); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:77:9 + --> $DIR/shared_at_bottom.rs:78:9 | LL | / println!("Hello World"); LL | | } @@ -67,7 +67,7 @@ LL + println!("Hello World"); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:93:5 + --> $DIR/shared_at_bottom.rs:94:5 | LL | / let later_used_value = "A string value"; LL | | println!("{}", later_used_value); @@ -84,7 +84,7 @@ LL + println!("{}", later_used_value); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:106:5 + --> $DIR/shared_at_bottom.rs:107:5 | LL | / let simple_examples = "I now identify as a &str :)"; LL | | println!("This is the new simple_example: {}", simple_examples); @@ -100,7 +100,7 @@ LL + println!("This is the new simple_example: {}", simple_examples); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:171:5 + --> $DIR/shared_at_bottom.rs:172:5 | LL | / x << 2 LL | | }; @@ -114,7 +114,7 @@ LL ~ x << 2; | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:178:5 + --> $DIR/shared_at_bottom.rs:179:5 | LL | / x * 4 LL | | } @@ -128,7 +128,7 @@ LL + x * 4 | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:190:44 + --> $DIR/shared_at_bottom.rs:191:44 | LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } | ^^^^^^^^^^^ diff --git a/tests/ui/branches_sharing_code/shared_at_top.rs b/tests/ui/branches_sharing_code/shared_at_top.rs index bdeb0a39558d..9e0b99f16665 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.rs +++ b/tests/ui/branches_sharing_code/shared_at_top.rs @@ -1,5 +1,6 @@ -#![allow(dead_code, clippy::mixed_read_write_in_expression)] -#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] +#![allow(dead_code)] +#![allow(clippy::mixed_read_write_in_expression, clippy::uninlined_format_args)] // This tests the branches_sharing_code lint at the start of blocks diff --git a/tests/ui/branches_sharing_code/shared_at_top.stderr b/tests/ui/branches_sharing_code/shared_at_top.stderr index fb3da641fb5e..3e3242a75d30 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top.stderr @@ -1,15 +1,15 @@ error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:10:5 + --> $DIR/shared_at_top.rs:11:5 | LL | / if true { LL | | println!("Hello World!"); | |_________________________________^ | note: the lint level is defined here - --> $DIR/shared_at_top.rs:2:36 + --> $DIR/shared_at_top.rs:1:9 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider moving these statements before the if | LL ~ println!("Hello World!"); @@ -17,7 +17,7 @@ LL + if true { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:19:5 + --> $DIR/shared_at_top.rs:20:5 | LL | / if x == 0 { LL | | let y = 9; @@ -35,7 +35,7 @@ LL + if x == 0 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:40:5 + --> $DIR/shared_at_top.rs:41:5 | LL | / let _ = if x == 7 { LL | | let y = 16; @@ -48,7 +48,7 @@ LL + let _ = if x == 7 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:58:5 + --> $DIR/shared_at_top.rs:59:5 | LL | / if x == 10 { LL | | let used_value_name = "Different type"; @@ -64,7 +64,7 @@ LL + if x == 10 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:72:5 + --> $DIR/shared_at_top.rs:73:5 | LL | / if x == 11 { LL | | let can_be_overridden = "Move me"; @@ -80,7 +80,7 @@ LL + if x == 11 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:88:5 + --> $DIR/shared_at_top.rs:89:5 | LL | / if x == 2020 { LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); @@ -95,7 +95,7 @@ LL + if x == 2020 { | error: this `if` has identical blocks - --> $DIR/shared_at_top.rs:96:18 + --> $DIR/shared_at_top.rs:97:18 | LL | if x == 2019 { | __________________^ @@ -104,7 +104,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/shared_at_top.rs:98:12 + --> $DIR/shared_at_top.rs:99:12 | LL | } else { | ____________^ @@ -112,10 +112,10 @@ LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); LL | | } | |_____^ note: the lint level is defined here - --> $DIR/shared_at_top.rs:2:9 + --> $DIR/shared_at_top.rs:1:40 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 7 previous errors diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs index deefdad32c9d..93b8c6e10dae 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs @@ -1,5 +1,6 @@ +#![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] #![allow(dead_code)] -#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![allow(clippy::uninlined_format_args)] // branches_sharing_code at the top and bottom of the if blocks diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr index 3edb8e53a7d4..ccd697a42155 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -1,5 +1,5 @@ error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:16:5 + --> $DIR/shared_at_top_and_bottom.rs:17:5 | LL | / if x == 7 { LL | | let t = 7; @@ -8,16 +8,16 @@ LL | | let _overlap_end = 2 * t; | |_________________________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:28:5 + --> $DIR/shared_at_top_and_bottom.rs:29:5 | LL | / let _u = 9; LL | | } | |_____^ note: the lint level is defined here - --> $DIR/shared_at_top_and_bottom.rs:2:36 + --> $DIR/shared_at_top_and_bottom.rs:1:9 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider moving these statements before the if | LL ~ let t = 7; @@ -32,7 +32,7 @@ LL + let _u = 9; | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:32:5 + --> $DIR/shared_at_top_and_bottom.rs:33:5 | LL | / if x == 99 { LL | | let r = 7; @@ -41,7 +41,7 @@ LL | | let _overlap_middle = r * r; | |____________________________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:43:5 + --> $DIR/shared_at_top_and_bottom.rs:44:5 | LL | / let _overlap_end = r * r * r; LL | | let z = "end"; @@ -63,7 +63,7 @@ LL + let z = "end"; | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:61:5 + --> $DIR/shared_at_top_and_bottom.rs:62:5 | LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { LL | | let a = 0xcafe; @@ -72,7 +72,7 @@ LL | | let e_id = gen_id(a, b); | |________________________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:81:5 + --> $DIR/shared_at_top_and_bottom.rs:82:5 | LL | / let pack = DataPack { LL | | id: e_id, @@ -102,14 +102,14 @@ LL + process_data(pack); | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:94:5 + --> $DIR/shared_at_top_and_bottom.rs:95:5 | LL | / let _ = if x == 7 { LL | | let _ = 19; | |___________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:103:5 + --> $DIR/shared_at_top_and_bottom.rs:104:5 | LL | / x << 2 LL | | }; @@ -127,14 +127,14 @@ LL ~ x << 2; | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:106:5 + --> $DIR/shared_at_top_and_bottom.rs:107:5 | LL | / if x == 9 { LL | | let _ = 17; | |___________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:115:5 + --> $DIR/shared_at_top_and_bottom.rs:116:5 | LL | / x * 4 LL | | } diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.rs b/tests/ui/branches_sharing_code/valid_if_blocks.rs index a26141be2373..2d6055eb6c42 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.rs +++ b/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -1,5 +1,6 @@ -#![allow(dead_code, clippy::mixed_read_write_in_expression)] -#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] +#![allow(dead_code)] +#![allow(clippy::mixed_read_write_in_expression, clippy::uninlined_format_args)] // This tests valid if blocks that shouldn't trigger the lint diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/tests/ui/branches_sharing_code/valid_if_blocks.stderr index d2acd6d9735c..ce7fff0122f1 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.stderr +++ b/tests/ui/branches_sharing_code/valid_if_blocks.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:104:14 + --> $DIR/valid_if_blocks.rs:105:14 | LL | if false { | ______________^ @@ -7,20 +7,20 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:105:12 + --> $DIR/valid_if_blocks.rs:106:12 | LL | } else { | ____________^ LL | | } | |_____^ note: the lint level is defined here - --> $DIR/valid_if_blocks.rs:2:9 + --> $DIR/valid_if_blocks.rs:1:40 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:115:15 + --> $DIR/valid_if_blocks.rs:116:15 | LL | if x == 0 { | _______________^ @@ -31,7 +31,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:119:12 + --> $DIR/valid_if_blocks.rs:120:12 | LL | } else { | ____________^ @@ -42,19 +42,19 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:126:23 + --> $DIR/valid_if_blocks.rs:127:23 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ | note: same as this - --> $DIR/valid_if_blocks.rs:126:34 + --> $DIR/valid_if_blocks.rs:127:34 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:132:23 + --> $DIR/valid_if_blocks.rs:133:23 | LL | } else if x == 68 { | _______________________^ @@ -66,7 +66,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:137:12 + --> $DIR/valid_if_blocks.rs:138:12 | LL | } else { | ____________^ @@ -78,7 +78,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:146:23 + --> $DIR/valid_if_blocks.rs:147:23 | LL | } else if x == 68 { | _______________________^ @@ -88,7 +88,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:149:12 + --> $DIR/valid_if_blocks.rs:150:12 | LL | } else { | ____________^ diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index 7ecefd7b1343..a37f3fec20f1 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::cast_abs_to_unsigned)] +#![allow(clippy::uninlined_format_args)] fn main() { let x: i32 = -42; diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 30c603fca9a1..5706930af5a0 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::cast_abs_to_unsigned)] +#![allow(clippy::uninlined_format_args)] fn main() { let x: i32 = -42; diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index 045537745267..7cea11c183d2 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -1,5 +1,5 @@ error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:6:18 + --> $DIR/cast_abs_to_unsigned.rs:7:18 | LL | let y: u32 = x.abs() as u32; | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` @@ -7,97 +7,97 @@ LL | let y: u32 = x.abs() as u32; = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:10:20 + --> $DIR/cast_abs_to_unsigned.rs:11:20 | LL | let _: usize = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:11:20 + --> $DIR/cast_abs_to_unsigned.rs:12:20 | LL | let _: usize = a.abs() as _; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:12:13 + --> $DIR/cast_abs_to_unsigned.rs:13:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:15:13 + --> $DIR/cast_abs_to_unsigned.rs:16:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:16:13 + --> $DIR/cast_abs_to_unsigned.rs:17:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:17:13 + --> $DIR/cast_abs_to_unsigned.rs:18:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:18:13 + --> $DIR/cast_abs_to_unsigned.rs:19:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:19:13 + --> $DIR/cast_abs_to_unsigned.rs:20:13 | LL | let _ = a.abs() as u64; | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:20:13 + --> $DIR/cast_abs_to_unsigned.rs:21:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:23:13 + --> $DIR/cast_abs_to_unsigned.rs:24:13 | LL | let _ = a.abs() as usize; | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:24:13 + --> $DIR/cast_abs_to_unsigned.rs:25:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:25:13 + --> $DIR/cast_abs_to_unsigned.rs:26:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:26:13 + --> $DIR/cast_abs_to_unsigned.rs:27:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:27:13 + --> $DIR/cast_abs_to_unsigned.rs:28:13 | LL | let _ = a.abs() as u64; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:28:13 + --> $DIR/cast_abs_to_unsigned.rs:29:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:30:13 + --> $DIR/cast_abs_to_unsigned.rs:31:13 | LL | let _ = (x as i64 - y as i64).abs() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()` diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 603ae7dc9eb1..7d53e08345d3 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -1,9 +1,10 @@ #![warn(clippy::collapsible_match)] #![allow( + clippy::equatable_if_let, clippy::needless_return, clippy::no_effect, clippy::single_match, - clippy::equatable_if_let + clippy::uninlined_format_args )] fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 33562e8401ca..2580bef58091 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -1,5 +1,5 @@ error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:12:20 + --> $DIR/collapsible_match.rs:13:20 | LL | Ok(val) => match val { | ____________________^ @@ -9,7 +9,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:12:12 + --> $DIR/collapsible_match.rs:13:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -18,7 +18,7 @@ LL | Some(n) => foo(n), = note: `-D clippy::collapsible-match` implied by `-D warnings` error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:21:20 + --> $DIR/collapsible_match.rs:22:20 | LL | Ok(val) => match val { | ____________________^ @@ -28,7 +28,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:21:12 + --> $DIR/collapsible_match.rs:22:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -36,7 +36,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:30:9 + --> $DIR/collapsible_match.rs:31:9 | LL | / if let Some(n) = val { LL | | take(n); @@ -44,7 +44,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:29:15 + --> $DIR/collapsible_match.rs:30:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -52,7 +52,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:37:9 + --> $DIR/collapsible_match.rs:38:9 | LL | / if let Some(n) = val { LL | | take(n); @@ -62,7 +62,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:36:15 + --> $DIR/collapsible_match.rs:37:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -70,7 +70,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:48:9 + --> $DIR/collapsible_match.rs:49:9 | LL | / match val { LL | | Some(n) => foo(n), @@ -79,7 +79,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:47:15 + --> $DIR/collapsible_match.rs:48:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -88,7 +88,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:57:13 + --> $DIR/collapsible_match.rs:58:13 | LL | / if let Some(n) = val { LL | | take(n); @@ -96,7 +96,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:56:12 + --> $DIR/collapsible_match.rs:57:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -104,7 +104,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:66:9 + --> $DIR/collapsible_match.rs:67:9 | LL | / match val { LL | | Some(n) => foo(n), @@ -113,7 +113,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:65:15 + --> $DIR/collapsible_match.rs:66:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -122,7 +122,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:77:13 + --> $DIR/collapsible_match.rs:78:13 | LL | / if let Some(n) = val { LL | | take(n); @@ -132,7 +132,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:76:12 + --> $DIR/collapsible_match.rs:77:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -140,7 +140,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:88:20 + --> $DIR/collapsible_match.rs:89:20 | LL | Ok(val) => match val { | ____________________^ @@ -150,7 +150,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:88:12 + --> $DIR/collapsible_match.rs:89:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -158,7 +158,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:97:22 + --> $DIR/collapsible_match.rs:98:22 | LL | Some(val) => match val { | ______________________^ @@ -168,7 +168,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:97:14 + --> $DIR/collapsible_match.rs:98:14 | LL | Some(val) => match val { | ^^^ replace this binding diff --git a/tests/ui/crashes/ice-4775.rs b/tests/ui/crashes/ice-4775.rs index 405e3039e7d0..f693aafd1cbb 100644 --- a/tests/ui/crashes/ice-4775.rs +++ b/tests/ui/crashes/ice-4775.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + pub struct ArrayWrapper([usize; N]); impl ArrayWrapper<{ N }> { diff --git a/tests/ui/crashes/ice-9445.rs b/tests/ui/crashes/ice-9445.rs new file mode 100644 index 000000000000..c67b22f6f8c4 --- /dev/null +++ b/tests/ui/crashes/ice-9445.rs @@ -0,0 +1,3 @@ +const UNINIT: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); + +fn main() {} diff --git a/tests/ui/crashes/ice-9459.rs b/tests/ui/crashes/ice-9459.rs new file mode 100644 index 000000000000..55615124fcf0 --- /dev/null +++ b/tests/ui/crashes/ice-9459.rs @@ -0,0 +1,5 @@ +#![feature(unsized_fn_params)] + +pub fn f0(_f: dyn FnOnce()) {} + +fn main() {} diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs index 55a8b403407c..b34997d4ee0b 100644 --- a/tests/ui/crashes/regressions.rs +++ b/tests/ui/crashes/regressions.rs @@ -1,4 +1,4 @@ -#![allow(clippy::disallowed_names)] +#![allow(clippy::disallowed_names, clippy::uninlined_format_args)] pub fn foo(bar: *const u8) { println!("{:#p}", bar); diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index fce66eb17596..eedd43619392 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -1,8 +1,8 @@ // run-rustfix // aux-build: proc_macro_with_span.rs - -#![allow(unused_imports, dead_code)] #![deny(clippy::default_trait_access)] +#![allow(dead_code, unused_imports)] +#![allow(clippy::uninlined_format_args)] extern crate proc_macro_with_span; diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index 3e8e898b7bc6..11d4bc5c5f02 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,8 +1,8 @@ // run-rustfix // aux-build: proc_macro_with_span.rs - -#![allow(unused_imports, dead_code)] #![deny(clippy::default_trait_access)] +#![allow(dead_code, unused_imports)] +#![allow(clippy::uninlined_format_args)] extern crate proc_macro_with_span; diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 3493de37a55b..49b2dde3f1e8 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -5,7 +5,7 @@ LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` | note: the lint level is defined here - --> $DIR/default_trait_access.rs:5:9 + --> $DIR/default_trait_access.rs:3:9 | LL | #![deny(clippy::default_trait_access)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/doc_link_with_quotes.rs b/tests/ui/doc_link_with_quotes.rs index ab52fb1a4a69..17c04c34e246 100644 --- a/tests/ui/doc_link_with_quotes.rs +++ b/tests/ui/doc_link_with_quotes.rs @@ -4,9 +4,14 @@ fn main() { foo() } -/// Calls ['bar'] +/// Calls ['bar'] uselessly pub fn foo() { bar() } +/// # Examples +/// This demonstrates issue \#8961 +/// ``` +/// let _ = vec!['w', 'a', 't']; +/// ``` pub fn bar() {} diff --git a/tests/ui/doc_link_with_quotes.stderr b/tests/ui/doc_link_with_quotes.stderr index bf6d57d8afe8..ea730e667d65 100644 --- a/tests/ui/doc_link_with_quotes.stderr +++ b/tests/ui/doc_link_with_quotes.stderr @@ -1,8 +1,8 @@ error: possible intra-doc link using quotes instead of backticks - --> $DIR/doc_link_with_quotes.rs:7:1 + --> $DIR/doc_link_with_quotes.rs:7:12 | -LL | /// Calls ['bar'] - | ^^^^^^^^^^^^^^^^^ +LL | /// Calls ['bar'] uselessly + | ^^^^^ | = note: `-D clippy::doc-link-with-quotes` implied by `-D warnings` diff --git a/tests/ui/drop_forget_copy.rs b/tests/ui/drop_forget_copy.rs index 7c7a9ecff67f..a7276dd59f43 100644 --- a/tests/ui/drop_forget_copy.rs +++ b/tests/ui/drop_forget_copy.rs @@ -64,3 +64,23 @@ fn main() { let a5 = a1.clone(); forget(a5); } + +#[allow(unused)] +#[allow(clippy::unit_cmp)] +fn issue9482(x: u8) { + fn println_and(t: T) -> T { + println!("foo"); + t + } + + match x { + 0 => drop(println_and(12)), // Don't lint (copy type), we only care about side-effects + 1 => drop(println_and(String::new())), // Don't lint (no copy type), we only care about side-effects + 2 => { + drop(println_and(13)); // Lint, even if we only care about the side-effect, it's already in a block + }, + 3 if drop(println_and(14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + 4 => drop(2), // Lint, not a fn/method call + _ => (), + } +} diff --git a/tests/ui/drop_forget_copy.stderr b/tests/ui/drop_forget_copy.stderr index 21adb3b3a504..90bef1c3c439 100644 --- a/tests/ui/drop_forget_copy.stderr +++ b/tests/ui/drop_forget_copy.stderr @@ -72,5 +72,41 @@ note: argument has type `SomeStruct` LL | forget(s4); | ^^ -error: aborting due to 6 previous errors +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact + --> $DIR/drop_forget_copy.rs:80:13 + | +LL | drop(println_and(13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `i32` + --> $DIR/drop_forget_copy.rs:80:18 + | +LL | drop(println_and(13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact + --> $DIR/drop_forget_copy.rs:82:14 + | +LL | 3 if drop(println_and(14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `i32` + --> $DIR/drop_forget_copy.rs:82:19 + | +LL | 3 if drop(println_and(14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact + --> $DIR/drop_forget_copy.rs:83:14 + | +LL | 4 => drop(2), // Lint, not a fn/method call + | ^^^^^^^ + | +note: argument has type `i32` + --> $DIR/drop_forget_copy.rs:83:19 + | +LL | 4 => drop(2), // Lint, not a fn/method call + | ^ + +error: aborting due to 9 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index f8d559bf226f..a9cc80aaaf62 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -1,14 +1,14 @@ // run-rustfix - -#![allow( - unused, - clippy::no_effect, - clippy::redundant_closure_call, - clippy::needless_pass_by_value, - clippy::option_map_unit_fn, - clippy::needless_borrow -)] #![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)] +#![allow(unused)] +#![allow( + clippy::needless_borrow, + clippy::needless_pass_by_value, + clippy::no_effect, + clippy::option_map_unit_fn, + clippy::redundant_closure_call, + clippy::uninlined_format_args +)] use std::path::{Path, PathBuf}; @@ -303,3 +303,16 @@ fn not_general_enough() { fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {} f(|path| std::fs::remove_file(path)); } + +// https://github.com/rust-lang/rust-clippy/issues/9369 +pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -> impl FnMut() { + fn takes_fn_mut(_: impl FnMut()) {} + takes_fn_mut(&mut f); + + fn takes_fn_once(_: impl FnOnce()) {} + takes_fn_once(&mut f); + + f(); + + move || takes_fn_mut(&mut f_used_once) +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index f0fb55a1e5f0..cc99906ccd66 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -1,14 +1,14 @@ // run-rustfix - -#![allow( - unused, - clippy::no_effect, - clippy::redundant_closure_call, - clippy::needless_pass_by_value, - clippy::option_map_unit_fn, - clippy::needless_borrow -)] #![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)] +#![allow(unused)] +#![allow( + clippy::needless_borrow, + clippy::needless_pass_by_value, + clippy::no_effect, + clippy::option_map_unit_fn, + clippy::redundant_closure_call, + clippy::uninlined_format_args +)] use std::path::{Path, PathBuf}; @@ -303,3 +303,16 @@ fn not_general_enough() { fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {} f(|path| std::fs::remove_file(path)); } + +// https://github.com/rust-lang/rust-clippy/issues/9369 +pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -> impl FnMut() { + fn takes_fn_mut(_: impl FnMut()) {} + takes_fn_mut(|| f()); + + fn takes_fn_once(_: impl FnOnce()) {} + takes_fn_once(|| f()); + + f(); + + move || takes_fn_mut(|| f_used_once()) +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index bf2e97e744ab..434706b7e258 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -116,5 +116,23 @@ error: redundant closure LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` -error: aborting due to 19 previous errors +error: redundant closure + --> $DIR/eta.rs:310:18 + | +LL | takes_fn_mut(|| f()); + | ^^^^^^ help: replace the closure with the function itself: `&mut f` + +error: redundant closure + --> $DIR/eta.rs:313:19 + | +LL | takes_fn_once(|| f()); + | ^^^^^^ help: replace the closure with the function itself: `&mut f` + +error: redundant closure + --> $DIR/eta.rs:317:26 + | +LL | move || takes_fn_mut(|| f_used_once()) + | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` + +error: aborting due to 22 previous errors diff --git a/tests/ui/expect_fun_call.fixed b/tests/ui/expect_fun_call.fixed index 53e45d28bded..15172ae345c2 100644 --- a/tests/ui/expect_fun_call.fixed +++ b/tests/ui/expect_fun_call.fixed @@ -1,7 +1,6 @@ // run-rustfix - #![warn(clippy::expect_fun_call)] -#![allow(clippy::to_string_in_format_args)] +#![allow(clippy::to_string_in_format_args, clippy::uninlined_format_args)] /// Checks implementation of the `EXPECT_FUN_CALL` lint @@ -101,4 +100,10 @@ fn main() { let opt_ref = &opt; opt_ref.unwrap_or_else(|| panic!("{:?}", opt_ref)); } + + let format_capture: Option = None; + format_capture.unwrap_or_else(|| panic!("{error_code}")); + + let format_capture_and_value: Option = None; + format_capture_and_value.unwrap_or_else(|| panic!("{error_code}, {}", 1)); } diff --git a/tests/ui/expect_fun_call.rs b/tests/ui/expect_fun_call.rs index 22e530b80349..0f448d004174 100644 --- a/tests/ui/expect_fun_call.rs +++ b/tests/ui/expect_fun_call.rs @@ -1,7 +1,6 @@ // run-rustfix - #![warn(clippy::expect_fun_call)] -#![allow(clippy::to_string_in_format_args)] +#![allow(clippy::to_string_in_format_args, clippy::uninlined_format_args)] /// Checks implementation of the `EXPECT_FUN_CALL` lint @@ -101,4 +100,10 @@ fn main() { let opt_ref = &opt; opt_ref.expect(&format!("{:?}", opt_ref)); } + + let format_capture: Option = None; + format_capture.expect(&format!("{error_code}")); + + let format_capture_and_value: Option = None; + format_capture_and_value.expect(&format!("{error_code}, {}", 1)); } diff --git a/tests/ui/expect_fun_call.stderr b/tests/ui/expect_fun_call.stderr index aca15935fca0..cb55e32aee02 100644 --- a/tests/ui/expect_fun_call.stderr +++ b/tests/ui/expect_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:35:26 + --> $DIR/expect_fun_call.rs:34:26 | LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` @@ -7,76 +7,88 @@ LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code = note: `-D clippy::expect-fun-call` implied by `-D warnings` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:38:26 + --> $DIR/expect_fun_call.rs:37:26 | LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:41:37 + --> $DIR/expect_fun_call.rs:40:37 | LL | with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:51:25 + --> $DIR/expect_fun_call.rs:50:25 | LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:54:25 + --> $DIR/expect_fun_call.rs:53:25 | LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:66:17 + --> $DIR/expect_fun_call.rs:65:17 | LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:87:21 + --> $DIR/expect_fun_call.rs:86:21 | LL | Some("foo").expect(&get_string()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:88:21 + --> $DIR/expect_fun_call.rs:87:21 | LL | Some("foo").expect(get_string().as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:89:21 + --> $DIR/expect_fun_call.rs:88:21 | LL | Some("foo").expect(get_string().as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:91:21 + --> $DIR/expect_fun_call.rs:90:21 | LL | Some("foo").expect(get_static_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_static_str()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:92:21 + --> $DIR/expect_fun_call.rs:91:21 | LL | Some("foo").expect(get_non_static_str(&0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:96:16 + --> $DIR/expect_fun_call.rs:95:16 | LL | Some(true).expect(&format!("key {}, {}", 1, 2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:102:17 + --> $DIR/expect_fun_call.rs:101:17 | LL | opt_ref.expect(&format!("{:?}", opt_ref)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))` -error: aborting due to 13 previous errors +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:105:20 + | +LL | format_capture.expect(&format!("{error_code}")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{error_code}"))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:108:30 + | +LL | format_capture_and_value.expect(&format!("{error_code}, {}", 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{error_code}, {}", 1))` + +error: aborting due to 15 previous errors diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index aa966761febd..6eddc01e2c47 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -1,4 +1,5 @@ #![warn(clippy::explicit_counter_loop)] +#![allow(clippy::uninlined_format_args)] fn main() { let mut vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/explicit_counter_loop.stderr b/tests/ui/explicit_counter_loop.stderr index f9f8407d5775..d3f3c626bbdf 100644 --- a/tests/ui/explicit_counter_loop.stderr +++ b/tests/ui/explicit_counter_loop.stderr @@ -1,5 +1,5 @@ error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:6:5 + --> $DIR/explicit_counter_loop.rs:7:5 | LL | for _v in &vec { | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` @@ -7,49 +7,49 @@ LL | for _v in &vec { = note: `-D clippy::explicit-counter-loop` implied by `-D warnings` error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:12:5 + --> $DIR/explicit_counter_loop.rs:13:5 | LL | for _v in &vec { | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:17:5 + --> $DIR/explicit_counter_loop.rs:18:5 | LL | for _v in &mut vec { | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter_mut().enumerate()` error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:22:5 + --> $DIR/explicit_counter_loop.rs:23:5 | LL | for _v in vec { | ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()` error: the variable `count` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:61:9 + --> $DIR/explicit_counter_loop.rs:62:9 | LL | for ch in text.chars() { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` error: the variable `count` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:72:9 + --> $DIR/explicit_counter_loop.rs:73:9 | LL | for ch in text.chars() { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` error: the variable `count` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:130:9 + --> $DIR/explicit_counter_loop.rs:131:9 | LL | for _i in 3..10 { | ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()` error: the variable `idx_usize` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:170:9 + --> $DIR/explicit_counter_loop.rs:171:9 | LL | for _item in slice { | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_usize, _item) in slice.iter().enumerate()` error: the variable `idx_u32` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:182:9 + --> $DIR/explicit_counter_loop.rs:183:9 | LL | for _item in slice { | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_u32, _item) in (0_u32..).zip(slice.iter())` diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 523cae183ee6..6d32bbece1e5 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -1,13 +1,13 @@ // run-rustfix - -#![allow( - unused_variables, - clippy::clone_double_ref, - clippy::needless_borrow, - clippy::borrow_deref_ref, - clippy::explicit_auto_deref -)] #![warn(clippy::explicit_deref_methods)] +#![allow(unused_variables)] +#![allow( + clippy::borrow_deref_ref, + clippy::clone_double_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + clippy::uninlined_format_args +)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index 0bbc1ae57cdf..779909e42380 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -1,13 +1,13 @@ // run-rustfix - -#![allow( - unused_variables, - clippy::clone_double_ref, - clippy::needless_borrow, - clippy::borrow_deref_ref, - clippy::explicit_auto_deref -)] #![warn(clippy::explicit_deref_methods)] +#![allow(unused_variables)] +#![allow( + clippy::borrow_deref_ref, + clippy::clone_double_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + clippy::uninlined_format_args +)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_write.fixed b/tests/ui/explicit_write.fixed index 35283725619a..862c3fea9ee8 100644 --- a/tests/ui/explicit_write.fixed +++ b/tests/ui/explicit_write.fixed @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports)] #![warn(clippy::explicit_write)] +#![allow(unused_imports)] +#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() diff --git a/tests/ui/explicit_write.rs b/tests/ui/explicit_write.rs index be864a55b663..41d7c2255738 100644 --- a/tests/ui/explicit_write.rs +++ b/tests/ui/explicit_write.rs @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports)] #![warn(clippy::explicit_write)] +#![allow(unused_imports)] +#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() diff --git a/tests/ui/explicit_write.stderr b/tests/ui/explicit_write.stderr index ff05f4343d77..457e9c627180 100644 --- a/tests/ui/explicit_write.stderr +++ b/tests/ui/explicit_write.stderr @@ -1,5 +1,5 @@ error: use of `write!(stdout(), ...).unwrap()` - --> $DIR/explicit_write.rs:23:9 + --> $DIR/explicit_write.rs:24:9 | LL | write!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` @@ -7,73 +7,73 @@ LL | write!(std::io::stdout(), "test").unwrap(); = note: `-D clippy::explicit-write` implied by `-D warnings` error: use of `write!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:24:9 + --> $DIR/explicit_write.rs:25:9 | LL | write!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> $DIR/explicit_write.rs:25:9 + --> $DIR/explicit_write.rs:26:9 | LL | writeln!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:26:9 + --> $DIR/explicit_write.rs:27:9 | LL | writeln!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")` error: use of `stdout().write_fmt(...).unwrap()` - --> $DIR/explicit_write.rs:27:9 + --> $DIR/explicit_write.rs:28:9 | LL | std::io::stdout().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` error: use of `stderr().write_fmt(...).unwrap()` - --> $DIR/explicit_write.rs:28:9 + --> $DIR/explicit_write.rs:29:9 | LL | std::io::stderr().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> $DIR/explicit_write.rs:31:9 + --> $DIR/explicit_write.rs:32:9 | LL | writeln!(std::io::stdout(), "test/ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:32:9 + --> $DIR/explicit_write.rs:33:9 | LL | writeln!(std::io::stderr(), "test/ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:35:9 + --> $DIR/explicit_write.rs:36:9 | LL | writeln!(std::io::stderr(), "with {}", value).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {}", value)` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:36:9 + --> $DIR/explicit_write.rs:37:9 | LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {} {}", 2, value)` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:37:9 + --> $DIR/explicit_write.rs:38:9 | LL | writeln!(std::io::stderr(), "with {value}").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {value}")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:38:9 + --> $DIR/explicit_write.rs:39:9 | LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("macro arg {}", one!())` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:40:9 + --> $DIR/explicit_write.rs:41:9 | LL | writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("{:w$}", value, w = width)` diff --git a/tests/ui/fallible_impl_from.rs b/tests/ui/fallible_impl_from.rs index 5d5af4e46329..fb6e8ec706b1 100644 --- a/tests/ui/fallible_impl_from.rs +++ b/tests/ui/fallible_impl_from.rs @@ -1,4 +1,5 @@ #![deny(clippy::fallible_impl_from)] +#![allow(clippy::uninlined_format_args)] // docs example struct Foo(i32); diff --git a/tests/ui/fallible_impl_from.stderr b/tests/ui/fallible_impl_from.stderr index 28a061af664d..21761484f8c4 100644 --- a/tests/ui/fallible_impl_from.stderr +++ b/tests/ui/fallible_impl_from.stderr @@ -1,5 +1,5 @@ error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:5:1 + --> $DIR/fallible_impl_from.rs:6:1 | LL | / impl From for Foo { LL | | fn from(s: String) -> Self { @@ -10,7 +10,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:7:13 + --> $DIR/fallible_impl_from.rs:8:13 | LL | Foo(s.parse().unwrap()) | ^^^^^^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | #![deny(clippy::fallible_impl_from)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:26:1 + --> $DIR/fallible_impl_from.rs:27:1 | LL | / impl From for Invalid { LL | | fn from(i: usize) -> Invalid { @@ -34,14 +34,14 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:29:13 + --> $DIR/fallible_impl_from.rs:30:13 | LL | panic!(); | ^^^^^^^^ = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:35:1 + --> $DIR/fallible_impl_from.rs:36:1 | LL | / impl From> for Invalid { LL | | fn from(s: Option) -> Invalid { @@ -54,7 +54,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:37:17 + --> $DIR/fallible_impl_from.rs:38:17 | LL | let s = s.unwrap(); | ^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | panic!("{:?}", s); = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:53:1 + --> $DIR/fallible_impl_from.rs:54:1 | LL | / impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { LL | | fn from(s: &'a mut as ProjStrTrait>::ProjString) -> Invalid { @@ -81,7 +81,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:55:12 + --> $DIR/fallible_impl_from.rs:56:12 | LL | if s.parse::().ok().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/floating_point_exp.fixed b/tests/ui/floating_point_exp.fixed index c86a502d15f0..b9e3d89c2b29 100644 --- a/tests/ui/floating_point_exp.fixed +++ b/tests/ui/floating_point_exp.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 2f32; diff --git a/tests/ui/floating_point_exp.rs b/tests/ui/floating_point_exp.rs index e59589f912a2..ef008dd9be05 100644 --- a/tests/ui/floating_point_exp.rs +++ b/tests/ui/floating_point_exp.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 2f32; diff --git a/tests/ui/floating_point_exp.stderr b/tests/ui/floating_point_exp.stderr index f84eede19872..b92fae56e421 100644 --- a/tests/ui/floating_point_exp.stderr +++ b/tests/ui/floating_point_exp.stderr @@ -1,5 +1,5 @@ error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:6:13 + --> $DIR/floating_point_exp.rs:7:13 | LL | let _ = x.exp() - 1.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` @@ -7,25 +7,25 @@ LL | let _ = x.exp() - 1.0; = note: `-D clippy::imprecise-flops` implied by `-D warnings` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:7:13 + --> $DIR/floating_point_exp.rs:8:13 | LL | let _ = x.exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:8:13 + --> $DIR/floating_point_exp.rs:9:13 | LL | let _ = (x as f32).exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:14:13 + --> $DIR/floating_point_exp.rs:15:13 | LL | let _ = x.exp() - 1.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:15:13 + --> $DIR/floating_point_exp.rs:16:13 | LL | let _ = x.exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index 4def9300bb7d..ee5406461600 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(dead_code, clippy::double_parens)] +#![allow(dead_code, clippy::double_parens, clippy::unnecessary_cast)] #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] const TWO: f32 = 2.0; diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs index 1e04caa7d2a8..0590670a50bc 100644 --- a/tests/ui/floating_point_log.rs +++ b/tests/ui/floating_point_log.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(dead_code, clippy::double_parens)] +#![allow(dead_code, clippy::double_parens, clippy::unnecessary_cast)] #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] const TWO: f32 = 2.0; diff --git a/tests/ui/floating_point_logbase.fixed b/tests/ui/floating_point_logbase.fixed index 936462f94066..7347bf72cbea 100644 --- a/tests/ui/floating_point_logbase.fixed +++ b/tests/ui/floating_point_logbase.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_logbase.rs b/tests/ui/floating_point_logbase.rs index 0b56fa8fa41f..ba5b8d406928 100644 --- a/tests/ui/floating_point_logbase.rs +++ b/tests/ui/floating_point_logbase.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_logbase.stderr b/tests/ui/floating_point_logbase.stderr index 384e3554cbbe..9d736b5e1a27 100644 --- a/tests/ui/floating_point_logbase.stderr +++ b/tests/ui/floating_point_logbase.stderr @@ -1,5 +1,5 @@ error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:7:13 + --> $DIR/floating_point_logbase.rs:8:13 | LL | let _ = x.ln() / y.ln(); | ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` @@ -7,25 +7,25 @@ LL | let _ = x.ln() / y.ln(); = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:8:13 + --> $DIR/floating_point_logbase.rs:9:13 | LL | let _ = (x as f32).ln() / y.ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log(y)` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:9:13 + --> $DIR/floating_point_logbase.rs:10:13 | LL | let _ = x.log2() / y.log2(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:10:13 + --> $DIR/floating_point_logbase.rs:11:13 | LL | let _ = x.log10() / y.log10(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:11:13 + --> $DIR/floating_point_logbase.rs:12:13 | LL | let _ = x.log(5f32) / y.log(5f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed index 169ec02f82be..d3e536ba3500 100644 --- a/tests/ui/floating_point_mul_add.fixed +++ b/tests/ui/floating_point_mul_add.fixed @@ -19,7 +19,9 @@ fn main() { let d: f64 = 0.0001; let _ = a.mul_add(b, c); + let _ = a.mul_add(b, -c); let _ = a.mul_add(b, c); + let _ = a.mul_add(-b, c); let _ = 2.0f64.mul_add(4.0, a); let _ = 2.0f64.mul_add(4., a); diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs index 5338d4fc2b74..5d4a9e35cfc2 100644 --- a/tests/ui/floating_point_mul_add.rs +++ b/tests/ui/floating_point_mul_add.rs @@ -19,7 +19,9 @@ fn main() { let d: f64 = 0.0001; let _ = a * b + c; + let _ = a * b - c; let _ = c + a * b; + let _ = c - a * b; let _ = a + 2.0 * 4.0; let _ = a + 2. * 4.; diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr index e637bbf90caa..a79ae94e8d43 100644 --- a/tests/ui/floating_point_mul_add.stderr +++ b/tests/ui/floating_point_mul_add.stderr @@ -9,56 +9,68 @@ LL | let _ = a * b + c; error: multiply and add expressions can be calculated more efficiently and accurately --> $DIR/floating_point_mul_add.rs:22:13 | +LL | let _ = a * b - c; + | ^^^^^^^^^ help: consider using: `a.mul_add(b, -c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:23:13 + | LL | let _ = c + a * b; | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:23:13 + --> $DIR/floating_point_mul_add.rs:24:13 + | +LL | let _ = c - a * b; + | ^^^^^^^^^ help: consider using: `a.mul_add(-b, c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:25:13 | LL | let _ = a + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:24:13 + --> $DIR/floating_point_mul_add.rs:26:13 | LL | let _ = a + 2. * 4.; | ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:26:13 + --> $DIR/floating_point_mul_add.rs:28:13 | LL | let _ = (a * b) + c; | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:27:13 + --> $DIR/floating_point_mul_add.rs:29:13 | LL | let _ = c + (a * b); | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:28:13 + --> $DIR/floating_point_mul_add.rs:30:13 | LL | let _ = a * b * c + d; | ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:30:13 + --> $DIR/floating_point_mul_add.rs:32:13 | LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:31:13 + --> $DIR/floating_point_mul_add.rs:33:13 | LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:33:13 + --> $DIR/floating_point_mul_add.rs:35:13 | LL | let _ = (a * a + b).sqrt(); | ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)` -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/floating_point_powf.fixed b/tests/ui/floating_point_powf.fixed index e7ef45634dff..f7f93de29577 100644 --- a/tests/ui/floating_point_powf.fixed +++ b/tests/ui/floating_point_powf.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_powf.rs b/tests/ui/floating_point_powf.rs index d749aa2d48a4..499fc0e15e47 100644 --- a/tests/ui/floating_point_powf.rs +++ b/tests/ui/floating_point_powf.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_powf.stderr b/tests/ui/floating_point_powf.stderr index e9693de8fc90..7c9d50db2f78 100644 --- a/tests/ui/floating_point_powf.stderr +++ b/tests/ui/floating_point_powf.stderr @@ -1,5 +1,5 @@ error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:6:13 + --> $DIR/floating_point_powf.rs:7:13 | LL | let _ = 2f32.powf(x); | ^^^^^^^^^^^^ help: consider using: `x.exp2()` @@ -7,43 +7,43 @@ LL | let _ = 2f32.powf(x); = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:7:13 + --> $DIR/floating_point_powf.rs:8:13 | LL | let _ = 2f32.powf(3.1); | ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:8:13 + --> $DIR/floating_point_powf.rs:9:13 | LL | let _ = 2f32.powf(-3.1); | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:9:13 + --> $DIR/floating_point_powf.rs:10:13 | LL | let _ = std::f32::consts::E.powf(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:10:13 + --> $DIR/floating_point_powf.rs:11:13 | LL | let _ = std::f32::consts::E.powf(3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:11:13 + --> $DIR/floating_point_powf.rs:12:13 | LL | let _ = std::f32::consts::E.powf(-3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()` error: square-root of a number can be computed more efficiently and accurately - --> $DIR/floating_point_powf.rs:12:13 + --> $DIR/floating_point_powf.rs:13:13 | LL | let _ = x.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:13:13 + --> $DIR/floating_point_powf.rs:14:13 | LL | let _ = x.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` @@ -51,139 +51,139 @@ LL | let _ = x.powf(1.0 / 3.0); = note: `-D clippy::imprecise-flops` implied by `-D warnings` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:14:13 + --> $DIR/floating_point_powf.rs:15:13 | LL | let _ = (x as f32).powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).cbrt()` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:15:13 + --> $DIR/floating_point_powf.rs:16:13 | LL | let _ = x.powf(3.0); | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:16:13 + --> $DIR/floating_point_powf.rs:17:13 | LL | let _ = x.powf(-2.0); | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:17:13 + --> $DIR/floating_point_powf.rs:18:13 | LL | let _ = x.powf(16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:18:13 + --> $DIR/floating_point_powf.rs:19:13 | LL | let _ = x.powf(-16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:19:13 + --> $DIR/floating_point_powf.rs:20:13 | LL | let _ = (x as f32).powf(-16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(-16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:20:13 + --> $DIR/floating_point_powf.rs:21:13 | LL | let _ = (x as f32).powf(3.0); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(3)` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:21:13 + --> $DIR/floating_point_powf.rs:22:13 | LL | let _ = (1.5_f32 + 1.0).powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(1.5_f32 + 1.0).cbrt()` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:22:13 + --> $DIR/floating_point_powf.rs:23:13 | LL | let _ = 1.5_f64.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.cbrt()` error: square-root of a number can be computed more efficiently and accurately - --> $DIR/floating_point_powf.rs:23:13 + --> $DIR/floating_point_powf.rs:24:13 | LL | let _ = 1.5_f64.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.sqrt()` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:24:13 + --> $DIR/floating_point_powf.rs:25:13 | LL | let _ = 1.5_f64.powf(3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.powi(3)` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:33:13 + --> $DIR/floating_point_powf.rs:34:13 | LL | let _ = 2f64.powf(x); | ^^^^^^^^^^^^ help: consider using: `x.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:34:13 + --> $DIR/floating_point_powf.rs:35:13 | LL | let _ = 2f64.powf(3.1); | ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:35:13 + --> $DIR/floating_point_powf.rs:36:13 | LL | let _ = 2f64.powf(-3.1); | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:36:13 + --> $DIR/floating_point_powf.rs:37:13 | LL | let _ = std::f64::consts::E.powf(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:37:13 + --> $DIR/floating_point_powf.rs:38:13 | LL | let _ = std::f64::consts::E.powf(3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:38:13 + --> $DIR/floating_point_powf.rs:39:13 | LL | let _ = std::f64::consts::E.powf(-3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()` error: square-root of a number can be computed more efficiently and accurately - --> $DIR/floating_point_powf.rs:39:13 + --> $DIR/floating_point_powf.rs:40:13 | LL | let _ = x.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:40:13 + --> $DIR/floating_point_powf.rs:41:13 | LL | let _ = x.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:41:13 + --> $DIR/floating_point_powf.rs:42:13 | LL | let _ = x.powf(3.0); | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:42:13 + --> $DIR/floating_point_powf.rs:43:13 | LL | let _ = x.powf(-2.0); | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:43:13 + --> $DIR/floating_point_powf.rs:44:13 | LL | let _ = x.powf(-2_147_483_648.0); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:44:13 + --> $DIR/floating_point_powf.rs:45:13 | LL | let _ = x.powf(2_147_483_647.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)` diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 5758db7c6c82..884d05fed71b 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let one = 1; @@ -7,7 +8,9 @@ fn main() { let y = 4f32; let _ = x.mul_add(x, y); + let _ = x.mul_add(x, -y); let _ = y.mul_add(y, x); + let _ = y.mul_add(-y, x); let _ = (y as f32).mul_add(y as f32, x); let _ = x.mul_add(x, y).sqrt(); let _ = y.mul_add(y, x).sqrt(); diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index 5926bf1b0004..e6a1c895371b 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let one = 1; @@ -7,7 +8,9 @@ fn main() { let y = 4f32; let _ = x.powi(2) + y; + let _ = x.powi(2) - y; let _ = x + y.powi(2); + let _ = x - y.powi(2); let _ = x + (y as f32).powi(2); let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index a3c74544212b..5df0de1fef22 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -1,5 +1,5 @@ error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:9:13 + --> $DIR/floating_point_powi.rs:10:13 | LL | let _ = x.powi(2) + y; | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` @@ -7,28 +7,40 @@ LL | let _ = x.powi(2) + y; = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:10:13 + --> $DIR/floating_point_powi.rs:11:13 + | +LL | let _ = x.powi(2) - y; + | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, -y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:12:13 | LL | let _ = x + y.powi(2); | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:11:13 + --> $DIR/floating_point_powi.rs:13:13 + | +LL | let _ = x - y.powi(2); + | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(-y, x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:14:13 | LL | let _ = x + (y as f32).powi(2); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y as f32).mul_add(y as f32, x)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:12:13 + --> $DIR/floating_point_powi.rs:15:13 | LL | let _ = (x.powi(2) + y).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:13:13 + --> $DIR/floating_point_powi.rs:16:13 | LL | let _ = (x + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed index aa69781d15a6..e9dd38fe40e6 100644 --- a/tests/ui/for_loop_fixable.fixed +++ b/tests/ui/for_loop_fixable.fixed @@ -1,6 +1,6 @@ // run-rustfix - #![allow(dead_code, unused)] +#![allow(clippy::uninlined_format_args)] use std::collections::*; diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs index 7c063d99511d..534fb4dd4ef2 100644 --- a/tests/ui/for_loop_fixable.rs +++ b/tests/ui/for_loop_fixable.rs @@ -1,6 +1,6 @@ // run-rustfix - #![allow(dead_code, unused)] +#![allow(clippy::uninlined_format_args)] use std::collections::*; diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index 3390111d0a8f..4b2a9297d084 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -1,4 +1,5 @@ #![warn(clippy::for_loops_over_fallibles)] +#![allow(clippy::uninlined_format_args)] fn for_loops_over_fallibles() { let option = Some(1); diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr index 68d2735b040e..f09adccabd1a 100644 --- a/tests/ui/for_loops_over_fallibles.stderr +++ b/tests/ui/for_loops_over_fallibles.stderr @@ -1,5 +1,5 @@ error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:9:14 + --> $DIR/for_loops_over_fallibles.rs:10:14 | LL | for x in option { | ^^^^^^ @@ -8,7 +8,7 @@ LL | for x in option { = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:14:14 + --> $DIR/for_loops_over_fallibles.rs:15:14 | LL | for x in option.iter() { | ^^^^^^ @@ -16,7 +16,7 @@ LL | for x in option.iter() { = help: consider replacing `for x in option.iter()` with `if let Some(x) = option` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:19:14 + --> $DIR/for_loops_over_fallibles.rs:20:14 | LL | for x in result { | ^^^^^^ @@ -24,7 +24,7 @@ LL | for x in result { = help: consider replacing `for x in result` with `if let Ok(x) = result` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:24:14 + --> $DIR/for_loops_over_fallibles.rs:25:14 | LL | for x in result.iter_mut() { | ^^^^^^ @@ -32,7 +32,7 @@ LL | for x in result.iter_mut() { = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:29:14 + --> $DIR/for_loops_over_fallibles.rs:30:14 | LL | for x in result.into_iter() { | ^^^^^^ @@ -40,7 +40,7 @@ LL | for x in result.into_iter() { = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result` error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:33:14 + --> $DIR/for_loops_over_fallibles.rs:34:14 | LL | for x in option.ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | for x in option.ok_or("x not found") { = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loops_over_fallibles.rs:39:14 + --> $DIR/for_loops_over_fallibles.rs:40:14 | LL | for x in v.iter().next() { | ^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | for x in v.iter().next() { = note: `#[deny(clippy::iter_next_loop)]` on by default error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:44:14 + --> $DIR/for_loops_over_fallibles.rs:45:14 | LL | for x in v.iter().next().and(Some(0)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | for x in v.iter().next().and(Some(0)) { = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:48:14 + --> $DIR/for_loops_over_fallibles.rs:49:14 | LL | for x in v.iter().next().ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | for x in v.iter().next().ok_or("x not found") { = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:60:5 + --> $DIR/for_loops_over_fallibles.rs:61:5 | LL | / while let Some(x) = option { LL | | println!("{}", x); @@ -83,7 +83,7 @@ LL | | } = note: `#[deny(clippy::never_loop)]` on by default error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:66:5 + --> $DIR/for_loops_over_fallibles.rs:67:5 | LL | / while let Ok(x) = result { LL | | println!("{}", x); diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index e0c5f692740a..beedf2c1db29 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -1,13 +1,13 @@ // run-rustfix - +#![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args, - clippy::needless_borrow + clippy::needless_borrow, + clippy::uninlined_format_args )] -#![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/tests/ui/format.rs b/tests/ui/format.rs index ff83cd64bf09..e805f1818898 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -1,13 +1,13 @@ // run-rustfix - +#![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args, - clippy::needless_borrow + clippy::needless_borrow, + clippy::uninlined_format_args )] -#![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/tests/ui/format_args.fixed b/tests/ui/format_args.fixed index e1c2d4d70be4..24cf0847dd58 100644 --- a/tests/ui/format_args.fixed +++ b/tests/ui/format_args.fixed @@ -1,10 +1,12 @@ // run-rustfix - -#![allow(unused)] -#![allow(clippy::assertions_on_constants)] -#![allow(clippy::eq_op)] -#![allow(clippy::print_literal)] #![warn(clippy::to_string_in_format_args)] +#![allow(unused)] +#![allow( + clippy::assertions_on_constants, + clippy::eq_op, + clippy::print_literal, + clippy::uninlined_format_args +)] use std::io::{stdout, Write}; use std::ops::Deref; diff --git a/tests/ui/format_args.rs b/tests/ui/format_args.rs index b9a4d66c28ad..753babf0afdc 100644 --- a/tests/ui/format_args.rs +++ b/tests/ui/format_args.rs @@ -1,10 +1,12 @@ // run-rustfix - -#![allow(unused)] -#![allow(clippy::assertions_on_constants)] -#![allow(clippy::eq_op)] -#![allow(clippy::print_literal)] #![warn(clippy::to_string_in_format_args)] +#![allow(unused)] +#![allow( + clippy::assertions_on_constants, + clippy::eq_op, + clippy::print_literal, + clippy::uninlined_format_args +)] use std::io::{stdout, Write}; use std::ops::Deref; diff --git a/tests/ui/format_args.stderr b/tests/ui/format_args.stderr index aa6e3659b43b..68b0bb9e089e 100644 --- a/tests/ui/format_args.stderr +++ b/tests/ui/format_args.stderr @@ -1,5 +1,5 @@ error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:74:72 + --> $DIR/format_args.rs:76:72 | LL | let _ = format!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this @@ -7,133 +7,133 @@ LL | let _ = format!("error: something failed at {}", Location::caller().to_ = note: `-D clippy::to-string-in-format-args` implied by `-D warnings` error: `to_string` applied to a type that implements `Display` in `write!` args - --> $DIR/format_args.rs:78:27 + --> $DIR/format_args.rs:80:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `writeln!` args - --> $DIR/format_args.rs:83:27 + --> $DIR/format_args.rs:85:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> $DIR/format_args.rs:85:63 + --> $DIR/format_args.rs:87:63 | LL | print!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:86:65 + --> $DIR/format_args.rs:88:65 | LL | println!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprint!` args - --> $DIR/format_args.rs:87:64 + --> $DIR/format_args.rs:89:64 | LL | eprint!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprintln!` args - --> $DIR/format_args.rs:88:66 + --> $DIR/format_args.rs:90:66 | LL | eprintln!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format_args!` args - --> $DIR/format_args.rs:89:77 + --> $DIR/format_args.rs:91:77 | LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert!` args - --> $DIR/format_args.rs:90:70 + --> $DIR/format_args.rs:92:70 | LL | assert!(true, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_eq!` args - --> $DIR/format_args.rs:91:73 + --> $DIR/format_args.rs:93:73 | LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_ne!` args - --> $DIR/format_args.rs:92:73 + --> $DIR/format_args.rs:94:73 | LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `panic!` args - --> $DIR/format_args.rs:93:63 + --> $DIR/format_args.rs:95:63 | LL | panic!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:94:20 + --> $DIR/format_args.rs:96:20 | LL | println!("{}", X(1).to_string()); | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:95:20 + --> $DIR/format_args.rs:97:20 | LL | println!("{}", Y(&X(1)).to_string()); | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:96:24 + --> $DIR/format_args.rs:98:24 | LL | println!("{}", Z(1).to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:97:20 + --> $DIR/format_args.rs:99:20 | LL | println!("{}", x.to_string()); | ^^^^^^^^^^^^^ help: use this: `**x` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:98:20 + --> $DIR/format_args.rs:100:20 | LL | println!("{}", x_ref.to_string()); | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:100:39 + --> $DIR/format_args.rs:102:39 | LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:101:52 + --> $DIR/format_args.rs:103:52 | LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:102:39 + --> $DIR/format_args.rs:104:39 | LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:103:52 + --> $DIR/format_args.rs:105:52 | LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:142:38 + --> $DIR/format_args.rs:144:38 | LL | let x = format!("{} {}", a, b.to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:156:24 + --> $DIR/format_args.rs:158:24 | LL | println!("{}", original[..10].to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]` diff --git a/tests/ui/format_args_unfixable.rs b/tests/ui/format_args_unfixable.rs index b24ddf7321e4..eb0ac15bfbf1 100644 --- a/tests/ui/format_args_unfixable.rs +++ b/tests/ui/format_args_unfixable.rs @@ -1,7 +1,5 @@ -#![allow(clippy::assertions_on_constants)] -#![allow(clippy::eq_op)] -#![warn(clippy::format_in_format_args)] -#![warn(clippy::to_string_in_format_args)] +#![warn(clippy::format_in_format_args, clippy::to_string_in_format_args)] +#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::uninlined_format_args)] use std::io::{stdout, Error, ErrorKind, Write}; use std::ops::Deref; diff --git a/tests/ui/format_args_unfixable.stderr b/tests/ui/format_args_unfixable.stderr index 37a6afb1ba7b..b291d475ad90 100644 --- a/tests/ui/format_args_unfixable.stderr +++ b/tests/ui/format_args_unfixable.stderr @@ -1,5 +1,5 @@ error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:27:5 + --> $DIR/format_args_unfixable.rs:25:5 | LL | println!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | println!("error: {}", format!("something failed at {}", Location::calle = note: `-D clippy::format-in-format-args` implied by `-D warnings` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:28:5 + --> $DIR/format_args_unfixable.rs:26:5 | LL | println!("{}: {}", error, format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | println!("{}: {}", error, format!("something failed at {}", Location::c = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:29:5 + --> $DIR/format_args_unfixable.rs:27:5 | LL | println!("{:?}: {}", error, format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | println!("{:?}: {}", error, format!("something failed at {}", Location: = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:30:5 + --> $DIR/format_args_unfixable.rs:28:5 | LL | println!("{{}}: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | println!("{{}}: {}", format!("something failed at {}", Location::caller = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:31:5 + --> $DIR/format_args_unfixable.rs:29:5 | LL | println!(r#"error: "{}""#, format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | println!(r#"error: "{}""#, format!("something failed at {}", Location:: = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:32:5 + --> $DIR/format_args_unfixable.rs:30:5 | LL | println!("error: {}", format!(r#"something failed at "{}""#, Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | println!("error: {}", format!(r#"something failed at "{}""#, Location:: = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:33:5 + --> $DIR/format_args_unfixable.rs:31:5 | LL | println!("error: {}", format!("something failed at {} {0}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | println!("error: {}", format!("something failed at {} {0}", Location::c = help: or consider changing `format!` to `format_args!` error: `format!` in `format!` args - --> $DIR/format_args_unfixable.rs:34:13 + --> $DIR/format_args_unfixable.rs:32:13 | LL | let _ = format!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = format!("error: {}", format!("something failed at {}", Location = help: or consider changing `format!` to `format_args!` error: `format!` in `write!` args - --> $DIR/format_args_unfixable.rs:35:13 + --> $DIR/format_args_unfixable.rs:33:13 | LL | let _ = write!( | _____________^ @@ -86,7 +86,7 @@ LL | | ); = help: or consider changing `format!` to `format_args!` error: `format!` in `writeln!` args - --> $DIR/format_args_unfixable.rs:40:13 + --> $DIR/format_args_unfixable.rs:38:13 | LL | let _ = writeln!( | _____________^ @@ -100,7 +100,7 @@ LL | | ); = help: or consider changing `format!` to `format_args!` error: `format!` in `print!` args - --> $DIR/format_args_unfixable.rs:45:5 + --> $DIR/format_args_unfixable.rs:43:5 | LL | print!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | print!("error: {}", format!("something failed at {}", Location::caller( = help: or consider changing `format!` to `format_args!` error: `format!` in `eprint!` args - --> $DIR/format_args_unfixable.rs:46:5 + --> $DIR/format_args_unfixable.rs:44:5 | LL | eprint!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | eprint!("error: {}", format!("something failed at {}", Location::caller = help: or consider changing `format!` to `format_args!` error: `format!` in `eprintln!` args - --> $DIR/format_args_unfixable.rs:47:5 + --> $DIR/format_args_unfixable.rs:45:5 | LL | eprintln!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | eprintln!("error: {}", format!("something failed at {}", Location::call = help: or consider changing `format!` to `format_args!` error: `format!` in `format_args!` args - --> $DIR/format_args_unfixable.rs:48:13 + --> $DIR/format_args_unfixable.rs:46:13 | LL | let _ = format_args!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | let _ = format_args!("error: {}", format!("something failed at {}", Loc = help: or consider changing `format!` to `format_args!` error: `format!` in `assert!` args - --> $DIR/format_args_unfixable.rs:49:5 + --> $DIR/format_args_unfixable.rs:47:5 | LL | assert!(true, "error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | assert!(true, "error: {}", format!("something failed at {}", Location:: = help: or consider changing `format!` to `format_args!` error: `format!` in `assert_eq!` args - --> $DIR/format_args_unfixable.rs:50:5 + --> $DIR/format_args_unfixable.rs:48:5 | LL | assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | assert_eq!(0, 0, "error: {}", format!("something failed at {}", Locatio = help: or consider changing `format!` to `format_args!` error: `format!` in `assert_ne!` args - --> $DIR/format_args_unfixable.rs:51:5 + --> $DIR/format_args_unfixable.rs:49:5 | LL | assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -163,7 +163,7 @@ LL | assert_ne!(0, 0, "error: {}", format!("something failed at {}", Locatio = help: or consider changing `format!` to `format_args!` error: `format!` in `panic!` args - --> $DIR/format_args_unfixable.rs:52:5 + --> $DIR/format_args_unfixable.rs:50:5 | LL | panic!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/functions.rs b/tests/ui/functions.rs index 5521870eaecf..18149bfbc3fe 100644 --- a/tests/ui/functions.rs +++ b/tests/ui/functions.rs @@ -1,6 +1,6 @@ #![warn(clippy::all)] -#![allow(dead_code)] -#![allow(unused_unsafe, clippy::missing_safety_doc)] +#![allow(dead_code, unused_unsafe)] +#![allow(clippy::missing_safety_doc, clippy::uninlined_format_args)] // TOO_MANY_ARGUMENTS fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {} diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed index fa564e23cd27..e7b9a78c5dbc 100644 --- a/tests/ui/identity_op.fixed +++ b/tests/ui/identity_op.fixed @@ -1,13 +1,13 @@ // run-rustfix - #![warn(clippy::identity_op)] +#![allow(unused)] #![allow( clippy::eq_op, clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref, clippy::double_parens, - unused + clippy::uninlined_format_args )] use std::fmt::Write as _; diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 3d06d2a73b62..9a435cdbb753 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -1,13 +1,13 @@ // run-rustfix - #![warn(clippy::identity_op)] +#![allow(unused)] #![allow( clippy::eq_op, clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref, clippy::double_parens, - unused + clippy::uninlined_format_args )] use std::fmt::Write as _; diff --git a/tests/ui/implicit_saturating_add.fixed b/tests/ui/implicit_saturating_add.fixed new file mode 100644 index 000000000000..7d363d59a6f0 --- /dev/null +++ b/tests/ui/implicit_saturating_add.fixed @@ -0,0 +1,106 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::implicit_saturating_add)] + +fn main() { + let mut u_8: u8 = 255; + let mut u_16: u16 = 500; + let mut u_32: u32 = 7000; + let mut u_64: u64 = 7000; + let mut i_8: i8 = 30; + let mut i_16: i16 = 500; + let mut i_32: i32 = 7000; + let mut i_64: i64 = 7000; + + if i_8 < 42 { + i_8 += 1; + } + if i_8 != 42 { + i_8 += 1; + } + + u_8 = u_8.saturating_add(1); + + u_8 = u_8.saturating_add(1); + + if u_8 < 15 { + u_8 += 1; + } + + u_16 = u_16.saturating_add(1); + + u_16 = u_16.saturating_add(1); + + u_16 = u_16.saturating_add(1); + + u_32 = u_32.saturating_add(1); + + u_32 = u_32.saturating_add(1); + + u_32 = u_32.saturating_add(1); + + u_64 = u_64.saturating_add(1); + + u_64 = u_64.saturating_add(1); + + u_64 = u_64.saturating_add(1); + + i_8 = i_8.saturating_add(1); + + i_8 = i_8.saturating_add(1); + + i_8 = i_8.saturating_add(1); + + i_16 = i_16.saturating_add(1); + + i_16 = i_16.saturating_add(1); + + i_16 = i_16.saturating_add(1); + + i_32 = i_32.saturating_add(1); + + i_32 = i_32.saturating_add(1); + + i_32 = i_32.saturating_add(1); + + i_64 = i_64.saturating_add(1); + + i_64 = i_64.saturating_add(1); + + i_64 = i_64.saturating_add(1); + + if i_64 < 42 { + i_64 += 1; + } + + if 42 > i_64 { + i_64 += 1; + } + + let a = 12; + let mut b = 8; + + if a < u8::MAX { + b += 1; + } + + if u8::MAX > a { + b += 1; + } + + if u_32 < u32::MAX { + u_32 += 1; + } else { + println!("don't lint this"); + } + + if u_32 < u32::MAX { + println!("don't lint this"); + u_32 += 1; + } + + if u_32 < 42 { + println!("brace yourself!"); + } else {u_32 = u_32.saturating_add(1); } +} diff --git a/tests/ui/implicit_saturating_add.rs b/tests/ui/implicit_saturating_add.rs new file mode 100644 index 000000000000..31a5916277fa --- /dev/null +++ b/tests/ui/implicit_saturating_add.rs @@ -0,0 +1,154 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::implicit_saturating_add)] + +fn main() { + let mut u_8: u8 = 255; + let mut u_16: u16 = 500; + let mut u_32: u32 = 7000; + let mut u_64: u64 = 7000; + let mut i_8: i8 = 30; + let mut i_16: i16 = 500; + let mut i_32: i32 = 7000; + let mut i_64: i64 = 7000; + + if i_8 < 42 { + i_8 += 1; + } + if i_8 != 42 { + i_8 += 1; + } + + if u_8 != u8::MAX { + u_8 += 1; + } + + if u_8 < u8::MAX { + u_8 += 1; + } + + if u_8 < 15 { + u_8 += 1; + } + + if u_16 != u16::MAX { + u_16 += 1; + } + + if u_16 < u16::MAX { + u_16 += 1; + } + + if u16::MAX > u_16 { + u_16 += 1; + } + + if u_32 != u32::MAX { + u_32 += 1; + } + + if u_32 < u32::MAX { + u_32 += 1; + } + + if u32::MAX > u_32 { + u_32 += 1; + } + + if u_64 != u64::MAX { + u_64 += 1; + } + + if u_64 < u64::MAX { + u_64 += 1; + } + + if u64::MAX > u_64 { + u_64 += 1; + } + + if i_8 != i8::MAX { + i_8 += 1; + } + + if i_8 < i8::MAX { + i_8 += 1; + } + + if i8::MAX > i_8 { + i_8 += 1; + } + + if i_16 != i16::MAX { + i_16 += 1; + } + + if i_16 < i16::MAX { + i_16 += 1; + } + + if i16::MAX > i_16 { + i_16 += 1; + } + + if i_32 != i32::MAX { + i_32 += 1; + } + + if i_32 < i32::MAX { + i_32 += 1; + } + + if i32::MAX > i_32 { + i_32 += 1; + } + + if i_64 != i64::MAX { + i_64 += 1; + } + + if i_64 < i64::MAX { + i_64 += 1; + } + + if i64::MAX > i_64 { + i_64 += 1; + } + + if i_64 < 42 { + i_64 += 1; + } + + if 42 > i_64 { + i_64 += 1; + } + + let a = 12; + let mut b = 8; + + if a < u8::MAX { + b += 1; + } + + if u8::MAX > a { + b += 1; + } + + if u_32 < u32::MAX { + u_32 += 1; + } else { + println!("don't lint this"); + } + + if u_32 < u32::MAX { + println!("don't lint this"); + u_32 += 1; + } + + if u_32 < 42 { + println!("brace yourself!"); + } else if u_32 < u32::MAX { + u_32 += 1; + } +} diff --git a/tests/ui/implicit_saturating_add.stderr b/tests/ui/implicit_saturating_add.stderr new file mode 100644 index 000000000000..42ae1b488853 --- /dev/null +++ b/tests/ui/implicit_saturating_add.stderr @@ -0,0 +1,197 @@ +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:23:5 + | +LL | / if u_8 != u8::MAX { +LL | | u_8 += 1; +LL | | } + | |_____^ help: use instead: `u_8 = u_8.saturating_add(1);` + | + = note: `-D clippy::implicit-saturating-add` implied by `-D warnings` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:27:5 + | +LL | / if u_8 < u8::MAX { +LL | | u_8 += 1; +LL | | } + | |_____^ help: use instead: `u_8 = u_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:35:5 + | +LL | / if u_16 != u16::MAX { +LL | | u_16 += 1; +LL | | } + | |_____^ help: use instead: `u_16 = u_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:39:5 + | +LL | / if u_16 < u16::MAX { +LL | | u_16 += 1; +LL | | } + | |_____^ help: use instead: `u_16 = u_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:43:5 + | +LL | / if u16::MAX > u_16 { +LL | | u_16 += 1; +LL | | } + | |_____^ help: use instead: `u_16 = u_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:47:5 + | +LL | / if u_32 != u32::MAX { +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `u_32 = u_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:51:5 + | +LL | / if u_32 < u32::MAX { +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `u_32 = u_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:55:5 + | +LL | / if u32::MAX > u_32 { +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `u_32 = u_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:59:5 + | +LL | / if u_64 != u64::MAX { +LL | | u_64 += 1; +LL | | } + | |_____^ help: use instead: `u_64 = u_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:63:5 + | +LL | / if u_64 < u64::MAX { +LL | | u_64 += 1; +LL | | } + | |_____^ help: use instead: `u_64 = u_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:67:5 + | +LL | / if u64::MAX > u_64 { +LL | | u_64 += 1; +LL | | } + | |_____^ help: use instead: `u_64 = u_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:71:5 + | +LL | / if i_8 != i8::MAX { +LL | | i_8 += 1; +LL | | } + | |_____^ help: use instead: `i_8 = i_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:75:5 + | +LL | / if i_8 < i8::MAX { +LL | | i_8 += 1; +LL | | } + | |_____^ help: use instead: `i_8 = i_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:79:5 + | +LL | / if i8::MAX > i_8 { +LL | | i_8 += 1; +LL | | } + | |_____^ help: use instead: `i_8 = i_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:83:5 + | +LL | / if i_16 != i16::MAX { +LL | | i_16 += 1; +LL | | } + | |_____^ help: use instead: `i_16 = i_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:87:5 + | +LL | / if i_16 < i16::MAX { +LL | | i_16 += 1; +LL | | } + | |_____^ help: use instead: `i_16 = i_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:91:5 + | +LL | / if i16::MAX > i_16 { +LL | | i_16 += 1; +LL | | } + | |_____^ help: use instead: `i_16 = i_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:95:5 + | +LL | / if i_32 != i32::MAX { +LL | | i_32 += 1; +LL | | } + | |_____^ help: use instead: `i_32 = i_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:99:5 + | +LL | / if i_32 < i32::MAX { +LL | | i_32 += 1; +LL | | } + | |_____^ help: use instead: `i_32 = i_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:103:5 + | +LL | / if i32::MAX > i_32 { +LL | | i_32 += 1; +LL | | } + | |_____^ help: use instead: `i_32 = i_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:107:5 + | +LL | / if i_64 != i64::MAX { +LL | | i_64 += 1; +LL | | } + | |_____^ help: use instead: `i_64 = i_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:111:5 + | +LL | / if i_64 < i64::MAX { +LL | | i_64 += 1; +LL | | } + | |_____^ help: use instead: `i_64 = i_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:115:5 + | +LL | / if i64::MAX > i_64 { +LL | | i_64 += 1; +LL | | } + | |_____^ help: use instead: `i_64 = i_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:151:12 + | +LL | } else if u_32 < u32::MAX { + | ____________^ +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `{u_32 = u_32.saturating_add(1); }` + +error: aborting due to 24 previous errors + diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/tests/ui/index_refutable_slice/if_let_slice_binding.rs index c2c0c520dc62..0a3374d11b03 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,4 +1,5 @@ #![deny(clippy::index_refutable_slice)] +#![allow(clippy::uninlined_format_args)] enum SomeEnum { One(T), diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.stderr b/tests/ui/index_refutable_slice/if_let_slice_binding.stderr index a607df9b8766..0a13ac1354e5 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.stderr +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:13:17 + --> $DIR/if_let_slice_binding.rs:14:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -19,7 +19,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:19:17 + --> $DIR/if_let_slice_binding.rs:20:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -34,7 +34,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:25:17 + --> $DIR/if_let_slice_binding.rs:26:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -50,7 +50,7 @@ LL ~ println!("{}", slice_0); | error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:32:26 + --> $DIR/if_let_slice_binding.rs:33:26 | LL | if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped { | ^^^^^ @@ -65,7 +65,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:39:29 + --> $DIR/if_let_slice_binding.rs:40:29 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ @@ -80,7 +80,7 @@ LL | println!("{} -> {}", a_2, b[1]); | ~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:39:38 + --> $DIR/if_let_slice_binding.rs:40:38 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ @@ -95,7 +95,7 @@ LL | println!("{} -> {}", a[2], b_1); | ~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:46:21 + --> $DIR/if_let_slice_binding.rs:47:21 | LL | if let Some(ref slice) = slice { | ^^^^^ @@ -110,7 +110,7 @@ LL | println!("{:?}", slice_1); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:54:17 + --> $DIR/if_let_slice_binding.rs:55:17 | LL | if let Some(slice) = &slice { | ^^^^^ @@ -125,7 +125,7 @@ LL | println!("{:?}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:123:17 + --> $DIR/if_let_slice_binding.rs:124:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ @@ -140,7 +140,7 @@ LL | println!("This is awesome! {}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:130:17 + --> $DIR/if_let_slice_binding.rs:131:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index a1e5fad0c621..622644f675d3 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { x * x < 64 diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index ba277e36339a..b911163f715e 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -1,29 +1,29 @@ error: infinite iteration detected - --> $DIR/infinite_iter.rs:9:5 + --> $DIR/infinite_iter.rs:11:5 | LL | repeat(0_u8).collect::>(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/infinite_iter.rs:7:8 + --> $DIR/infinite_iter.rs:9:8 | LL | #[deny(clippy::infinite_iter)] | ^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:10:5 + --> $DIR/infinite_iter.rs:12:5 | LL | (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:11:5 + --> $DIR/infinite_iter.rs:13:5 | LL | (0..8_u64).chain(0..).max(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:16:5 + --> $DIR/infinite_iter.rs:18:5 | LL | / (0..8_u32) LL | | .rev() @@ -33,37 +33,37 @@ LL | | .for_each(|x| println!("{}", x)); // infinite iter | |________________________________________^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:22:5 + --> $DIR/infinite_iter.rs:24:5 | LL | (0_usize..).flat_map(|x| 0..x).product::(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:23:5 + --> $DIR/infinite_iter.rs:25:5 | LL | (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:30:5 + --> $DIR/infinite_iter.rs:32:5 | LL | (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/infinite_iter.rs:28:8 + --> $DIR/infinite_iter.rs:30:8 | LL | #[deny(clippy::maybe_infinite_iter)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:31:5 + --> $DIR/infinite_iter.rs:33:5 | LL | repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:32:5 + --> $DIR/infinite_iter.rs:34:5 | LL | / (1..) LL | | .scan(0, |state, x| { @@ -74,31 +74,31 @@ LL | | .min(); // maybe infinite iter | |______________^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:38:5 + --> $DIR/infinite_iter.rs:40:5 | LL | (0..).find(|x| *x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:39:5 + --> $DIR/infinite_iter.rs:41:5 | LL | (0..).position(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:40:5 + --> $DIR/infinite_iter.rs:42:5 | LL | (0..).any(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:41:5 + --> $DIR/infinite_iter.rs:43:5 | LL | (0..).all(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:63:31 + --> $DIR/infinite_iter.rs:65:31 | LL | let _: HashSet = (0..).collect(); // Infinite iter | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/issue_2356.fixed b/tests/ui/issue_2356.fixed index 942e99fa8787..a73ee0fb2e59 100644 --- a/tests/ui/issue_2356.fixed +++ b/tests/ui/issue_2356.fixed @@ -1,6 +1,7 @@ // run-rustfix #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] +#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; diff --git a/tests/ui/issue_2356.rs b/tests/ui/issue_2356.rs index b000234ea596..9dd9069609b1 100644 --- a/tests/ui/issue_2356.rs +++ b/tests/ui/issue_2356.rs @@ -1,6 +1,7 @@ // run-rustfix #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] +#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; diff --git a/tests/ui/issue_2356.stderr b/tests/ui/issue_2356.stderr index 4e3ff7522e0b..a24b0b32e470 100644 --- a/tests/ui/issue_2356.stderr +++ b/tests/ui/issue_2356.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> $DIR/issue_2356.rs:17:9 + --> $DIR/issue_2356.rs:18:9 | LL | while let Some(e) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` diff --git a/tests/ui/issue_4266.rs b/tests/ui/issue_4266.rs index d9d48189bd74..8e0620e52b65 100644 --- a/tests/ui/issue_4266.rs +++ b/tests/ui/issue_4266.rs @@ -1,4 +1,5 @@ #![allow(dead_code)] +#![allow(clippy::uninlined_format_args)] async fn sink1<'a>(_: &'a str) {} // lint async fn sink1_elided(_: &str) {} // ok diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr index 240f4bcc38fd..fb2a93c9580e 100644 --- a/tests/ui/issue_4266.stderr +++ b/tests/ui/issue_4266.stderr @@ -1,5 +1,5 @@ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/issue_4266.rs:3:1 + --> $DIR/issue_4266.rs:4:1 | LL | async fn sink1<'a>(_: &'a str) {} // lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | async fn sink1<'a>(_: &'a str) {} // lint = note: `-D clippy::needless-lifetimes` implied by `-D warnings` error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/issue_4266.rs:7:1 + --> $DIR/issue_4266.rs:8:1 | LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: methods called `new` usually take no `self` - --> $DIR/issue_4266.rs:27:22 + --> $DIR/issue_4266.rs:28:22 | LL | pub async fn new(&mut self) -> Self { | ^^^^^^^^^ diff --git a/tests/ui/item_after_statement.rs b/tests/ui/item_after_statement.rs index d439ca1e4e1a..5e92dcab1f5a 100644 --- a/tests/ui/item_after_statement.rs +++ b/tests/ui/item_after_statement.rs @@ -1,4 +1,5 @@ #![warn(clippy::items_after_statements)] +#![allow(clippy::uninlined_format_args)] fn ok() { fn foo() { diff --git a/tests/ui/item_after_statement.stderr b/tests/ui/item_after_statement.stderr index ab4a6374c73c..2523c53ac53a 100644 --- a/tests/ui/item_after_statement.stderr +++ b/tests/ui/item_after_statement.stderr @@ -1,5 +1,5 @@ error: adding items after statements is confusing, since items exist from the start of the scope - --> $DIR/item_after_statement.rs:12:5 + --> $DIR/item_after_statement.rs:13:5 | LL | / fn foo() { LL | | println!("foo"); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::items-after-statements` implied by `-D warnings` error: adding items after statements is confusing, since items exist from the start of the scope - --> $DIR/item_after_statement.rs:19:5 + --> $DIR/item_after_statement.rs:20:5 | LL | / fn foo() { LL | | println!("foo"); @@ -17,7 +17,7 @@ LL | | } | |_____^ error: adding items after statements is confusing, since items exist from the start of the scope - --> $DIR/item_after_statement.rs:32:13 + --> $DIR/item_after_statement.rs:33:13 | LL | / fn say_something() { LL | | println!("something"); diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed index 65598f1eaccc..26e3b8f63e70 100644 --- a/tests/ui/manual_assert.edition2018.fixed +++ b/tests/ui/manual_assert.edition2018.fixed @@ -4,7 +4,8 @@ // run-rustfix #![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)] macro_rules! one { () => { @@ -50,3 +51,14 @@ fn main() { assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); } + +fn issue7730(a: u8) { + // Suggestion should preserve comment + // comment +/* this is a + multiline + comment */ +/// Doc comment +// comment after `panic!` +assert!(!(a > 2), "panic with comment"); +} diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index a0f31afd6ebf..7718588fdf6f 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -1,68 +1,124 @@ error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:30:5 + --> $DIR/manual_assert.rs:31:5 | LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);` + | |_____^ | = note: `-D clippy::manual-assert` implied by `-D warnings` +help: try instead + | +LL | assert!(a.is_empty(), "qaqaq{:?}", a); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:33:5 + --> $DIR/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");` + | |_____^ + | +help: try instead + | +LL | assert!(a.is_empty(), "qwqwq"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:50:5 + --> $DIR/manual_assert.rs:51:5 | LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ help: try: `assert!(!b.is_empty(), "panic1");` + | |_____^ + | +help: try instead + | +LL | assert!(!b.is_empty(), "panic1"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:53:5 + --> $DIR/manual_assert.rs:54:5 | LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:56:5 + --> $DIR/manual_assert.rs:57:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:59:5 + --> $DIR/manual_assert.rs:60:5 | LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:62:5 + --> $DIR/manual_assert.rs:63:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:65:5 + --> $DIR/manual_assert.rs:66:5 | LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ help: try: `assert!(!a.is_empty(), "with expansion {}", one!());` + | |_____^ + | +help: try instead + | +LL | assert!(!a.is_empty(), "with expansion {}", one!()); + | -error: aborting due to 8 previous errors +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:73:5 + | +LL | / if a > 2 { +LL | | // comment +LL | | /* this is a +LL | | multiline +... | +LL | | panic!("panic with comment") // comment after `panic!` +LL | | } + | |_____^ + | +help: try instead + | +LL | assert!(!(a > 2), "panic with comment"); + | + +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed index 65598f1eaccc..26e3b8f63e70 100644 --- a/tests/ui/manual_assert.edition2021.fixed +++ b/tests/ui/manual_assert.edition2021.fixed @@ -4,7 +4,8 @@ // run-rustfix #![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)] macro_rules! one { () => { @@ -50,3 +51,14 @@ fn main() { assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); } + +fn issue7730(a: u8) { + // Suggestion should preserve comment + // comment +/* this is a + multiline + comment */ +/// Doc comment +// comment after `panic!` +assert!(!(a > 2), "panic with comment"); +} diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index a0f31afd6ebf..7718588fdf6f 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -1,68 +1,124 @@ error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:30:5 + --> $DIR/manual_assert.rs:31:5 | LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);` + | |_____^ | = note: `-D clippy::manual-assert` implied by `-D warnings` +help: try instead + | +LL | assert!(a.is_empty(), "qaqaq{:?}", a); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:33:5 + --> $DIR/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");` + | |_____^ + | +help: try instead + | +LL | assert!(a.is_empty(), "qwqwq"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:50:5 + --> $DIR/manual_assert.rs:51:5 | LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ help: try: `assert!(!b.is_empty(), "panic1");` + | |_____^ + | +help: try instead + | +LL | assert!(!b.is_empty(), "panic1"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:53:5 + --> $DIR/manual_assert.rs:54:5 | LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:56:5 + --> $DIR/manual_assert.rs:57:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:59:5 + --> $DIR/manual_assert.rs:60:5 | LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:62:5 + --> $DIR/manual_assert.rs:63:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:65:5 + --> $DIR/manual_assert.rs:66:5 | LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ help: try: `assert!(!a.is_empty(), "with expansion {}", one!());` + | |_____^ + | +help: try instead + | +LL | assert!(!a.is_empty(), "with expansion {}", one!()); + | -error: aborting due to 8 previous errors +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:73:5 + | +LL | / if a > 2 { +LL | | // comment +LL | | /* this is a +LL | | multiline +... | +LL | | panic!("panic with comment") // comment after `panic!` +LL | | } + | |_____^ + | +help: try instead + | +LL | assert!(!(a > 2), "panic with comment"); + | + +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_assert.fixed b/tests/ui/manual_assert.fixed deleted file mode 100644 index a2393674fe61..000000000000 --- a/tests/ui/manual_assert.fixed +++ /dev/null @@ -1,45 +0,0 @@ -// revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 -// run-rustfix - -#![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] - -fn main() { - let a = vec![1, 2, 3]; - let c = Some(2); - if !a.is_empty() - && a.len() == 3 - && c.is_some() - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - { - panic!("qaqaq{:?}", a); - } - assert!(a.is_empty(), "qaqaq{:?}", a); - assert!(a.is_empty(), "qwqwq"); - if a.len() == 3 { - println!("qwq"); - println!("qwq"); - println!("qwq"); - } - if let Some(b) = c { - panic!("orz {}", b); - } - if a.len() == 3 { - panic!("qaqaq"); - } else { - println!("qwq"); - } - let b = vec![1, 2, 3]; - assert!(!b.is_empty(), "panic1"); - assert!(!(b.is_empty() && a.is_empty()), "panic2"); - assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - assert!(!(b.is_empty() || a.is_empty()), "panic4"); - assert!(!(a.is_empty() || !b.is_empty()), "panic5"); -} diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index 4d2706dd6211..8c37753071df 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -4,7 +4,8 @@ // run-rustfix #![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)] macro_rules! one { () => { @@ -66,3 +67,15 @@ fn main() { panic!("with expansion {}", one!()) } } + +fn issue7730(a: u8) { + // Suggestion should preserve comment + if a > 2 { + // comment + /* this is a + multiline + comment */ + /// Doc comment + panic!("panic with comment") // comment after `panic!` + } +} diff --git a/tests/ui/manual_bits.fixed b/tests/ui/manual_bits.fixed index 386360dbdcdb..e7f8cd878ca7 100644 --- a/tests/ui/manual_bits.fixed +++ b/tests/ui/manual_bits.fixed @@ -6,7 +6,8 @@ clippy::useless_conversion, path_statements, unused_must_use, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::unnecessary_cast )] use std::mem::{size_of, size_of_val}; diff --git a/tests/ui/manual_bits.rs b/tests/ui/manual_bits.rs index 62638f047eb0..7b1d15495287 100644 --- a/tests/ui/manual_bits.rs +++ b/tests/ui/manual_bits.rs @@ -6,7 +6,8 @@ clippy::useless_conversion, path_statements, unused_must_use, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::unnecessary_cast )] use std::mem::{size_of, size_of_val}; diff --git a/tests/ui/manual_bits.stderr b/tests/ui/manual_bits.stderr index 69c591a203d3..652fafbc41d8 100644 --- a/tests/ui/manual_bits.stderr +++ b/tests/ui/manual_bits.stderr @@ -1,5 +1,5 @@ error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:15:5 + --> $DIR/manual_bits.rs:16:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` @@ -7,169 +7,169 @@ LL | size_of::() * 8; = note: `-D clippy::manual-bits` implied by `-D warnings` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:16:5 + --> $DIR/manual_bits.rs:17:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:17:5 + --> $DIR/manual_bits.rs:18:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:18:5 + --> $DIR/manual_bits.rs:19:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:19:5 + --> $DIR/manual_bits.rs:20:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:20:5 + --> $DIR/manual_bits.rs:21:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:22:5 + --> $DIR/manual_bits.rs:23:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:23:5 + --> $DIR/manual_bits.rs:24:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:24:5 + --> $DIR/manual_bits.rs:25:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:25:5 + --> $DIR/manual_bits.rs:26:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:26:5 + --> $DIR/manual_bits.rs:27:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:27:5 + --> $DIR/manual_bits.rs:28:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:29:5 + --> $DIR/manual_bits.rs:30:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:30:5 + --> $DIR/manual_bits.rs:31:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:31:5 + --> $DIR/manual_bits.rs:32:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:32:5 + --> $DIR/manual_bits.rs:33:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:33:5 + --> $DIR/manual_bits.rs:34:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:34:5 + --> $DIR/manual_bits.rs:35:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:36:5 + --> $DIR/manual_bits.rs:37:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:37:5 + --> $DIR/manual_bits.rs:38:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:38:5 + --> $DIR/manual_bits.rs:39:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:39:5 + --> $DIR/manual_bits.rs:40:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:40:5 + --> $DIR/manual_bits.rs:41:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:41:5 + --> $DIR/manual_bits.rs:42:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:51:5 + --> $DIR/manual_bits.rs:52:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:55:18 + --> $DIR/manual_bits.rs:56:18 | LL | let _: u32 = (size_of::() * 8) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:56:18 + --> $DIR/manual_bits.rs:57:18 | LL | let _: u32 = (size_of::() * 8).try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:57:13 + --> $DIR/manual_bits.rs:58:13 | LL | let _ = (size_of::() * 8).pow(5); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:58:14 + --> $DIR/manual_bits.rs:59:14 | LL | let _ = &(size_of::() * 8); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs new file mode 100644 index 000000000000..54fd888af99f --- /dev/null +++ b/tests/ui/manual_clamp.rs @@ -0,0 +1,304 @@ +#![warn(clippy::manual_clamp)] +#![allow( + unused, + dead_code, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::if_same_then_else +)] + +use std::cmp::{max as cmp_max, min as cmp_min}; + +const CONST_MAX: i32 = 10; +const CONST_MIN: i32 = 4; + +const CONST_F64_MAX: f64 = 10.0; +const CONST_F64_MIN: f64 = 4.0; + +fn main() { + let (input, min, max) = (0, -2, 3); + // Lint + let x0 = if max < input { + max + } else if min > input { + min + } else { + input + }; + + let x1 = if input > max { + max + } else if input < min { + min + } else { + input + }; + + let x2 = if input < min { + min + } else if input > max { + max + } else { + input + }; + + let x3 = if min > input { + min + } else if max < input { + max + } else { + input + }; + + let x4 = input.max(min).min(max); + + let x5 = input.min(max).max(min); + + let x6 = match input { + x if x > max => max, + x if x < min => min, + x => x, + }; + + let x7 = match input { + x if x < min => min, + x if x > max => max, + x => x, + }; + + let x8 = match input { + x if max < x => max, + x if min > x => min, + x => x, + }; + + let mut x9 = input; + if x9 < min { + x9 = min; + } + if x9 > max { + x9 = max; + } + + let x10 = match input { + x if min > x => min, + x if max < x => max, + x => x, + }; + + let mut x11 = input; + let _ = 1; + if x11 > max { + x11 = max; + } + if x11 < min { + x11 = min; + } + + let mut x12 = input; + if min > x12 { + x12 = min; + } + if max < x12 { + x12 = max; + } + + let mut x13 = input; + if max < x13 { + x13 = max; + } + if min > x13 { + x13 = min; + } + + let x14 = if input > CONST_MAX { + CONST_MAX + } else if input < CONST_MIN { + CONST_MIN + } else { + input + }; + { + let (input, min, max) = (0.0f64, -2.0, 3.0); + let x15 = if input > max { + max + } else if input < min { + min + } else { + input + }; + } + { + let input: i32 = cmp_min_max(1); + // These can only be detected if exactly one of the arguments to the inner function is const. + let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); + let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); + let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); + let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); + let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); + let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); + let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); + let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); + let input: f64 = cmp_min_max(1) as f64; + let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); + let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); + let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); + let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); + let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); + let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); + let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); + let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); + } + let mut x32 = input; + if x32 < min { + x32 = min; + } else if x32 > max { + x32 = max; + } + + // It's important this be the last set of statements + let mut x33 = input; + if max < x33 { + x33 = max; + } + if min > x33 { + x33 = min; + } +} + +// This code intentionally nonsense. +fn no_lint() { + let (input, min, max) = (0, -2, 3); + let x0 = if max < input { + max + } else if min > input { + max + } else { + min + }; + + let x1 = if input > max { + max + } else if input > min { + min + } else { + max + }; + + let x2 = if max < min { + min + } else if input > max { + input + } else { + input + }; + + let x3 = if min > input { + input + } else if max < input { + max + } else { + max + }; + + let x6 = match input { + x if x < max => x, + x if x < min => x, + x => x, + }; + + let x7 = match input { + x if x < min => max, + x if x > max => min, + x => x, + }; + + let x8 = match input { + x if max > x => max, + x if min > x => min, + x => x, + }; + + let mut x9 = input; + if x9 > min { + x9 = min; + } + if x9 > max { + x9 = max; + } + + let x10 = match input { + x if min > x => min, + x if max < x => max, + x => min, + }; + + let mut x11 = input; + if x11 > max { + x11 = min; + } + if x11 < min { + x11 = max; + } + + let mut x12 = input; + if min > x12 { + x12 = max * 3; + } + if max < x12 { + x12 = min; + } + + let mut x13 = input; + if max < x13 { + let x13 = max; + } + if min > x13 { + x13 = min; + } + let mut x14 = input; + if x14 < min { + x14 = 3; + } else if x14 > max { + x14 = max; + } + { + let input: i32 = cmp_min_max(1); + // These can only be detected if exactly one of the arguments to the inner function is const. + let x16 = cmp_max(cmp_max(input, CONST_MAX), CONST_MIN); + let x17 = cmp_min(cmp_min(input, CONST_MIN), CONST_MAX); + let x18 = cmp_max(CONST_MIN, cmp_max(input, CONST_MAX)); + let x19 = cmp_min(CONST_MAX, cmp_min(input, CONST_MIN)); + let x20 = cmp_max(cmp_max(CONST_MAX, input), CONST_MIN); + let x21 = cmp_min(cmp_min(CONST_MIN, input), CONST_MAX); + let x22 = cmp_max(CONST_MIN, cmp_max(CONST_MAX, input)); + let x23 = cmp_min(CONST_MAX, cmp_min(CONST_MIN, input)); + let input: f64 = cmp_min_max(1) as f64; + let x24 = f64::max(f64::max(input, CONST_F64_MAX), CONST_F64_MIN); + let x25 = f64::min(f64::min(input, CONST_F64_MIN), CONST_F64_MAX); + let x26 = f64::max(CONST_F64_MIN, f64::max(input, CONST_F64_MAX)); + let x27 = f64::min(CONST_F64_MAX, f64::min(input, CONST_F64_MIN)); + let x28 = f64::max(f64::max(CONST_F64_MAX, input), CONST_F64_MIN); + let x29 = f64::min(f64::min(CONST_F64_MIN, input), CONST_F64_MAX); + let x30 = f64::max(CONST_F64_MIN, f64::max(CONST_F64_MAX, input)); + let x31 = f64::min(CONST_F64_MAX, f64::min(CONST_F64_MIN, input)); + let x32 = f64::min(CONST_F64_MAX, f64::min(CONST_F64_MIN, CONST_F64_MAX)); + } +} + +fn dont_tell_me_what_to_do() { + let (input, min, max) = (0, -2, 3); + let mut x_never = input; + #[allow(clippy::manual_clamp)] + if x_never < min { + x_never = min; + } + if x_never > max { + x_never = max; + } +} + +/// Just to ensure this isn't const evaled +fn cmp_min_max(input: i32) -> i32 { + input * 3 +} diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr new file mode 100644 index 000000000000..0604f8606c3f --- /dev/null +++ b/tests/ui/manual_clamp.stderr @@ -0,0 +1,375 @@ +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:76:5 + | +LL | / if x9 < min { +LL | | x9 = min; +LL | | } +LL | | if x9 > max { +LL | | x9 = max; +LL | | } + | |_____^ help: replace with clamp: `x9 = x9.clamp(min, max);` + | + = note: clamp will panic if max < min + = note: `-D clippy::manual-clamp` implied by `-D warnings` + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:91:5 + | +LL | / if x11 > max { +LL | | x11 = max; +LL | | } +LL | | if x11 < min { +LL | | x11 = min; +LL | | } + | |_____^ help: replace with clamp: `x11 = x11.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:99:5 + | +LL | / if min > x12 { +LL | | x12 = min; +LL | | } +LL | | if max < x12 { +LL | | x12 = max; +LL | | } + | |_____^ help: replace with clamp: `x12 = x12.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:107:5 + | +LL | / if max < x13 { +LL | | x13 = max; +LL | | } +LL | | if min > x13 { +LL | | x13 = min; +LL | | } + | |_____^ help: replace with clamp: `x13 = x13.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:161:5 + | +LL | / if max < x33 { +LL | | x33 = max; +LL | | } +LL | | if min > x33 { +LL | | x33 = min; +LL | | } + | |_____^ help: replace with clamp: `x33 = x33.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:21:14 + | +LL | let x0 = if max < input { + | ______________^ +LL | | max +LL | | } else if min > input { +LL | | min +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:29:14 + | +LL | let x1 = if input > max { + | ______________^ +LL | | max +LL | | } else if input < min { +LL | | min +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:37:14 + | +LL | let x2 = if input < min { + | ______________^ +LL | | min +LL | | } else if input > max { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:45:14 + | +LL | let x3 = if min > input { + | ______________^ +LL | | min +LL | | } else if max < input { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:53:14 + | +LL | let x4 = input.max(min).min(max); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:55:14 + | +LL | let x5 = input.min(max).max(min); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:57:14 + | +LL | let x6 = match input { + | ______________^ +LL | | x if x > max => max, +LL | | x if x < min => min, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:63:14 + | +LL | let x7 = match input { + | ______________^ +LL | | x if x < min => min, +LL | | x if x > max => max, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:69:14 + | +LL | let x8 = match input { + | ______________^ +LL | | x if max < x => max, +LL | | x if min > x => min, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:83:15 + | +LL | let x10 = match input { + | _______________^ +LL | | x if min > x => min, +LL | | x if max < x => max, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:114:15 + | +LL | let x14 = if input > CONST_MAX { + | _______________^ +LL | | CONST_MAX +LL | | } else if input < CONST_MIN { +LL | | CONST_MIN +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:123:19 + | +LL | let x15 = if input > max { + | ___________________^ +LL | | max +LL | | } else if input < min { +LL | | min +LL | | } else { +LL | | input +LL | | }; + | |_________^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:134:19 + | +LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:135:19 + | +LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:136:19 + | +LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:137:19 + | +LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:138:19 + | +LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:139:19 + | +LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:140:19 + | +LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:141:19 + | +LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:143:19 + | +LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:144:19 + | +LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:145:19 + | +LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:146:19 + | +LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:147:19 + | +LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:148:19 + | +LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:149:19 + | +LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:150:19 + | +LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:153:5 + | +LL | / if x32 < min { +LL | | x32 = min; +LL | | } else if x32 > max { +LL | | x32 = max; +LL | | } + | |_____^ help: replace with clamp: `x32 = x32.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: aborting due to 34 previous errors + diff --git a/tests/ui/manual_find_fixable.fixed b/tests/ui/manual_find_fixable.fixed index 36d1644c22bd..2bce6e624c90 100644 --- a/tests/ui/manual_find_fixable.fixed +++ b/tests/ui/manual_find_fixable.fixed @@ -1,7 +1,7 @@ // run-rustfix - -#![allow(unused, clippy::needless_return)] #![warn(clippy::manual_find)] +#![allow(unused)] +#![allow(clippy::needless_return, clippy::uninlined_format_args)] use std::collections::HashMap; diff --git a/tests/ui/manual_find_fixable.rs b/tests/ui/manual_find_fixable.rs index ed277ddaa722..f5c6de37a257 100644 --- a/tests/ui/manual_find_fixable.rs +++ b/tests/ui/manual_find_fixable.rs @@ -1,7 +1,7 @@ // run-rustfix - -#![allow(unused, clippy::needless_return)] #![warn(clippy::manual_find)] +#![allow(unused)] +#![allow(clippy::needless_return, clippy::uninlined_format_args)] use std::collections::HashMap; diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index d922593bc6f9..96cd87c0e19a 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -1,5 +1,5 @@ #![warn(clippy::manual_flatten)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::uninlined_format_args)] fn main() { // Test for loop over implicitly adjusted `Iterator` with `if let` expression diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 87e16f5d09bd..5429fb4e454e 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,6 +1,6 @@ // aux-build:option_helpers.rs - #![warn(clippy::map_unwrap_or)] +#![allow(clippy::uninlined_format_args)] #[macro_use] extern crate option_helpers; diff --git a/tests/ui/match_ref_pats.fixed b/tests/ui/match_ref_pats.fixed index 1b6c2d924121..cf37fc6dc90a 100644 --- a/tests/ui/match_ref_pats.fixed +++ b/tests/ui/match_ref_pats.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_ref_pats)] -#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] +#![allow(dead_code, unused_variables)] +#![allow(clippy::enum_variant_names, clippy::equatable_if_let, clippy::uninlined_format_args)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.rs b/tests/ui/match_ref_pats.rs index 68dfac4e2e97..3220b97d1b51 100644 --- a/tests/ui/match_ref_pats.rs +++ b/tests/ui/match_ref_pats.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_ref_pats)] -#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] +#![allow(dead_code, unused_variables)] +#![allow(clippy::enum_variant_names, clippy::equatable_if_let, clippy::uninlined_format_args)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index 353f7399d9c2..7d9646c842ee 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -1,5 +1,5 @@ error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:8:9 + --> $DIR/match_ref_pats.rs:9:9 | LL | / match v { LL | | &Some(v) => println!("{:?}", v), @@ -16,7 +16,7 @@ LL ~ None => println!("none"), | error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_ref_pats.rs:25:5 + --> $DIR/match_ref_pats.rs:26:5 | LL | / match &w { LL | | &Some(v) => println!("{:?}", v), @@ -32,7 +32,7 @@ LL ~ None => println!("none"), | error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:37:12 + --> $DIR/match_ref_pats.rs:38:12 | LL | if let &None = a { | -------^^^^^---- help: try this: `if a.is_none()` @@ -40,13 +40,13 @@ LL | if let &None = a { = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:42:12 + --> $DIR/match_ref_pats.rs:43:12 | LL | if let &None = &b { | -------^^^^^----- help: try this: `if b.is_none()` error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:102:9 + --> $DIR/match_ref_pats.rs:103:9 | LL | / match foobar_variant!(0) { LL | | &FooBar::Foo => println!("Foo"), diff --git a/tests/ui/match_result_ok.fixed b/tests/ui/match_result_ok.fixed index d4760a97567e..8b91b9854a04 100644 --- a/tests/ui/match_result_ok.fixed +++ b/tests/ui/match_result_ok.fixed @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::match_result_ok)] -#![allow(clippy::boxed_local)] #![allow(dead_code)] +#![allow(clippy::boxed_local, clippy::uninlined_format_args)] // Checking `if` cases diff --git a/tests/ui/match_result_ok.rs b/tests/ui/match_result_ok.rs index 0b818723d989..bc2c4b50e272 100644 --- a/tests/ui/match_result_ok.rs +++ b/tests/ui/match_result_ok.rs @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::match_result_ok)] -#![allow(clippy::boxed_local)] #![allow(dead_code)] +#![allow(clippy::boxed_local, clippy::uninlined_format_args)] // Checking `if` cases diff --git a/tests/ui/match_result_ok.stderr b/tests/ui/match_result_ok.stderr index cc3bc8c76ff3..98a95705ca59 100644 --- a/tests/ui/match_result_ok.stderr +++ b/tests/ui/match_result_ok.stderr @@ -1,5 +1,5 @@ error: matching on `Some` with `ok()` is redundant - --> $DIR/match_result_ok.rs:10:5 + --> $DIR/match_result_ok.rs:9:5 | LL | if let Some(y) = x.parse().ok() { y } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | if let Ok(y) = x.parse() { y } else { 0 } | ~~~~~~~~~~~~~~~~~~~~~~~~ error: matching on `Some` with `ok()` is redundant - --> $DIR/match_result_ok.rs:20:9 + --> $DIR/match_result_ok.rs:19:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | if let Ok(y) = x . parse() { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: matching on `Some` with `ok()` is redundant - --> $DIR/match_result_ok.rs:46:5 + --> $DIR/match_result_ok.rs:45:5 | LL | while let Some(a) = wat.next().ok() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index 61793e80c98d..82b2c433d99e 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -1,5 +1,9 @@ #![warn(clippy::match_same_arms)] -#![allow(clippy::disallowed_names, clippy::diverging_sub_expression)] +#![allow( + clippy::disallowed_names, + clippy::diverging_sub_expression, + clippy::uninlined_format_args +)] fn bar(_: T) {} fn foo() -> bool { diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index b260155d2189..06cd4300054d 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -1,5 +1,5 @@ error: this match arm has an identical body to the `_` wildcard arm - --> $DIR/match_same_arms2.rs:11:9 + --> $DIR/match_same_arms2.rs:15:9 | LL | / 42 => { LL | | foo(); @@ -12,7 +12,7 @@ LL | | }, | = help: or try changing either arm body note: `_` wildcard arm here - --> $DIR/match_same_arms2.rs:20:9 + --> $DIR/match_same_arms2.rs:24:9 | LL | / _ => { LL | | //~ ERROR match arms have same body @@ -25,7 +25,7 @@ LL | | }, = note: `-D clippy::match-same-arms` implied by `-D warnings` error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:34:9 + --> $DIR/match_same_arms2.rs:38:9 | LL | 51 => foo(), //~ ERROR match arms have same body | --^^^^^^^^^ @@ -34,13 +34,13 @@ LL | 51 => foo(), //~ ERROR match arms have same body | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:33:9 + --> $DIR/match_same_arms2.rs:37:9 | LL | 42 => foo(), | ^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:40:9 + --> $DIR/match_same_arms2.rs:44:9 | LL | None => 24, //~ ERROR match arms have same body | ----^^^^^^ @@ -49,13 +49,13 @@ LL | None => 24, //~ ERROR match arms have same body | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:39:9 + --> $DIR/match_same_arms2.rs:43:9 | LL | Some(_) => 24, | ^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:62:9 + --> $DIR/match_same_arms2.rs:66:9 | LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body | ---------------^^^^^^^^^^ @@ -64,13 +64,13 @@ LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:61:9 + --> $DIR/match_same_arms2.rs:65:9 | LL | (Some(a), None) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:67:9 + --> $DIR/match_same_arms2.rs:71:9 | LL | (Some(a), ..) => bar(a), | -------------^^^^^^^^^^ @@ -79,13 +79,13 @@ LL | (Some(a), ..) => bar(a), | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:68:9 + --> $DIR/match_same_arms2.rs:72:9 | LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body | ^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:101:9 + --> $DIR/match_same_arms2.rs:105:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ----------------^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,13 +94,13 @@ LL | (Ok(x), Some(_)) => println!("ok {}", x), | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:102:9 + --> $DIR/match_same_arms2.rs:106:9 | LL | (Ok(_), Some(x)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:117:9 + --> $DIR/match_same_arms2.rs:121:9 | LL | Ok(_) => println!("ok"), | -----^^^^^^^^^^^^^^^^^^ @@ -109,13 +109,13 @@ LL | Ok(_) => println!("ok"), | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:116:9 + --> $DIR/match_same_arms2.rs:120:9 | LL | Ok(3) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:144:9 + --> $DIR/match_same_arms2.rs:148:9 | LL | 1 => { | ^ help: try merging the arm patterns: `1 | 0` @@ -127,7 +127,7 @@ LL | | }, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:141:9 + --> $DIR/match_same_arms2.rs:145:9 | LL | / 0 => { LL | | empty!(0); @@ -135,7 +135,7 @@ LL | | }, | |_________^ error: match expression looks like `matches!` macro - --> $DIR/match_same_arms2.rs:162:16 + --> $DIR/match_same_arms2.rs:166:16 | LL | let _ans = match x { | ________________^ @@ -148,7 +148,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:194:9 + --> $DIR/match_same_arms2.rs:198:9 | LL | Foo::X(0) => 1, | ---------^^^^^ @@ -157,13 +157,13 @@ LL | Foo::X(0) => 1, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:196:9 + --> $DIR/match_same_arms2.rs:200:9 | LL | Foo::Z(_) => 1, | ^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:204:9 + --> $DIR/match_same_arms2.rs:208:9 | LL | Foo::Z(_) => 1, | ---------^^^^^ @@ -172,13 +172,13 @@ LL | Foo::Z(_) => 1, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:202:9 + --> $DIR/match_same_arms2.rs:206:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:227:9 + --> $DIR/match_same_arms2.rs:231:9 | LL | Some(Bar { y: 0, x: 5, .. }) => 1, | ----------------------------^^^^^ @@ -187,7 +187,7 @@ LL | Some(Bar { y: 0, x: 5, .. }) => 1, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:224:9 + --> $DIR/match_same_arms2.rs:228:9 | LL | Some(Bar { x: 0, y: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index de46e6cff55b..951f552eb32b 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] -#![allow(unused_variables, clippy::toplevel_ref_arg)] +#![allow(unused_variables)] +#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)] struct Point { x: i32, diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index eea64fcb292b..19c0fee8fd68 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] -#![allow(unused_variables, clippy::toplevel_ref_arg)] +#![allow(unused_variables)] +#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)] struct Point { x: i32, diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed index a91fcc2125d4..6a7db67e311a 100644 --- a/tests/ui/match_single_binding2.fixed +++ b/tests/ui/match_single_binding2.fixed @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] #![allow(unused_variables)] +#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs index 476386ebabe2..5a4bb8441fff 100644 --- a/tests/ui/match_single_binding2.rs +++ b/tests/ui/match_single_binding2.rs @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] #![allow(unused_variables)] +#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index b2bc97f4744a..24e52afd6917 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -1,4 +1,5 @@ #![warn(clippy::all)] +#![allow(clippy::manual_clamp)] use std::cmp::max as my_max; use std::cmp::min as my_min; diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index c70b77eabbd8..069d9068657e 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -1,5 +1,5 @@ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:23:5 + --> $DIR/min_max.rs:24:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ @@ -7,73 +7,73 @@ LL | min(1, max(3, x)); = note: `-D clippy::min-max` implied by `-D warnings` error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:24:5 + --> $DIR/min_max.rs:25:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:25:5 + --> $DIR/min_max.rs:26:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:26:5 + --> $DIR/min_max.rs:27:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:28:5 + --> $DIR/min_max.rs:29:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:38:5 + --> $DIR/min_max.rs:39:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:39:5 + --> $DIR/min_max.rs:40:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:44:5 + --> $DIR/min_max.rs:45:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:45:5 + --> $DIR/min_max.rs:46:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:46:5 + --> $DIR/min_max.rs:47:5 | LL | f.max(3f32).min(1f32); | ^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:52:5 + --> $DIR/min_max.rs:53:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:55:5 + --> $DIR/min_max.rs:56:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:56:5 + --> $DIR/min_max.rs:57:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 44e407bd1ab2..c4c6391bb4c1 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -160,6 +160,17 @@ fn manual_rem_euclid() { let _: i32 = ((x % 4) + 4) % 4; } +fn manual_clamp() { + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} + fn main() { filter_map_next(); checked_conversion(); @@ -180,6 +191,7 @@ fn main() { err_expect(); cast_abs_to_unsigned(); manual_rem_euclid(); + manual_clamp(); } mod just_under_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 6e749d2741c4..d1cffc26a831 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,21 +1,3 @@ -error: stripping a prefix manually - --> $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:203:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `-D clippy::manual-strip` implied by `-D warnings` -help: try using the `strip_prefix` method - | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); - | - error: stripping a prefix manually --> $DIR/min_rust_version_attr.rs:216:24 | @@ -25,6 +7,24 @@ LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); note: the prefix was tested here --> $DIR/min_rust_version_attr.rs:215:9 | +LL | if s.starts_with("hello, ") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::manual-strip` implied by `-D warnings` +help: try using the `strip_prefix` method + | +LL ~ if let Some() = s.strip_prefix("hello, ") { +LL ~ assert_eq!(.to_uppercase(), "WORLD!"); + | + +error: stripping a prefix manually + --> $DIR/min_rust_version_attr.rs:228:24 + | +LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/min_rust_version_attr.rs:227:9 + | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using the `strip_prefix` method diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index be854d941833..ac8fd9d8fb09 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -1,7 +1,7 @@ // aux-build:macro_rules.rs - -#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::mut_mut)] +#![allow(unused)] +#![allow(clippy::no_effect, clippy::uninlined_format_args, clippy::unnecessary_operation)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 8cf93bd24817..aa2687159ef4 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,9 +1,9 @@ // run-rustfix - #![feature(custom_inner_attributes, lint_reasons)] #[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables, clippy::unnecessary_mut_passed)] +#[allow(unused_variables)] +#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] fn main() { let a = 5; let ref_a = &a; @@ -298,3 +298,32 @@ mod meets_msrv { let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); } } + +#[allow(unused)] +fn issue9383() { + // Should not lint because unions need explicit deref when accessing field + use std::mem::ManuallyDrop; + + union Coral { + crab: ManuallyDrop>, + } + + union Ocean { + coral: ManuallyDrop, + } + + let mut ocean = Ocean { + coral: ManuallyDrop::new(Coral { + crab: ManuallyDrop::new(vec![1, 2, 3]), + }), + }; + + unsafe { + ManuallyDrop::drop(&mut (&mut ocean.coral).crab); + + (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); + ManuallyDrop::drop(&mut (*ocean.coral).crab); + + ManuallyDrop::drop(&mut ocean.coral); + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index fd9b2a11df96..d41251e8f6aa 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,9 +1,9 @@ // run-rustfix - #![feature(custom_inner_attributes, lint_reasons)] #[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables, clippy::unnecessary_mut_passed)] +#[allow(unused_variables)] +#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] fn main() { let a = 5; let ref_a = &a; @@ -298,3 +298,32 @@ mod meets_msrv { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } + +#[allow(unused)] +fn issue9383() { + // Should not lint because unions need explicit deref when accessing field + use std::mem::ManuallyDrop; + + union Coral { + crab: ManuallyDrop>, + } + + union Ocean { + coral: ManuallyDrop, + } + + let mut ocean = Ocean { + coral: ManuallyDrop::new(Coral { + crab: ManuallyDrop::new(vec![1, 2, 3]), + }), + }; + + unsafe { + ManuallyDrop::drop(&mut (&mut ocean.coral).crab); + + (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); + ManuallyDrop::drop(&mut (*ocean.coral).crab); + + ManuallyDrop::drop(&mut ocean.coral); + } +} diff --git a/tests/ui/needless_borrowed_ref.fixed b/tests/ui/needless_borrowed_ref.fixed index a0937a2c5f62..bcb4eb2dd48a 100644 --- a/tests/ui/needless_borrowed_ref.fixed +++ b/tests/ui/needless_borrowed_ref.fixed @@ -1,17 +1,38 @@ // run-rustfix -#[warn(clippy::needless_borrowed_reference)] -#[allow(unused_variables)] -fn main() { +#![warn(clippy::needless_borrowed_reference)] +#![allow(unused, clippy::needless_borrow)] + +fn main() {} + +fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|a| a.is_empty()); - // ^ should be linted let var = 3; let thingy = Some(&var); - if let Some(&ref v) = thingy { - // ^ should be linted - } + if let Some(v) = thingy {} + + if let &[a, ref b] = slice_of_refs {} + + let [a, ..] = &array; + let [a, b, ..] = &array; + + if let [a, b] = slice {} + if let [a, b] = &vec[..] {} + + if let [a, b, ..] = slice {} + if let [a, .., b] = slice {} + if let [.., a, b] = slice {} +} + +fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { + if let [ref a] = slice {} + if let &[ref a, b] = slice {} + if let &[ref a, .., b] = slice {} + + // must not be removed as variables must be bound consistently across | patterns + if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} let mut var2 = 5; let thingy2 = Some(&mut var2); @@ -28,17 +49,15 @@ fn main() { } } -#[allow(dead_code)] enum Animal { Cat(u64), Dog(u64), } -#[allow(unused_variables)] -#[allow(dead_code)] fn foo(a: &Animal, b: &Animal) { match (a, b) { - (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 + (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted } diff --git a/tests/ui/needless_borrowed_ref.rs b/tests/ui/needless_borrowed_ref.rs index 500ac448f0d5..f6de1a6d83d1 100644 --- a/tests/ui/needless_borrowed_ref.rs +++ b/tests/ui/needless_borrowed_ref.rs @@ -1,17 +1,38 @@ // run-rustfix -#[warn(clippy::needless_borrowed_reference)] -#[allow(unused_variables)] -fn main() { +#![warn(clippy::needless_borrowed_reference)] +#![allow(unused, clippy::needless_borrow)] + +fn main() {} + +fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|&ref a| a.is_empty()); - // ^ should be linted let var = 3; let thingy = Some(&var); - if let Some(&ref v) = thingy { - // ^ should be linted - } + if let Some(&ref v) = thingy {} + + if let &[&ref a, ref b] = slice_of_refs {} + + let &[ref a, ..] = &array; + let &[ref a, ref b, ..] = &array; + + if let &[ref a, ref b] = slice {} + if let &[ref a, ref b] = &vec[..] {} + + if let &[ref a, ref b, ..] = slice {} + if let &[ref a, .., ref b] = slice {} + if let &[.., ref a, ref b] = slice {} +} + +fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { + if let [ref a] = slice {} + if let &[ref a, b] = slice {} + if let &[ref a, .., b] = slice {} + + // must not be removed as variables must be bound consistently across | patterns + if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} let mut var2 = 5; let thingy2 = Some(&mut var2); @@ -28,17 +49,15 @@ fn main() { } } -#[allow(dead_code)] enum Animal { Cat(u64), Dog(u64), } -#[allow(unused_variables)] -#[allow(dead_code)] fn foo(a: &Animal, b: &Animal) { match (a, b) { - (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 + (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted } diff --git a/tests/ui/needless_borrowed_ref.stderr b/tests/ui/needless_borrowed_ref.stderr index 0a5cfb3db0b1..7453542e673f 100644 --- a/tests/ui/needless_borrowed_ref.stderr +++ b/tests/ui/needless_borrowed_ref.stderr @@ -1,10 +1,123 @@ -error: this pattern takes a reference on something that is being de-referenced - --> $DIR/needless_borrowed_ref.rs:7:34 +error: this pattern takes a reference on something that is being dereferenced + --> $DIR/needless_borrowed_ref.rs:10:34 | LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty()); - | ^^^^^^ help: try removing the `&ref` part and just keep: `a` + | ^^^^^^ | = note: `-D clippy::needless-borrowed-reference` implied by `-D warnings` +help: try removing the `&ref` part + | +LL - let _ = v.iter_mut().filter(|&ref a| a.is_empty()); +LL + let _ = v.iter_mut().filter(|a| a.is_empty()); + | -error: aborting due to previous error +error: this pattern takes a reference on something that is being dereferenced + --> $DIR/needless_borrowed_ref.rs:14:17 + | +LL | if let Some(&ref v) = thingy {} + | ^^^^^^ + | +help: try removing the `&ref` part + | +LL - if let Some(&ref v) = thingy {} +LL + if let Some(v) = thingy {} + | + +error: this pattern takes a reference on something that is being dereferenced + --> $DIR/needless_borrowed_ref.rs:16:14 + | +LL | if let &[&ref a, ref b] = slice_of_refs {} + | ^^^^^^ + | +help: try removing the `&ref` part + | +LL - if let &[&ref a, ref b] = slice_of_refs {} +LL + if let &[a, ref b] = slice_of_refs {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:18:9 + | +LL | let &[ref a, ..] = &array; + | ^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - let &[ref a, ..] = &array; +LL + let [a, ..] = &array; + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:19:9 + | +LL | let &[ref a, ref b, ..] = &array; + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - let &[ref a, ref b, ..] = &array; +LL + let [a, b, ..] = &array; + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:21:12 + | +LL | if let &[ref a, ref b] = slice {} + | ^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, ref b] = slice {} +LL + if let [a, b] = slice {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:22:12 + | +LL | if let &[ref a, ref b] = &vec[..] {} + | ^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, ref b] = &vec[..] {} +LL + if let [a, b] = &vec[..] {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:24:12 + | +LL | if let &[ref a, ref b, ..] = slice {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, ref b, ..] = slice {} +LL + if let [a, b, ..] = slice {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:25:12 + | +LL | if let &[ref a, .., ref b] = slice {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, .., ref b] = slice {} +LL + if let [a, .., b] = slice {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:26:12 + | +LL | if let &[.., ref a, ref b] = slice {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[.., ref a, ref b] = slice {} +LL + if let [.., a, b] = slice {} + | + +error: aborting due to 10 previous errors diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 12a9ace1ee68..6d213b46c20c 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; fn main() { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 9f0880cc6069..99e1b91d8fea 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:5:39 + --> $DIR/needless_collect_indirect.rs:7:39 | LL | let indirect_iter = sample.iter().collect::>(); | ^^^^^^^ @@ -14,7 +14,7 @@ LL ~ sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:7:38 + --> $DIR/needless_collect_indirect.rs:9:38 | LL | let indirect_len = sample.iter().collect::>(); | ^^^^^^^ @@ -28,7 +28,7 @@ LL ~ sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:9:40 + --> $DIR/needless_collect_indirect.rs:11:40 | LL | let indirect_empty = sample.iter().collect::>(); | ^^^^^^^ @@ -42,7 +42,7 @@ LL ~ sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:11:43 + --> $DIR/needless_collect_indirect.rs:13:43 | LL | let indirect_contains = sample.iter().collect::>(); | ^^^^^^^ @@ -56,7 +56,7 @@ LL ~ sample.iter().any(|x| x == &5); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:23:48 + --> $DIR/needless_collect_indirect.rs:25:48 | LL | let non_copy_contains = sample.into_iter().collect::>(); | ^^^^^^^ @@ -70,7 +70,7 @@ LL ~ sample.into_iter().any(|x| x == a); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:52:51 + --> $DIR/needless_collect_indirect.rs:54:51 | LL | let buffer: Vec<&str> = string.split('/').collect(); | ^^^^^^^ @@ -84,7 +84,7 @@ LL ~ string.split('/').count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:57:55 + --> $DIR/needless_collect_indirect.rs:59:55 | LL | let indirect_len: VecDeque<_> = sample.iter().collect(); | ^^^^^^^ @@ -98,7 +98,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:62:57 + --> $DIR/needless_collect_indirect.rs:64:57 | LL | let indirect_len: LinkedList<_> = sample.iter().collect(); | ^^^^^^^ @@ -112,7 +112,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:67:57 + --> $DIR/needless_collect_indirect.rs:69:57 | LL | let indirect_len: BinaryHeap<_> = sample.iter().collect(); | ^^^^^^^ @@ -126,7 +126,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:127:59 + --> $DIR/needless_collect_indirect.rs:129:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -143,7 +143,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == i); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:152:59 + --> $DIR/needless_collect_indirect.rs:154:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -160,7 +160,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:181:63 + --> $DIR/needless_collect_indirect.rs:183:63 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -177,7 +177,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:217:59 + --> $DIR/needless_collect_indirect.rs:219:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -195,7 +195,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:242:26 + --> $DIR/needless_collect_indirect.rs:244:26 | LL | let w = v.iter().collect::>(); | ^^^^^^^ @@ -211,7 +211,7 @@ LL ~ for _ in 0..v.iter().count() { | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:264:30 + --> $DIR/needless_collect_indirect.rs:266:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ @@ -227,7 +227,7 @@ LL ~ while 1 == v.iter().count() { | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:286:30 + --> $DIR/needless_collect_indirect.rs:288:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ diff --git a/tests/ui/needless_continue.rs b/tests/ui/needless_continue.rs index f105d3d659ac..c891c9de3aec 100644 --- a/tests/ui/needless_continue.rs +++ b/tests/ui/needless_continue.rs @@ -1,4 +1,5 @@ #![warn(clippy::needless_continue)] +#![allow(clippy::uninlined_format_args)] macro_rules! zero { ($x:expr) => { diff --git a/tests/ui/needless_continue.stderr b/tests/ui/needless_continue.stderr index 005ba010f34f..d99989b54fc2 100644 --- a/tests/ui/needless_continue.stderr +++ b/tests/ui/needless_continue.stderr @@ -1,5 +1,5 @@ error: this `else` block is redundant - --> $DIR/needless_continue.rs:29:16 + --> $DIR/needless_continue.rs:30:16 | LL | } else { | ________________^ @@ -35,7 +35,7 @@ LL | | } = note: `-D clippy::needless-continue` implied by `-D warnings` error: there is no need for an explicit `else` block for this `if` expression - --> $DIR/needless_continue.rs:44:9 + --> $DIR/needless_continue.rs:45:9 | LL | / if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { LL | | continue; @@ -55,7 +55,7 @@ LL | | } } error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:57:9 + --> $DIR/needless_continue.rs:58:9 | LL | continue; // should lint here | ^^^^^^^^^ @@ -63,7 +63,7 @@ LL | continue; // should lint here = help: consider dropping the `continue` expression error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:64:9 + --> $DIR/needless_continue.rs:65:9 | LL | continue; // should lint here | ^^^^^^^^^ @@ -71,7 +71,7 @@ LL | continue; // should lint here = help: consider dropping the `continue` expression error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:71:9 + --> $DIR/needless_continue.rs:72:9 | LL | continue // should lint here | ^^^^^^^^ @@ -79,7 +79,7 @@ LL | continue // should lint here = help: consider dropping the `continue` expression error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:79:9 + --> $DIR/needless_continue.rs:80:9 | LL | continue // should lint here | ^^^^^^^^ @@ -87,7 +87,7 @@ LL | continue // should lint here = help: consider dropping the `continue` expression error: this `else` block is redundant - --> $DIR/needless_continue.rs:129:24 + --> $DIR/needless_continue.rs:130:24 | LL | } else { | ________________________^ @@ -110,7 +110,7 @@ LL | | } } error: there is no need for an explicit `else` block for this `if` expression - --> $DIR/needless_continue.rs:135:17 + --> $DIR/needless_continue.rs:136:17 | LL | / if condition() { LL | | continue; // should lint here diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed index c1685f7b6d7a..09e671b88e1a 100644 --- a/tests/ui/needless_for_each_fixable.fixed +++ b/tests/ui/needless_for_each_fixable.fixed @@ -1,10 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] +#![allow(unused)] #![allow( - unused, - clippy::needless_return, + clippy::let_unit_value, clippy::match_single_binding, - clippy::let_unit_value + clippy::needless_return, + clippy::uninlined_format_args )] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs index ad17b0956fa9..abb4045b9197 100644 --- a/tests/ui/needless_for_each_fixable.rs +++ b/tests/ui/needless_for_each_fixable.rs @@ -1,10 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] +#![allow(unused)] #![allow( - unused, - clippy::needless_return, + clippy::let_unit_value, clippy::match_single_binding, - clippy::let_unit_value + clippy::needless_return, + clippy::uninlined_format_args )] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.stderr b/tests/ui/needless_for_each_fixable.stderr index 08e995851d7a..aebb762cc228 100644 --- a/tests/ui/needless_for_each_fixable.stderr +++ b/tests/ui/needless_for_each_fixable.stderr @@ -1,5 +1,5 @@ error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:15:5 + --> $DIR/needless_for_each_fixable.rs:16:5 | LL | / v.iter().for_each(|elem| { LL | | acc += elem; @@ -15,7 +15,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:18:5 + --> $DIR/needless_for_each_fixable.rs:19:5 | LL | / v.into_iter().for_each(|elem| { LL | | acc += elem; @@ -30,7 +30,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:22:5 + --> $DIR/needless_for_each_fixable.rs:23:5 | LL | / [1, 2, 3].iter().for_each(|elem| { LL | | acc += elem; @@ -45,7 +45,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:27:5 + --> $DIR/needless_for_each_fixable.rs:28:5 | LL | / hash_map.iter().for_each(|(k, v)| { LL | | acc += k + v; @@ -60,7 +60,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:30:5 + --> $DIR/needless_for_each_fixable.rs:31:5 | LL | / hash_map.iter_mut().for_each(|(k, v)| { LL | | acc += *k + *v; @@ -75,7 +75,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:33:5 + --> $DIR/needless_for_each_fixable.rs:34:5 | LL | / hash_map.keys().for_each(|k| { LL | | acc += k; @@ -90,7 +90,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:36:5 + --> $DIR/needless_for_each_fixable.rs:37:5 | LL | / hash_map.values().for_each(|v| { LL | | acc += v; @@ -105,7 +105,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:43:5 + --> $DIR/needless_for_each_fixable.rs:44:5 | LL | / my_vec().iter().for_each(|elem| { LL | | acc += elem; diff --git a/tests/ui/needless_for_each_unfixable.rs b/tests/ui/needless_for_each_unfixable.rs index d765d7dab65c..282c72881d51 100644 --- a/tests/ui/needless_for_each_unfixable.rs +++ b/tests/ui/needless_for_each_unfixable.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_for_each)] -#![allow(clippy::needless_return)] +#![allow(clippy::needless_return, clippy::uninlined_format_args)] fn main() { let v: Vec = Vec::new(); diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index fee8e3030b80..17f2227ba91c 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -1,12 +1,13 @@ // run-rustfix #![feature(let_chains)] +#![allow(unused)] #![allow( - unused, clippy::assign_op_pattern, clippy::blocks_in_if_conditions, clippy::let_and_return, clippy::let_unit_value, - clippy::nonminimal_bool + clippy::nonminimal_bool, + clippy::uninlined_format_args )] use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 402d9f9ef7f8..d84457a29875 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,12 +1,13 @@ // run-rustfix #![feature(let_chains)] +#![allow(unused)] #![allow( - unused, clippy::assign_op_pattern, clippy::blocks_in_if_conditions, clippy::let_and_return, clippy::let_unit_value, - clippy::nonminimal_bool + clippy::nonminimal_bool, + clippy::uninlined_format_args )] use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 313cdbbeba18..0a256fb4a131 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initialization - --> $DIR/needless_late_init.rs:23:5 + --> $DIR/needless_late_init.rs:24:5 | LL | let a; | ^^^^^^ created here @@ -13,7 +13,7 @@ LL | let a = "zero"; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:26:5 + --> $DIR/needless_late_init.rs:27:5 | LL | let b; | ^^^^^^ created here @@ -27,7 +27,7 @@ LL | let b = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:27:5 + --> $DIR/needless_late_init.rs:28:5 | LL | let c; | ^^^^^^ created here @@ -41,7 +41,7 @@ LL | let c = 2; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:31:5 + --> $DIR/needless_late_init.rs:32:5 | LL | let d: usize; | ^^^^^^^^^^^^^ created here @@ -54,7 +54,7 @@ LL | let d: usize = 1; | ~~~~~~~~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:34:5 + --> $DIR/needless_late_init.rs:35:5 | LL | let e; | ^^^^^^ created here @@ -67,7 +67,7 @@ LL | let e = format!("{}", d); | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:39:5 + --> $DIR/needless_late_init.rs:40:5 | LL | let a; | ^^^^^^ @@ -88,7 +88,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:48:5 + --> $DIR/needless_late_init.rs:49:5 | LL | let b; | ^^^^^^ @@ -109,7 +109,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:55:5 + --> $DIR/needless_late_init.rs:56:5 | LL | let d; | ^^^^^^ @@ -130,7 +130,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:63:5 + --> $DIR/needless_late_init.rs:64:5 | LL | let e; | ^^^^^^ @@ -151,7 +151,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:70:5 + --> $DIR/needless_late_init.rs:71:5 | LL | let f; | ^^^^^^ @@ -167,7 +167,7 @@ LL + 1 => "three", | error: unneeded late initialization - --> $DIR/needless_late_init.rs:76:5 + --> $DIR/needless_late_init.rs:77:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -187,7 +187,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:84:5 + --> $DIR/needless_late_init.rs:85:5 | LL | let x; | ^^^^^^ created here @@ -201,7 +201,7 @@ LL | let x = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:88:5 + --> $DIR/needless_late_init.rs:89:5 | LL | let x; | ^^^^^^ created here @@ -215,7 +215,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:92:5 + --> $DIR/needless_late_init.rs:93:5 | LL | let x; | ^^^^^^ created here @@ -229,7 +229,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:111:5 + --> $DIR/needless_late_init.rs:112:5 | LL | let a; | ^^^^^^ @@ -250,7 +250,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:128:5 + --> $DIR/needless_late_init.rs:129:5 | LL | let a; | ^^^^^^ diff --git a/tests/ui/needless_pass_by_value.rs b/tests/ui/needless_pass_by_value.rs index 5a35b100afe0..d79ad86b1948 100644 --- a/tests/ui/needless_pass_by_value.rs +++ b/tests/ui/needless_pass_by_value.rs @@ -1,10 +1,11 @@ #![warn(clippy::needless_pass_by_value)] +#![allow(dead_code)] #![allow( - dead_code, - clippy::single_match, - clippy::redundant_pattern_matching, clippy::option_option, - clippy::redundant_clone + clippy::redundant_clone, + clippy::redundant_pattern_matching, + clippy::single_match, + clippy::uninlined_format_args )] use std::borrow::Borrow; diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr index 38f33c53f128..0e660a77dc0c 100644 --- a/tests/ui/needless_pass_by_value.stderr +++ b/tests/ui/needless_pass_by_value.stderr @@ -1,5 +1,5 @@ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:17:23 + --> $DIR/needless_pass_by_value.rs:18:23 | LL | fn foo(v: Vec, w: Vec, mut x: Vec, y: Vec) -> Vec { | ^^^^^^ help: consider changing the type to: `&[T]` @@ -7,55 +7,55 @@ LL | fn foo(v: Vec, w: Vec, mut x: Vec, y: Vec) -> Vec $DIR/needless_pass_by_value.rs:31:11 + --> $DIR/needless_pass_by_value.rs:32:11 | LL | fn bar(x: String, y: Wrapper) { | ^^^^^^ help: consider changing the type to: `&str` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:31:22 + --> $DIR/needless_pass_by_value.rs:32:22 | LL | fn bar(x: String, y: Wrapper) { | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:37:71 + --> $DIR/needless_pass_by_value.rs:38:71 | LL | fn test_borrow_trait, U: AsRef, V>(t: T, u: U, v: V) { | ^ help: consider taking a reference instead: `&V` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:49:18 + --> $DIR/needless_pass_by_value.rs:50:18 | LL | fn test_match(x: Option>, y: Option>) { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option>` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:62:24 + --> $DIR/needless_pass_by_value.rs:63:24 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:62:36 + --> $DIR/needless_pass_by_value.rs:63:36 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:78:49 + --> $DIR/needless_pass_by_value.rs:79:49 | LL | fn test_blanket_ref(_foo: T, _serializable: S) {} | ^ help: consider taking a reference instead: `&T` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:18 + --> $DIR/needless_pass_by_value.rs:81:18 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^ help: consider taking a reference instead: `&String` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:29 + --> $DIR/needless_pass_by_value.rs:81:29 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^ @@ -70,13 +70,13 @@ LL | let _ = t.to_string(); | ~~~~~~~~~~~~~ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:40 + --> $DIR/needless_pass_by_value.rs:81:40 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^^^ help: consider taking a reference instead: `&Vec` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:53 + --> $DIR/needless_pass_by_value.rs:81:53 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^^^ @@ -91,85 +91,85 @@ LL | let _ = v.to_owned(); | ~~~~~~~~~~~~ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:93:12 + --> $DIR/needless_pass_by_value.rs:94:12 | LL | s: String, | ^^^^^^ help: consider changing the type to: `&str` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:94:12 + --> $DIR/needless_pass_by_value.rs:95:12 | LL | t: String, | ^^^^^^ help: consider taking a reference instead: `&String` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:103:23 + --> $DIR/needless_pass_by_value.rs:104:23 | LL | fn baz(&self, _u: U, _s: Self) {} | ^ help: consider taking a reference instead: `&U` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:103:30 + --> $DIR/needless_pass_by_value.rs:104:30 | LL | fn baz(&self, _u: U, _s: Self) {} | ^^^^ help: consider taking a reference instead: `&Self` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:125:24 + --> $DIR/needless_pass_by_value.rs:126:24 | LL | fn bar_copy(x: u32, y: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:131:29 + --> $DIR/needless_pass_by_value.rs:132:29 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:131:45 + --> $DIR/needless_pass_by_value.rs:132:45 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:131:61 + --> $DIR/needless_pass_by_value.rs:132:61 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:143:40 + --> $DIR/needless_pass_by_value.rs:144:40 | LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {} | ^ help: consider taking a reference instead: `&S` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:148:20 + --> $DIR/needless_pass_by_value.rs:149:20 | LL | fn more_fun(_item: impl Club<'static, i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` diff --git a/tests/ui/needless_range_loop.rs b/tests/ui/needless_range_loop.rs index 3fce34367ae5..921801138a9b 100644 --- a/tests/ui/needless_range_loop.rs +++ b/tests/ui/needless_range_loop.rs @@ -1,4 +1,5 @@ #![warn(clippy::needless_range_loop)] +#![allow(clippy::uninlined_format_args)] static STATIC: [usize; 4] = [0, 1, 8, 16]; const CONST: [usize; 4] = [0, 1, 8, 16]; diff --git a/tests/ui/needless_range_loop.stderr b/tests/ui/needless_range_loop.stderr index a86cc69dfc5d..b31544ec334a 100644 --- a/tests/ui/needless_range_loop.stderr +++ b/tests/ui/needless_range_loop.stderr @@ -1,5 +1,5 @@ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:10:14 + --> $DIR/needless_range_loop.rs:11:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | for in &vec { | ~~~~~~ ~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:19:14 + --> $DIR/needless_range_loop.rs:20:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | for in &vec { | ~~~~~~ ~~~~ error: the loop variable `j` is only used to index `STATIC` - --> $DIR/needless_range_loop.rs:24:14 + --> $DIR/needless_range_loop.rs:25:14 | LL | for j in 0..4 { | ^^^^ @@ -33,7 +33,7 @@ LL | for in &STATIC { | ~~~~~~ ~~~~~~~ error: the loop variable `j` is only used to index `CONST` - --> $DIR/needless_range_loop.rs:28:14 + --> $DIR/needless_range_loop.rs:29:14 | LL | for j in 0..4 { | ^^^^ @@ -44,7 +44,7 @@ LL | for in &CONST { | ~~~~~~ ~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:32:14 + --> $DIR/needless_range_loop.rs:33:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | for (i, ) in vec.iter().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec2` - --> $DIR/needless_range_loop.rs:40:14 + --> $DIR/needless_range_loop.rs:41:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | for in vec2.iter().take(vec.len()) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:44:14 + --> $DIR/needless_range_loop.rs:45:14 | LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | for in vec.iter().skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:48:14 + --> $DIR/needless_range_loop.rs:49:14 | LL | for i in 0..MAX_LEN { | ^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | for in vec.iter().take(MAX_LEN) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:52:14 + --> $DIR/needless_range_loop.rs:53:14 | LL | for i in 0..=MAX_LEN { | ^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | for in vec.iter().take(MAX_LEN + 1) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:56:14 + --> $DIR/needless_range_loop.rs:57:14 | LL | for i in 5..10 { | ^^^^^ @@ -110,7 +110,7 @@ LL | for in vec.iter().take(10).skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:60:14 + --> $DIR/needless_range_loop.rs:61:14 | LL | for i in 5..=10 { | ^^^^^^ @@ -121,7 +121,7 @@ LL | for in vec.iter().take(10 + 1).skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:64:14 + --> $DIR/needless_range_loop.rs:65:14 | LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | for (i, ) in vec.iter().enumerate().skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:68:14 + --> $DIR/needless_range_loop.rs:69:14 | LL | for i in 5..10 { | ^^^^^ @@ -143,7 +143,7 @@ LL | for (i, ) in vec.iter().enumerate().take(10).skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:73:14 + --> $DIR/needless_range_loop.rs:74:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 695883e8dff7..d2163b14fcad 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -232,4 +232,41 @@ fn issue_9361() -> i32 { return 1 + 2; } +fn issue8336(x: i32) -> bool { + if x > 0 { + println!("something"); + true + } else { + false + } +} + +fn issue8156(x: u8) -> u64 { + match x { + 80 => { + 10 + }, + _ => { + 100 + }, + } +} + +// Ideally the compiler should throw `unused_braces` in this case +fn issue9192() -> i32 { + { + 0 + } +} + +fn issue9503(x: usize) -> isize { + unsafe { + if x > 12 { + *(x as *const isize) + } else { + !*(x as *const isize) + } + } +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 63d9fe9ecdf8..114414b5fac7 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -232,4 +232,41 @@ fn issue_9361() -> i32 { return 1 + 2; } +fn issue8336(x: i32) -> bool { + if x > 0 { + println!("something"); + return true; + } else { + return false; + }; +} + +fn issue8156(x: u8) -> u64 { + match x { + 80 => { + return 10; + }, + _ => { + return 100; + }, + }; +} + +// Ideally the compiler should throw `unused_braces` in this case +fn issue9192() -> i32 { + { + return 0; + }; +} + +fn issue9503(x: usize) -> isize { + unsafe { + if x > 12 { + return *(x as *const isize); + } else { + return !*(x as *const isize); + }; + }; +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index cadee6e00dff..047fb6c2311a 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -2,225 +2,354 @@ error: unneeded `return` statement --> $DIR/needless_return.rs:26:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ | = note: `-D clippy::needless-return` implied by `-D warnings` + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:30:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:35:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:37:9 | LL | return false; - | ^^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:43:17 | LL | true => return false, - | ^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:45:13 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:52:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:54:16 | LL | let _ = || return true; - | ^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:58:5 | LL | return the_answer!(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:62:5 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:67:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:69:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:76:14 | LL | _ => return, - | ^^^^^^ help: replace `return` with a unit value: `()` + | ^^^^^^ + | + = help: replace `return` with a unit value error: unneeded `return` statement --> $DIR/needless_return.rs:85:13 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:87:14 | LL | _ => return, - | ^^^^^^ help: replace `return` with a unit value: `()` + | ^^^^^^ + | + = help: replace `return` with a unit value error: unneeded `return` statement --> $DIR/needless_return.rs:100:9 | LL | return String::from("test"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:102:9 | LL | return String::new(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:124:32 | LL | bar.unwrap_or_else(|_| return) - | ^^^^^^ help: replace `return` with an empty block: `{}` + | ^^^^^^ + | + = help: replace `return` with an empty block error: unneeded `return` statement --> $DIR/needless_return.rs:129:13 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:131:20 | LL | let _ = || return; - | ^^^^^^ help: replace `return` with an empty block: `{}` + | ^^^^^^ + | + = help: replace `return` with an empty block error: unneeded `return` statement --> $DIR/needless_return.rs:137:32 | LL | res.unwrap_or_else(|_| return Foo) - | ^^^^^^^^^^ help: remove `return`: `Foo` + | ^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:146:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:150:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:155:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:157:9 | LL | return false; - | ^^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:163:17 | LL | true => return false, - | ^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:165:13 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:172:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:174:16 | LL | let _ = || return true; - | ^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:178:5 | LL | return the_answer!(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:182:5 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:187:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:189:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:196:14 | LL | _ => return, - | ^^^^^^ help: replace `return` with a unit value: `()` + | ^^^^^^ + | + = help: replace `return` with a unit value error: unneeded `return` statement --> $DIR/needless_return.rs:209:9 | LL | return String::from("test"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:211:9 | LL | return String::new(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:227:5 | LL | return format!("Hello {}", "world!"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `format!("Hello {}", "world!")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` -error: aborting due to 37 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:238:9 + | +LL | return true; + | ^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:240:9 + | +LL | return false; + | ^^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:247:13 + | +LL | return 10; + | ^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:250:13 + | +LL | return 100; + | ^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:258:9 + | +LL | return 0; + | ^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:265:13 + | +LL | return *(x as *const isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:267:13 + | +LL | return !*(x as *const isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: aborting due to 44 previous errors diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 0a21589dd0d4..3dbef19890e9 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -203,6 +203,32 @@ pub fn test17() { }; } +// Issue #9356: `continue` in else branch of let..else +pub fn test18() { + let x = Some(0); + let y = 0; + // might loop + let _ = loop { + let Some(x) = x else { + if y > 0 { + continue; + } else { + return; + } + }; + + break x; + }; + // never loops + let _ = loop { + let Some(x) = x else { + return; + }; + + break x; + }; +} + fn main() { test1(); test2(); diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index f49b23924efe..3033f019244a 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -101,5 +101,18 @@ LL | | break 'label; LL | | } | |_________^ -error: aborting due to 9 previous errors +error: this loop never actually loops + --> $DIR/never_loop.rs:223:13 + | +LL | let _ = loop { + | _____________^ +LL | | let Some(x) = x else { +LL | | return; +LL | | }; +LL | | +LL | | break x; +LL | | }; + | |_____^ + +error: aborting due to 10 previous errors diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index fdefb11ae17a..f08eb092e6b2 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -1,9 +1,7 @@ #![feature(box_syntax, fn_traits, unboxed_closures)] #![warn(clippy::no_effect_underscore_binding)] -#![allow(dead_code)] -#![allow(path_statements)] -#![allow(clippy::deref_addrof)] -#![allow(clippy::redundant_field_names)] +#![allow(dead_code, path_statements)] +#![allow(clippy::deref_addrof, clippy::redundant_field_names, clippy::uninlined_format_args)] struct Unit; struct Tuple(i32); diff --git a/tests/ui/no_effect.stderr b/tests/ui/no_effect.stderr index 328d2555ceb8..6a1e636f9a61 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:94:5 + --> $DIR/no_effect.rs:92: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:95:5 + --> $DIR/no_effect.rs:93:5 | LL | s2; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:96:5 + --> $DIR/no_effect.rs:94:5 | LL | Unit; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:97:5 + --> $DIR/no_effect.rs:95:5 | LL | Tuple(0); | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:98:5 + --> $DIR/no_effect.rs:96:5 | LL | Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:99:5 + --> $DIR/no_effect.rs:97:5 | LL | Struct { ..s }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:100:5 + --> $DIR/no_effect.rs:98:5 | LL | Union { a: 0 }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:101:5 + --> $DIR/no_effect.rs:99:5 | LL | Enum::Tuple(0); | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:102:5 + --> $DIR/no_effect.rs:100:5 | LL | Enum::Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:103:5 + --> $DIR/no_effect.rs:101:5 | LL | 5 + 6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:104:5 + --> $DIR/no_effect.rs:102:5 | LL | *&42; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:105:5 + --> $DIR/no_effect.rs:103:5 | LL | &6; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:106:5 + --> $DIR/no_effect.rs:104:5 | LL | (5, 6, 7); | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:107:5 + --> $DIR/no_effect.rs:105:5 | LL | box 42; | ^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:108:5 + --> $DIR/no_effect.rs:106:5 | LL | ..; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:109:5 + --> $DIR/no_effect.rs:107:5 | LL | 5..; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:110:5 + --> $DIR/no_effect.rs:108:5 | LL | ..5; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:111:5 + --> $DIR/no_effect.rs:109:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:112:5 + --> $DIR/no_effect.rs:110:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:113:5 + --> $DIR/no_effect.rs:111:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:114:5 + --> $DIR/no_effect.rs:112:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:115:5 + --> $DIR/no_effect.rs:113:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:116:5 + --> $DIR/no_effect.rs:114:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:117:5 + --> $DIR/no_effect.rs:115:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:119:5 + --> $DIR/no_effect.rs:117:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:121:5 + --> $DIR/no_effect.rs:119:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:122:5 + --> $DIR/no_effect.rs:120: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:123:5 + --> $DIR/no_effect.rs:121:5 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:124:5 + --> $DIR/no_effect.rs:122:5 | LL | let _duck = Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:125:5 + --> $DIR/no_effect.rs:123:5 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 1290bd8efebd..00264dcceaa8 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index f3e5b62c65b7..f3363ebce54e 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index ab2a294a060f..0305387b9f8a 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,5 +1,5 @@ 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:39:5 + --> $DIR/option_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -9,7 +9,7 @@ LL | x.field.map(do_nothing); = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` 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:41:5 + --> $DIR/option_map_unit_fn_fixable.rs:40:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -17,7 +17,7 @@ LL | x.field.map(do_nothing); | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` 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:43:5 + --> $DIR/option_map_unit_fn_fixable.rs:42:5 | LL | x.field.map(diverge); | ^^^^^^^^^^^^^^^^^^^^- @@ -25,7 +25,7 @@ LL | x.field.map(diverge); | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` 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:49:5 + --> $DIR/option_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -33,7 +33,7 @@ LL | x.field.map(|value| x.do_option_nothing(value + captured)); | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` 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:51:5 + --> $DIR/option_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -41,7 +41,7 @@ LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` 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:54:5 + --> $DIR/option_map_unit_fn_fixable.rs:53:5 | LL | x.field.map(|value| do_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -49,7 +49,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` 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:56:5 + --> $DIR/option_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -57,7 +57,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` 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:58:5 + --> $DIR/option_map_unit_fn_fixable.rs:57:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -65,7 +65,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` 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:60:5 + --> $DIR/option_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -73,7 +73,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` 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:63:5 + --> $DIR/option_map_unit_fn_fixable.rs:62:5 | LL | x.field.map(|value| diverge(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -81,7 +81,7 @@ LL | x.field.map(|value| diverge(value + captured)); | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` 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:65:5 + --> $DIR/option_map_unit_fn_fixable.rs:64:5 | LL | x.field.map(|value| { diverge(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -89,7 +89,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` 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:67:5 + --> $DIR/option_map_unit_fn_fixable.rs:66:5 | LL | x.field.map(|value| { diverge(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -97,7 +97,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` 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:69:5 + --> $DIR/option_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -105,7 +105,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` 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:74:5 + --> $DIR/option_map_unit_fn_fixable.rs:73:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -113,7 +113,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` 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:76:5 + --> $DIR/option_map_unit_fn_fixable.rs:75:5 | LL | x.field.map(|value| { plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -121,7 +121,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` 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:78:5 + --> $DIR/option_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -129,7 +129,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` 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:81:5 + --> $DIR/option_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -137,7 +137,7 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | help: try this: `if let Some(ref value) = x.field { 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 + --> $DIR/option_map_unit_fn_fixable.rs:82:5 | LL | option().map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^^- @@ -145,7 +145,7 @@ LL | option().map(do_nothing); | help: try this: `if let Some(a) = option() { do_nothing(a) }` 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 + --> $DIR/option_map_unit_fn_fixable.rs:84:5 | LL | option().map(|value| println!("{:?}", value)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- diff --git a/tests/ui/option_take_on_temporary.fixed b/tests/ui/option_take_on_temporary.fixed deleted file mode 100644 index 29691e81666f..000000000000 --- a/tests/ui/option_take_on_temporary.fixed +++ /dev/null @@ -1,15 +0,0 @@ -// run-rustfix - -fn main() { - println!("Testing non erroneous option_take_on_temporary"); - let mut option = Some(1); - let _ = Box::new(move || option.take().unwrap()); - - println!("Testing non erroneous option_take_on_temporary"); - let x = Some(3); - x.as_ref(); - - println!("Testing erroneous option_take_on_temporary"); - let x = Some(3); - x.as_ref(); -} diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 5991188ab637..896430780ea8 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] +#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index c353b41e4495..2473163d4fd2 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] +#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index e3dab4cb1477..113ba150c619 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:49:22 + --> $DIR/or_fun_call.rs:48:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)` @@ -7,151 +7,151 @@ LL | with_constructor.unwrap_or(make()); = note: `-D clippy::or-fun-call` implied by `-D warnings` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:52:14 + --> $DIR/or_fun_call.rs:51:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:55:21 + --> $DIR/or_fun_call.rs:54:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:58:14 + --> $DIR/or_fun_call.rs:57:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:61:19 + --> $DIR/or_fun_call.rs:60:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:64:24 + --> $DIR/or_fun_call.rs:63:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:67:23 + --> $DIR/or_fun_call.rs:66:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:70:18 + --> $DIR/or_fun_call.rs:69:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(::default)` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:73:18 + --> $DIR/or_fun_call.rs:72:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:76:14 + --> $DIR/or_fun_call.rs:75:14 | LL | with_vec.unwrap_or(vec![]); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:79:21 + --> $DIR/or_fun_call.rs:78:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:82:19 + --> $DIR/or_fun_call.rs:81:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:85:23 + --> $DIR/or_fun_call.rs:84:23 | LL | map_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:88:21 + --> $DIR/or_fun_call.rs:87:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:91:25 + --> $DIR/or_fun_call.rs:90:25 | LL | btree_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:94:21 + --> $DIR/or_fun_call.rs:93:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:102:21 + --> $DIR/or_fun_call.rs:101:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:104:21 + --> $DIR/or_fun_call.rs:103:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:128:35 + --> $DIR/or_fun_call.rs:127:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:167:14 + --> $DIR/or_fun_call.rs:166:14 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:173:14 + --> $DIR/or_fun_call.rs:172:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:175:14 + --> $DIR/or_fun_call.rs:174:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:189:14 + --> $DIR/or_fun_call.rs:188:14 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:202:14 + --> $DIR/or_fun_call.rs:201:14 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:214:14 + --> $DIR/or_fun_call.rs:213:14 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:225:10 + --> $DIR/or_fun_call.rs:224:10 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` diff --git a/tests/ui/panic_in_result_fn_assertions.rs b/tests/ui/panic_in_result_fn_assertions.rs index ffdf8288adc7..08ab4d8681ed 100644 --- a/tests/ui/panic_in_result_fn_assertions.rs +++ b/tests/ui/panic_in_result_fn_assertions.rs @@ -1,5 +1,5 @@ #![warn(clippy::panic_in_result_fn)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] struct A; diff --git a/tests/ui/panic_in_result_fn_debug_assertions.rs b/tests/ui/panic_in_result_fn_debug_assertions.rs index c4fcd7e70944..df89d8c50246 100644 --- a/tests/ui/panic_in_result_fn_debug_assertions.rs +++ b/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -1,5 +1,5 @@ #![warn(clippy::panic_in_result_fn)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] // debug_assert should never trigger the `panic_in_result_fn` lint diff --git a/tests/ui/patterns.fixed b/tests/ui/patterns.fixed index f22388154499..cd69014326eb 100644 --- a/tests/ui/patterns.fixed +++ b/tests/ui/patterns.fixed @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused)] #![warn(clippy::all)] +#![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn main() { let v = Some(true); diff --git a/tests/ui/patterns.rs b/tests/ui/patterns.rs index 5848ecd38d98..9128da420c0d 100644 --- a/tests/ui/patterns.rs +++ b/tests/ui/patterns.rs @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused)] #![warn(clippy::all)] +#![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn main() { let v = Some(true); diff --git a/tests/ui/patterns.stderr b/tests/ui/patterns.stderr index af067580688b..2c46b4eb593e 100644 --- a/tests/ui/patterns.stderr +++ b/tests/ui/patterns.stderr @@ -1,5 +1,5 @@ error: the `y @ _` pattern can be written as just `y` - --> $DIR/patterns.rs:10:9 + --> $DIR/patterns.rs:11:9 | LL | y @ _ => (), | ^^^^^ help: try: `y` @@ -7,13 +7,13 @@ LL | y @ _ => (), = note: `-D clippy::redundant-pattern` implied by `-D warnings` error: the `x @ _` pattern can be written as just `x` - --> $DIR/patterns.rs:25:9 + --> $DIR/patterns.rs:26:9 | LL | ref mut x @ _ => { | ^^^^^^^^^^^^^ help: try: `ref mut x` error: the `x @ _` pattern can be written as just `x` - --> $DIR/patterns.rs:33:9 + --> $DIR/patterns.rs:34:9 | LL | ref x @ _ => println!("vec: {:?}", x), | ^^^^^^^^^ help: try: `ref x` diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index 3f6639c14585..86f908f66b8f 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -1,4 +1,5 @@ #![warn(clippy::print_literal)] +#![allow(clippy::uninlined_format_args)] fn main() { // these should be fine diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index 23e6dbc3e341..6404dacdafa5 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -1,5 +1,5 @@ error: literal with an empty format string - --> $DIR/print_literal.rs:26:24 + --> $DIR/print_literal.rs:27:24 | LL | print!("Hello {}", "world"); | ^^^^^^^ @@ -12,7 +12,7 @@ LL + print!("Hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:27:36 + --> $DIR/print_literal.rs:28:36 | LL | println!("Hello {} {}", world, "world"); | ^^^^^^^ @@ -24,7 +24,7 @@ LL + println!("Hello {} world", world); | error: literal with an empty format string - --> $DIR/print_literal.rs:28:26 + --> $DIR/print_literal.rs:29:26 | LL | println!("Hello {}", "world"); | ^^^^^^^ @@ -36,7 +36,7 @@ LL + println!("Hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:29:26 + --> $DIR/print_literal.rs:30:26 | LL | println!("{} {:.4}", "a literal", 5); | ^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL + println!("a literal {:.4}", 5); | error: literal with an empty format string - --> $DIR/print_literal.rs:34:25 + --> $DIR/print_literal.rs:35:25 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ @@ -60,7 +60,7 @@ LL + println!("hello {1}", "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:34:34 + --> $DIR/print_literal.rs:35:34 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ @@ -72,7 +72,7 @@ LL + println!("{0} world", "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:35:34 + --> $DIR/print_literal.rs:36:34 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ @@ -84,7 +84,7 @@ LL + println!("world {0}", "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:35:25 + --> $DIR/print_literal.rs:36:25 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ @@ -96,7 +96,7 @@ LL + println!("{1} hello", "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:38:35 + --> $DIR/print_literal.rs:39:35 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ @@ -108,7 +108,7 @@ LL + println!("hello {bar}", bar = "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:38:50 + --> $DIR/print_literal.rs:39:50 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ @@ -120,7 +120,7 @@ LL + println!("{foo} world", foo = "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:39:50 + --> $DIR/print_literal.rs:40:50 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ @@ -132,7 +132,7 @@ LL + println!("world {foo}", foo = "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:39:35 + --> $DIR/print_literal.rs:40:35 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ diff --git a/tests/ui/ptr_offset_with_cast.fixed b/tests/ui/ptr_offset_with_cast.fixed index 718e391e8bf6..c57e2990fb95 100644 --- a/tests/ui/ptr_offset_with_cast.fixed +++ b/tests/ui/ptr_offset_with_cast.fixed @@ -1,4 +1,5 @@ // run-rustfix +#![allow(clippy::unnecessary_cast)] fn main() { let vec = vec![b'a', b'b', b'c']; diff --git a/tests/ui/ptr_offset_with_cast.rs b/tests/ui/ptr_offset_with_cast.rs index f613742c741e..3de7997acddd 100644 --- a/tests/ui/ptr_offset_with_cast.rs +++ b/tests/ui/ptr_offset_with_cast.rs @@ -1,4 +1,5 @@ // run-rustfix +#![allow(clippy::unnecessary_cast)] fn main() { let vec = vec![b'a', b'b', b'c']; diff --git a/tests/ui/ptr_offset_with_cast.stderr b/tests/ui/ptr_offset_with_cast.stderr index fd45224ca067..3ba40593d644 100644 --- a/tests/ui/ptr_offset_with_cast.stderr +++ b/tests/ui/ptr_offset_with_cast.stderr @@ -1,5 +1,5 @@ error: use of `offset` with a `usize` casted to an `isize` - --> $DIR/ptr_offset_with_cast.rs:12:17 + --> $DIR/ptr_offset_with_cast.rs:13:17 | LL | let _ = ptr.offset(offset_usize as isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` @@ -7,7 +7,7 @@ LL | let _ = ptr.offset(offset_usize as isize); = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings` error: use of `wrapping_offset` with a `usize` casted to an `isize` - --> $DIR/ptr_offset_with_cast.rs:16:17 + --> $DIR/ptr_offset_with_cast.rs:17:17 | LL | let _ = ptr.wrapping_offset(offset_usize as isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 57f23bd1916f..993389232cc2 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -223,3 +223,12 @@ fn pattern() -> Result<(), PatternedError> { } fn main() {} + +// should not lint, `?` operator not available in const context +const fn issue9175(option: Option<()>) -> Option<()> { + if option.is_none() { + return None; + } + //stuff + Some(()) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 436f027c215d..9ae0d88829af 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -259,3 +259,12 @@ fn pattern() -> Result<(), PatternedError> { } fn main() {} + +// should not lint, `?` operator not available in const context +const fn issue9175(option: Option<()>) -> Option<()> { + if option.is_none() { + return None; + } + //stuff + Some(()) +} diff --git a/tests/ui/recursive_format_impl.rs b/tests/ui/recursive_format_impl.rs index cb6ba36b14c8..b92490b4c523 100644 --- a/tests/ui/recursive_format_impl.rs +++ b/tests/ui/recursive_format_impl.rs @@ -1,9 +1,10 @@ #![warn(clippy::recursive_format_impl)] #![allow( + clippy::borrow_deref_ref, + clippy::deref_addrof, clippy::inherent_to_string_shadow_display, clippy::to_string_in_format_args, - clippy::deref_addrof, - clippy::borrow_deref_ref + clippy::uninlined_format_args )] use std::fmt; diff --git a/tests/ui/recursive_format_impl.stderr b/tests/ui/recursive_format_impl.stderr index 84ce69df5669..8a58b9a3b178 100644 --- a/tests/ui/recursive_format_impl.stderr +++ b/tests/ui/recursive_format_impl.stderr @@ -1,5 +1,5 @@ error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion - --> $DIR/recursive_format_impl.rs:30:25 + --> $DIR/recursive_format_impl.rs:31:25 | LL | write!(f, "{}", self.to_string()) | ^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | write!(f, "{}", self.to_string()) = note: `-D clippy::recursive-format-impl` implied by `-D warnings` error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:74:9 + --> $DIR/recursive_format_impl.rs:75:9 | LL | write!(f, "{}", self) | ^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | write!(f, "{}", self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:83:9 + --> $DIR/recursive_format_impl.rs:84:9 | LL | write!(f, "{}", &self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | write!(f, "{}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:89:9 + --> $DIR/recursive_format_impl.rs:90:9 | LL | write!(f, "{:?}", &self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | write!(f, "{:?}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:98:9 + --> $DIR/recursive_format_impl.rs:99:9 | LL | write!(f, "{}", &&&self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | write!(f, "{}", &&&self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:172:9 + --> $DIR/recursive_format_impl.rs:173:9 | LL | write!(f, "{}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | write!(f, "{}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:178:9 + --> $DIR/recursive_format_impl.rs:179:9 | LL | write!(f, "{:?}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | write!(f, "{:?}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:194:9 + --> $DIR/recursive_format_impl.rs:195:9 | LL | write!(f, "{}", *self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | write!(f, "{}", *self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:210:9 + --> $DIR/recursive_format_impl.rs:211:9 | LL | write!(f, "{}", **&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL | write!(f, "{}", **&&*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:226:9 + --> $DIR/recursive_format_impl.rs:227:9 | LL | write!(f, "{}", &&**&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index da52c0acf93b..00b427450935 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -1,8 +1,8 @@ // run-rustfix // rustfix-only-machine-applicable - #![feature(lint_reasons)] -#![allow(clippy::implicit_clone, clippy::drop_non_drop)] +#![allow(clippy::drop_non_drop, clippy::implicit_clone, clippy::uninlined_format_args)] + use std::ffi::OsString; use std::path::Path; diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index 5867d019dbb7..f899127db8d0 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -1,8 +1,8 @@ // run-rustfix // rustfix-only-machine-applicable - #![feature(lint_reasons)] -#![allow(clippy::implicit_clone, clippy::drop_non_drop)] +#![allow(clippy::drop_non_drop, clippy::implicit_clone, clippy::uninlined_format_args)] + use std::ffi::OsString; use std::path::Path; diff --git a/tests/ui/redundant_pattern_matching_ipaddr.fixed b/tests/ui/redundant_pattern_matching_ipaddr.fixed index acc8de5f41ee..21bae909555c 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.fixed +++ b/tests/ui/redundant_pattern_matching_ipaddr.fixed @@ -1,8 +1,11 @@ // run-rustfix - -#![warn(clippy::all)] -#![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![warn(clippy::all, clippy::redundant_pattern_matching)] +#![allow(unused_must_use)] +#![allow( + clippy::match_like_matches_macro, + clippy::needless_bool, + clippy::uninlined_format_args +)] use std::net::{ IpAddr::{self, V4, V6}, diff --git a/tests/ui/redundant_pattern_matching_ipaddr.rs b/tests/ui/redundant_pattern_matching_ipaddr.rs index 678d91ce93ac..4dd9171677ec 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.rs +++ b/tests/ui/redundant_pattern_matching_ipaddr.rs @@ -1,8 +1,11 @@ // run-rustfix - -#![warn(clippy::all)] -#![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![warn(clippy::all, clippy::redundant_pattern_matching)] +#![allow(unused_must_use)] +#![allow( + clippy::match_like_matches_macro, + clippy::needless_bool, + clippy::uninlined_format_args +)] use std::net::{ IpAddr::{self, V4, V6}, diff --git a/tests/ui/redundant_pattern_matching_ipaddr.stderr b/tests/ui/redundant_pattern_matching_ipaddr.stderr index caf458cd862e..536b589de54c 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.stderr +++ b/tests/ui/redundant_pattern_matching_ipaddr.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:14:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:17:12 | LL | if let V4(_) = &ipaddr {} | -------^^^^^---------- help: try this: `if ipaddr.is_ipv4()` @@ -7,31 +7,31 @@ LL | if let V4(_) = &ipaddr {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:16:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:19:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:18:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:21:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:20:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:23:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:22:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:25:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:32:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:35:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:37:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:40:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:42:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:45:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:47:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:50:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => true, @@ -67,49 +67,49 @@ LL | | }; | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:52:20 + --> $DIR/redundant_pattern_matching_ipaddr.rs:55:20 | LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:60:20 + --> $DIR/redundant_pattern_matching_ipaddr.rs:63:20 | LL | let _ = if let V4(_) = gen_ipaddr() { | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:62:19 + --> $DIR/redundant_pattern_matching_ipaddr.rs:65:19 | LL | } else if let V6(_) = gen_ipaddr() { | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:74:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:77:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:76:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:79:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:78:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:81:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:80:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:83:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:82:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:85:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => true, @@ -118,7 +118,7 @@ LL | | }; | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:87:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:90:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => false, diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed index 83c783385efe..b88c5d0bec82 100644 --- a/tests/ui/redundant_pattern_matching_result.fixed +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -1,14 +1,13 @@ // run-rustfix - #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] +#![allow(deprecated, unused_must_use)] #![allow( - unused_must_use, - clippy::needless_bool, + clippy::if_same_then_else, clippy::match_like_matches_macro, - clippy::unnecessary_wraps, - deprecated, - clippy::if_same_then_else + clippy::needless_bool, + clippy::uninlined_format_args, + clippy::unnecessary_wraps )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.rs b/tests/ui/redundant_pattern_matching_result.rs index e06d4485ae4f..5949cb2271c6 100644 --- a/tests/ui/redundant_pattern_matching_result.rs +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -1,14 +1,13 @@ // run-rustfix - #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] +#![allow(deprecated, unused_must_use)] #![allow( - unused_must_use, - clippy::needless_bool, + clippy::if_same_then_else, clippy::match_like_matches_macro, - clippy::unnecessary_wraps, - deprecated, - clippy::if_same_then_else + clippy::needless_bool, + clippy::uninlined_format_args, + clippy::unnecessary_wraps )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr index d674d061e4dd..e6afe9eb78ea 100644 --- a/tests/ui/redundant_pattern_matching_result.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:16:12 + --> $DIR/redundant_pattern_matching_result.rs:15:12 | LL | if let Ok(_) = &result {} | -------^^^^^---------- help: try this: `if result.is_ok()` @@ -7,31 +7,31 @@ LL | if let Ok(_) = &result {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:18:12 + --> $DIR/redundant_pattern_matching_result.rs:17:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:20:12 + --> $DIR/redundant_pattern_matching_result.rs:19:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:22:15 + --> $DIR/redundant_pattern_matching_result.rs:21:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:24:15 + --> $DIR/redundant_pattern_matching_result.rs:23:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:34:5 + --> $DIR/redundant_pattern_matching_result.rs:33:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:39:5 + --> $DIR/redundant_pattern_matching_result.rs:38:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:44:5 + --> $DIR/redundant_pattern_matching_result.rs:43:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:49:5 + --> $DIR/redundant_pattern_matching_result.rs:48:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -67,73 +67,73 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:54:20 + --> $DIR/redundant_pattern_matching_result.rs:53:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:60:20 + --> $DIR/redundant_pattern_matching_result.rs:59:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:62:19 + --> $DIR/redundant_pattern_matching_result.rs:61:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:85:19 + --> $DIR/redundant_pattern_matching_result.rs:84:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while (r#try!(result_opt())).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:86:16 + --> $DIR/redundant_pattern_matching_result.rs:85:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if (r#try!(result_opt())).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:92:12 + --> $DIR/redundant_pattern_matching_result.rs:91:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:93:15 + --> $DIR/redundant_pattern_matching_result.rs:92:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:111:12 + --> $DIR/redundant_pattern_matching_result.rs:110:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:113:12 + --> $DIR/redundant_pattern_matching_result.rs:112:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:115:15 + --> $DIR/redundant_pattern_matching_result.rs:114:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:117:15 + --> $DIR/redundant_pattern_matching_result.rs:116:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:119:5 + --> $DIR/redundant_pattern_matching_result.rs:118:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:124:5 + --> $DIR/redundant_pattern_matching_result.rs:123:5 | LL | / match Err::(42) { LL | | Ok(_) => false, diff --git a/tests/ui/result_map_unit_fn_fixable.fixed b/tests/ui/result_map_unit_fn_fixable.fixed index 14c331f67e73..d8b56237e983 100644 --- a/tests/ui/result_map_unit_fn_fixable.fixed +++ b/tests/ui/result_map_unit_fn_fixable.fixed @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::result_map_unit_fn)] #![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn do_nothing(_: T) {} diff --git a/tests/ui/result_map_unit_fn_fixable.rs b/tests/ui/result_map_unit_fn_fixable.rs index 8b0fca9ece1a..44f50d21109c 100644 --- a/tests/ui/result_map_unit_fn_fixable.rs +++ b/tests/ui/result_map_unit_fn_fixable.rs @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::result_map_unit_fn)] #![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn do_nothing(_: T) {} diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed index 79e482eec303..c67edb36c67a 100644 --- a/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] const ANSWER: i32 = 42; diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs index b2e8bf33771a..0a4fef5bfe87 100644 --- a/tests/ui/reversed_empty_ranges_fixable.rs +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] const ANSWER: i32 = 42; diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 2d1bfe62c923..c2495ea95f97 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:9:5 + --> $DIR/reversed_empty_ranges_fixable.rs:10:5 | LL | (42..=21).for_each(|x| println!("{}", x)); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:10:13 + --> $DIR/reversed_empty_ranges_fixable.rs:11:13 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect:: $DIR/reversed_empty_ranges_fixable.rs:12:14 + --> $DIR/reversed_empty_ranges_fixable.rs:13:14 | LL | for _ in -21..=-42 {} | ^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for _ in (-42..=-21).rev() {} | ~~~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:13:14 + --> $DIR/reversed_empty_ranges_fixable.rs:14:14 | LL | for _ in 42u32..21u32 {} | ^^^^^^^^^^^^ diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.fixed b/tests/ui/reversed_empty_ranges_loops_fixable.fixed index f1503ed6d12f..78401e463d50 100644 --- a/tests/ui/reversed_empty_ranges_loops_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_loops_fixable.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] fn main() { const MAX_LEN: usize = 42; diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.rs b/tests/ui/reversed_empty_ranges_loops_fixable.rs index a733788dc22c..f9e0f7fcd6db 100644 --- a/tests/ui/reversed_empty_ranges_loops_fixable.rs +++ b/tests/ui/reversed_empty_ranges_loops_fixable.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] fn main() { const MAX_LEN: usize = 42; diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.stderr b/tests/ui/reversed_empty_ranges_loops_fixable.stderr index a135da488ffd..dfc52e64c751 100644 --- a/tests/ui/reversed_empty_ranges_loops_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_loops_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:7:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:8:14 | LL | for i in 10..0 { | ^^^^^ @@ -11,7 +11,7 @@ LL | for i in (0..10).rev() { | ~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:11:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:12:14 | LL | for i in 10..=0 { | ^^^^^^ @@ -22,7 +22,7 @@ LL | for i in (0..=10).rev() { | ~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:15:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:16:14 | LL | for i in MAX_LEN..0 { | ^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for i in (0..MAX_LEN).rev() { | ~~~~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:34:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:35:14 | LL | for i in (10..0).map(|x| x * 2) { | ^^^^^^^ @@ -44,7 +44,7 @@ LL | for i in (0..10).rev().map(|x| x * 2) { | ~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:39:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:40:14 | LL | for i in 10..5 + 4 { | ^^^^^^^^^ @@ -55,7 +55,7 @@ LL | for i in (5 + 4..10).rev() { | ~~~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:43:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:44:14 | LL | for i in (5 + 2)..(3 - 1) { | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/reversed_empty_ranges_loops_unfixable.rs b/tests/ui/reversed_empty_ranges_loops_unfixable.rs index c4c572244168..50264ef68cc0 100644 --- a/tests/ui/reversed_empty_ranges_loops_unfixable.rs +++ b/tests/ui/reversed_empty_ranges_loops_unfixable.rs @@ -1,4 +1,5 @@ #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] fn main() { for i in 5..5 { diff --git a/tests/ui/reversed_empty_ranges_loops_unfixable.stderr b/tests/ui/reversed_empty_ranges_loops_unfixable.stderr index 30095d20cfd4..4490ff35f5a6 100644 --- a/tests/ui/reversed_empty_ranges_loops_unfixable.stderr +++ b/tests/ui/reversed_empty_ranges_loops_unfixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_unfixable.rs:4:14 + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:5:14 | LL | for i in 5..5 { | ^^^^ @@ -7,7 +7,7 @@ LL | for i in 5..5 { = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_unfixable.rs:8:14 + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:9:14 | LL | for i in (5 + 2)..(8 - 1) { | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index a48829caac01..e6198a1bc9a0 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -1,8 +1,14 @@ #![feature(adt_const_params)] -#![allow(incomplete_features)] #![warn(clippy::same_functions_in_if_condition)] -#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`. -#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks +// ifs_same_cond warning is different from `ifs_same_cond`. +// clippy::if_same_then_else, clippy::comparison_chain -- all empty blocks +#![allow(incomplete_features)] +#![allow( + clippy::comparison_chain, + clippy::if_same_then_else, + clippy::ifs_same_cond, + clippy::uninlined_format_args +)] fn function() -> bool { true diff --git a/tests/ui/same_functions_in_if_condition.stderr b/tests/ui/same_functions_in_if_condition.stderr index 3901546cbd65..f352ade150ee 100644 --- a/tests/ui/same_functions_in_if_condition.stderr +++ b/tests/ui/same_functions_in_if_condition.stderr @@ -1,72 +1,72 @@ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:31:15 + --> $DIR/same_functions_in_if_condition.rs:37:15 | LL | } else if function() { | ^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:30:8 + --> $DIR/same_functions_in_if_condition.rs:36:8 | LL | if function() { | ^^^^^^^^^^ = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings` error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:36:15 + --> $DIR/same_functions_in_if_condition.rs:42:15 | LL | } else if fn_arg(a) { | ^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:35:8 + --> $DIR/same_functions_in_if_condition.rs:41:8 | LL | if fn_arg(a) { | ^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:41:15 + --> $DIR/same_functions_in_if_condition.rs:47:15 | LL | } else if obj.method() { | ^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:40:8 + --> $DIR/same_functions_in_if_condition.rs:46:8 | LL | if obj.method() { | ^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:46:15 + --> $DIR/same_functions_in_if_condition.rs:52:15 | LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:45:8 + --> $DIR/same_functions_in_if_condition.rs:51:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:53:15 + --> $DIR/same_functions_in_if_condition.rs:59:15 | LL | } else if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:51:8 + --> $DIR/same_functions_in_if_condition.rs:57:8 | LL | if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:58:15 + --> $DIR/same_functions_in_if_condition.rs:64:15 | LL | } else if v.len() == 42 { | ^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:56:8 + --> $DIR/same_functions_in_if_condition.rs:62:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ diff --git a/tests/ui/semicolon_if_nothing_returned.rs b/tests/ui/semicolon_if_nothing_returned.rs index c4dfbd9210e0..4ab7dbab59cf 100644 --- a/tests/ui/semicolon_if_nothing_returned.rs +++ b/tests/ui/semicolon_if_nothing_returned.rs @@ -1,5 +1,5 @@ #![warn(clippy::semicolon_if_nothing_returned)] -#![allow(clippy::redundant_closure)] +#![allow(clippy::redundant_closure, clippy::uninlined_format_args)] fn get_unit() {} diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.stderr index d2f41e3f934a..161dd66b086e 100644 --- a/tests/ui/should_impl_trait/method_list_1.stderr +++ b/tests/ui/should_impl_trait/method_list_1.stderr @@ -99,6 +99,16 @@ LL | | } | = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name +error: method `default` can be confused for the standard trait method `std::default::Default::default` + --> $DIR/method_list_1.rs:65:5 + | +LL | / pub fn default() -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name + error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` --> $DIR/method_list_1.rs:69:5 | @@ -139,5 +149,5 @@ LL | | } | = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index 84ecf1ea53ed..c65df9ece38c 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -1,11 +1,8 @@ // FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934 // // run-rustfix - #![warn(clippy::significant_drop_in_scrutinee)] -#![allow(clippy::single_match)] -#![allow(clippy::match_single_binding)] -#![allow(unused_assignments)] -#![allow(dead_code)] +#![allow(dead_code, unused_assignments)] +#![allow(clippy::match_single_binding, clippy::single_match, clippy::uninlined_format_args)] use std::num::ParseIntError; use std::ops::Deref; diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index f1ed808ba087..75063a8c987e 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -1,5 +1,5 @@ error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:59:11 + --> $DIR/significant_drop_in_scrutinee.rs:56:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:145:11 + --> $DIR/significant_drop_in_scrutinee.rs:142:11 | LL | match s.lock_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -38,7 +38,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:166:11 + --> $DIR/significant_drop_in_scrutinee.rs:163:11 | LL | match s.lock_m_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:214:11 + --> $DIR/significant_drop_in_scrutinee.rs:211:11 | LL | match counter.temp_increment().len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:237:16 + --> $DIR/significant_drop_in_scrutinee.rs:234:16 | LL | match (mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL ~ match (value, true) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:246:22 + --> $DIR/significant_drop_in_scrutinee.rs:243:22 | LL | match (true, mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL ~ match (true, value, true) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:256:16 + --> $DIR/significant_drop_in_scrutinee.rs:253:16 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL ~ match (value, true, mutex2.lock().unwrap().s.len()) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:256:54 + --> $DIR/significant_drop_in_scrutinee.rs:253:54 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -153,7 +153,7 @@ LL ~ match (mutex1.lock().unwrap().s.len(), true, value) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:267:15 + --> $DIR/significant_drop_in_scrutinee.rs:264:15 | LL | match mutex3.lock().unwrap().s.as_str() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:277:22 + --> $DIR/significant_drop_in_scrutinee.rs:274:22 | LL | match (true, mutex3.lock().unwrap().s.as_str()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -185,7 +185,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:296:11 + --> $DIR/significant_drop_in_scrutinee.rs:293:11 | LL | match mutex.lock().unwrap().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -204,7 +204,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:303:11 + --> $DIR/significant_drop_in_scrutinee.rs:300:11 | LL | match 1 < mutex.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -223,7 +223,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:321:11 + --> $DIR/significant_drop_in_scrutinee.rs:318:11 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -244,7 +244,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:332:11 + --> $DIR/significant_drop_in_scrutinee.rs:329:11 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -265,7 +265,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:367:11 + --> $DIR/significant_drop_in_scrutinee.rs:364:11 | LL | match get_mutex_guard().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -284,7 +284,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:384:11 + --> $DIR/significant_drop_in_scrutinee.rs:381:11 | LL | match match i { | ___________^ @@ -316,7 +316,7 @@ LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:410:11 + --> $DIR/significant_drop_in_scrutinee.rs:407:11 | LL | match if i > 1 { | ___________^ @@ -349,7 +349,7 @@ LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:464:11 + --> $DIR/significant_drop_in_scrutinee.rs:461:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -367,7 +367,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:492:11 + --> $DIR/significant_drop_in_scrutinee.rs:489:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -380,7 +380,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:511:11 + --> $DIR/significant_drop_in_scrutinee.rs:508:11 | LL | match mutex.lock().unwrap().i = i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -399,7 +399,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:517:11 + --> $DIR/significant_drop_in_scrutinee.rs:514:11 | LL | match i = mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -418,7 +418,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:523:11 + --> $DIR/significant_drop_in_scrutinee.rs:520:11 | LL | match mutex.lock().unwrap().i += 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -437,7 +437,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:529:11 + --> $DIR/significant_drop_in_scrutinee.rs:526:11 | LL | match i += mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -456,7 +456,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:592:11 + --> $DIR/significant_drop_in_scrutinee.rs:589:11 | LL | match rwlock.read().unwrap().to_number() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -467,7 +467,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression - --> $DIR/significant_drop_in_scrutinee.rs:602:14 + --> $DIR/significant_drop_in_scrutinee.rs:599:14 | LL | for s in rwlock.read().unwrap().iter() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -478,7 +478,7 @@ LL | } = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:617:11 + --> $DIR/significant_drop_in_scrutinee.rs:614:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index dd148edf5292..d0c9b7b5663e 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -1,4 +1,5 @@ #![warn(clippy::single_match)] +#![allow(clippy::uninlined_format_args)] fn dummy() {} diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 4d2b9ec5f903..7cecc1b73950 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:8:5 + --> $DIR/single_match.rs:9:5 | LL | / match x { LL | | Some(y) => { @@ -18,7 +18,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:16:5 + --> $DIR/single_match.rs:17:5 | LL | / match x { LL | | // Note the missing block braces. @@ -30,7 +30,7 @@ LL | | } | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:25:5 + --> $DIR/single_match.rs:26:5 | LL | / match z { LL | | (2..=3, 7..=9) => dummy(), @@ -39,7 +39,7 @@ LL | | }; | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:54:5 + --> $DIR/single_match.rs:55:5 | LL | / match x { LL | | Some(y) => dummy(), @@ -48,7 +48,7 @@ LL | | }; | |_____^ help: try this: `if let Some(y) = x { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:59:5 + --> $DIR/single_match.rs:60:5 | LL | / match y { LL | | Ok(y) => dummy(), @@ -57,7 +57,7 @@ LL | | }; | |_____^ help: try this: `if let Ok(y) = y { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:66:5 + --> $DIR/single_match.rs:67:5 | LL | / match c { LL | | Cow::Borrowed(..) => dummy(), @@ -66,7 +66,7 @@ LL | | }; | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:87:5 + --> $DIR/single_match.rs:88:5 | LL | / match x { LL | | "test" => println!(), @@ -75,7 +75,7 @@ LL | | } | |_____^ help: try this: `if x == "test" { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:100:5 + --> $DIR/single_match.rs:101:5 | LL | / match x { LL | | Foo::A => println!(), @@ -84,7 +84,7 @@ LL | | } | |_____^ help: try this: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:106:5 + --> $DIR/single_match.rs:107:5 | LL | / match x { LL | | FOO_C => println!(), @@ -93,7 +93,7 @@ LL | | } | |_____^ help: try this: `if x == FOO_C { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:111:5 + --> $DIR/single_match.rs:112:5 | LL | / match &&x { LL | | Foo::A => println!(), @@ -102,7 +102,7 @@ LL | | } | |_____^ help: try this: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:117:5 + --> $DIR/single_match.rs:118:5 | LL | / match &x { LL | | Foo::A => println!(), @@ -111,7 +111,7 @@ LL | | } | |_____^ help: try this: `if x == &Foo::A { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:134:5 + --> $DIR/single_match.rs:135:5 | LL | / match x { LL | | Bar::A => println!(), @@ -120,7 +120,7 @@ LL | | } | |_____^ help: try this: `if let Bar::A = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:142:5 + --> $DIR/single_match.rs:143:5 | LL | / match x { LL | | None => println!(), @@ -129,7 +129,7 @@ LL | | }; | |_____^ help: try this: `if let None = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:164:5 + --> $DIR/single_match.rs:165:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -138,7 +138,7 @@ LL | | } | |_____^ help: try this: `if let (Some(_), _) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:170:5 + --> $DIR/single_match.rs:171:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -147,7 +147,7 @@ LL | | } | |_____^ help: try this: `if let (Some(E::V), _) = x { todo!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:176:5 + --> $DIR/single_match.rs:177:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 70d6febb71f9..5d03f77e9326 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,8 +1,6 @@ // aux-build: proc_macro_with_span.rs - #![warn(clippy::single_match_else)] -#![allow(clippy::needless_return)] -#![allow(clippy::no_effect)] +#![allow(clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] extern crate proc_macro_with_span; use proc_macro_with_span::with_span; diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 38fd9c6a6782..62876a55dc61 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:19:13 + --> $DIR/single_match_else.rs:17:13 | LL | let _ = match ExprNode::Butterflies { | _____________^ @@ -21,7 +21,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:84:5 + --> $DIR/single_match_else.rs:82:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -41,7 +41,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:93:5 + --> $DIR/single_match_else.rs:91:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -61,7 +61,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:103:5 + --> $DIR/single_match_else.rs:101:5 | LL | / match Result::::Ok(1) { LL | | Ok(a) => println!("${:?}", a), @@ -81,7 +81,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:112:5 + --> $DIR/single_match_else.rs:110:5 | LL | / match Cow::from("moo") { LL | | Cow::Owned(a) => println!("${:?}", a), diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index 6b27475de4c8..75b114ba0aed 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -24,6 +24,12 @@ fn std_instead_of_core() { let cell_absolute = ::std::cell::Cell::new(8u32); let _ = std::env!("PATH"); + + // do not lint until `error_in_core` is stable + use std::error::Error; + + // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator` + use std::iter::Iterator; } #[warn(clippy::std_instead_of_alloc)] diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index 8138ccb82a00..d2102497350b 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -63,9 +63,17 @@ LL | let cell_absolute = ::std::cell::Cell::new(8u32); | = help: consider importing the item from `core` -error: used import from `std` instead of `alloc` +error: used import from `std` instead of `core` --> $DIR/std_instead_of_core.rs:32:9 | +LL | use std::iter::Iterator; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider importing the item from `core` + +error: used import from `std` instead of `alloc` + --> $DIR/std_instead_of_core.rs:38:9 + | LL | use std::vec; | ^^^^^^^^ | @@ -73,7 +81,7 @@ LL | use std::vec; = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:33:9 + --> $DIR/std_instead_of_core.rs:39:9 | LL | use std::vec::Vec; | ^^^^^^^^^^^^^ @@ -81,7 +89,7 @@ LL | use std::vec::Vec; = help: consider importing the item from `alloc` error: used import from `alloc` instead of `core` - --> $DIR/std_instead_of_core.rs:38:9 + --> $DIR/std_instead_of_core.rs:44:9 | LL | use alloc::slice::from_ref; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -89,5 +97,5 @@ LL | use alloc::slice::from_ref; = help: consider importing the item from `core` = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/toplevel_ref_arg.fixed b/tests/ui/toplevel_ref_arg.fixed index b129d95c5602..09fb66ca37e0 100644 --- a/tests/ui/toplevel_ref_arg.fixed +++ b/tests/ui/toplevel_ref_arg.fixed @@ -1,7 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs - #![warn(clippy::toplevel_ref_arg)] +#![allow(clippy::uninlined_format_args)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/toplevel_ref_arg.rs b/tests/ui/toplevel_ref_arg.rs index 73eb4ff7306f..9d1f2f810983 100644 --- a/tests/ui/toplevel_ref_arg.rs +++ b/tests/ui/toplevel_ref_arg.rs @@ -1,7 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs - #![warn(clippy::toplevel_ref_arg)] +#![allow(clippy::uninlined_format_args)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/trivially_copy_pass_by_ref.rs b/tests/ui/trivially_copy_pass_by_ref.rs index c0c64ebcabfb..af4f3b18443b 100644 --- a/tests/ui/trivially_copy_pass_by_ref.rs +++ b/tests/ui/trivially_copy_pass_by_ref.rs @@ -1,8 +1,11 @@ // normalize-stderr-test "\(\d+ byte\)" -> "(N byte)" // normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)" - #![deny(clippy::trivially_copy_pass_by_ref)] -#![allow(clippy::disallowed_names, clippy::redundant_field_names)] +#![allow( + clippy::disallowed_names, + clippy::redundant_field_names, + clippy::uninlined_format_args +)] #[derive(Copy, Clone)] struct Foo(u32); diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index 66ecb3d8e77a..6a8eca965534 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -1,113 +1,113 @@ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:47:11 + --> $DIR/trivially_copy_pass_by_ref.rs:50:11 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` | note: the lint level is defined here - --> $DIR/trivially_copy_pass_by_ref.rs:4:9 + --> $DIR/trivially_copy_pass_by_ref.rs:3:9 | LL | #![deny(clippy::trivially_copy_pass_by_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:47:20 + --> $DIR/trivially_copy_pass_by_ref.rs:50:20 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:47:29 + --> $DIR/trivially_copy_pass_by_ref.rs:50:29 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:12 + --> $DIR/trivially_copy_pass_by_ref.rs:57:12 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^^ help: consider passing by value instead: `self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:22 + --> $DIR/trivially_copy_pass_by_ref.rs:57:22 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:31 + --> $DIR/trivially_copy_pass_by_ref.rs:57:31 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:40 + --> $DIR/trivially_copy_pass_by_ref.rs:57:40 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:56:16 + --> $DIR/trivially_copy_pass_by_ref.rs:59:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:56:25 + --> $DIR/trivially_copy_pass_by_ref.rs:59:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:56:34 + --> $DIR/trivially_copy_pass_by_ref.rs:59:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:58:35 + --> $DIR/trivially_copy_pass_by_ref.rs:61:35 | LL | fn bad_issue7518(self, other: &Self) {} | ^^^^^ help: consider passing by value instead: `Self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:70:16 + --> $DIR/trivially_copy_pass_by_ref.rs:73:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:70:25 + --> $DIR/trivially_copy_pass_by_ref.rs:73:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:70:34 + --> $DIR/trivially_copy_pass_by_ref.rs:73:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:74:34 + --> $DIR/trivially_copy_pass_by_ref.rs:77:34 | LL | fn trait_method(&self, _foo: &Foo); | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:106:21 + --> $DIR/trivially_copy_pass_by_ref.rs:109:21 | LL | fn foo_never(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:111:15 + --> $DIR/trivially_copy_pass_by_ref.rs:114:15 | LL | fn foo(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:138:37 + --> $DIR/trivially_copy_pass_by_ref.rs:141:37 | LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 { | ^^^^^^^ help: consider passing by value instead: `u32` diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index dc150cf28f2c..194e4fc157ef 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -91,4 +91,10 @@ fn main() { vec1.set_len(200); vec2.set_len(200); } + + // set_len(0) should not be detected + let mut vec: Vec = Vec::with_capacity(1000); + unsafe { + vec.set_len(0); + } } diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed new file mode 100644 index 000000000000..dcf10ed60a25 --- /dev/null +++ b/tests/ui/uninlined_format_args.fixed @@ -0,0 +1,164 @@ +// aux-build:proc_macro_with_span.rs +// run-rustfix +#![feature(custom_inner_attributes)] +#![warn(clippy::uninlined_format_args)] +#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] +#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] + +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + +macro_rules! no_param_str { + () => { + "{}" + }; +} + +macro_rules! my_println { + ($($args:tt),*) => {{ + println!($($args),*) + }}; +} + +macro_rules! my_println_args { + ($($args:tt),*) => {{ + println!("foo: {}", format_args!($($args),*)) + }}; +} + +fn tester(fn_arg: i32) { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + let width = 4; + let prec = 5; + let val = 6; + + // make sure this file hasn't been corrupted with tabs converted to spaces + // let _ = ' '; // <- this is a single tab character + let _: &[u8; 3] = b" "; // <- + + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); // 3 spaces + println!("val='{local_i32}'"); // tab + println!("val='{local_i32}'"); // space+tab + println!("val='{local_i32}'"); // tab+space + println!( + "val='{local_i32}'" + ); + println!("{local_i32}"); + println!("{fn_arg}"); + println!("{local_i32:?}"); + println!("{local_i32:#?}"); + println!("{local_i32:4}"); + println!("{local_i32:04}"); + println!("{local_i32:<3}"); + println!("{local_i32:#010x}"); + println!("{local_f64:.1}"); + println!("Hello {} is {local_f64:.local_i32$}", "x"); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("{local_i32} {local_f64}"); + println!("{local_i32}, {}", local_opt.unwrap()); + println!("{val}"); + println!("{val}"); + println!("{} {1}", local_i32, 42); + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); + println!("val='{fn_arg}'"); + println!("{local_i32}"); + println!("{local_i32:?}"); + println!("{local_i32:#?}"); + println!("{local_i32:04}"); + println!("{local_i32:<3}"); + println!("{local_i32:#010x}"); + println!("{local_f64:.1}"); + println!("{local_i32} {local_i32}"); + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); + println!("{local_i32} {local_f64}"); + println!("{local_f64} {local_i32}"); + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); + println!("{1} {0}", "str", local_i32); + println!("{local_i32}"); + println!("{local_i32:width$}"); + println!("{local_i32:width$}"); + println!("{local_i32:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{val:val$}"); + println!("{val:val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{width:width$}"); + println!("{local_i32:width$}"); + println!("{width:width$}"); + println!("{local_i32:width$}"); + println!("{prec:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{prec:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{width:width$.prec$}"); + println!("{width:width$.prec$}"); + println!("{local_f64:width$.prec$}"); + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); + println!( + "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", + ); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", + local_i32, + width, + prec, + 1 + 2 + ); + println!("Width = {local_i32}, value with width = {local_f64:local_i32$}"); + println!("{local_i32:width$.prec$}"); + println!("{width:width$.prec$}"); + println!("{}", format!("{local_i32}")); + my_println!("{}", local_i32); + my_println_args!("{}", local_i32); + + // these should NOT be modified by the lint + println!(concat!("nope ", "{}"), local_i32); + println!("val='{local_i32}'"); + println!("val='{local_i32 }'"); + println!("val='{local_i32 }'"); // with tab + println!("val='{local_i32\n}'"); + println!("{}", usize::MAX); + println!("{}", local_opt.unwrap()); + println!( + "val='{local_i32 + }'" + ); + println!(no_param_str!(), local_i32); + + println!( + "{val}", + ); + println!("{val}"); + + println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); + println!("{}", with_span!(span val)); +} + +fn main() { + tester(42); +} + +fn _under_msrv() { + #![clippy::msrv = "1.57"] + let local_i32 = 1; + println!("don't expand='{}'", local_i32); +} + +fn _meets_msrv() { + #![clippy::msrv = "1.58"] + let local_i32 = 1; + println!("expand='{local_i32}'"); +} diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs new file mode 100644 index 000000000000..924191f4324c --- /dev/null +++ b/tests/ui/uninlined_format_args.rs @@ -0,0 +1,169 @@ +// aux-build:proc_macro_with_span.rs +// run-rustfix +#![feature(custom_inner_attributes)] +#![warn(clippy::uninlined_format_args)] +#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] +#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] + +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + +macro_rules! no_param_str { + () => { + "{}" + }; +} + +macro_rules! my_println { + ($($args:tt),*) => {{ + println!($($args),*) + }}; +} + +macro_rules! my_println_args { + ($($args:tt),*) => {{ + println!("foo: {}", format_args!($($args),*)) + }}; +} + +fn tester(fn_arg: i32) { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + let width = 4; + let prec = 5; + let val = 6; + + // make sure this file hasn't been corrupted with tabs converted to spaces + // let _ = ' '; // <- this is a single tab character + let _: &[u8; 3] = b" "; // <- + + println!("val='{}'", local_i32); + println!("val='{ }'", local_i32); // 3 spaces + println!("val='{ }'", local_i32); // tab + println!("val='{ }'", local_i32); // space+tab + println!("val='{ }'", local_i32); // tab+space + println!( + "val='{ + }'", + local_i32 + ); + println!("{}", local_i32); + println!("{}", fn_arg); + println!("{:?}", local_i32); + println!("{:#?}", local_i32); + println!("{:4}", local_i32); + println!("{:04}", local_i32); + println!("{:<3}", local_i32); + println!("{:#010x}", local_i32); + println!("{:.1}", local_f64); + println!("Hello {} is {:.*}", "x", local_i32, local_f64); + println!("Hello {} is {:.*}", local_i32, 5, local_f64); + println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + println!("{} {}", local_i32, local_f64); + println!("{}, {}", local_i32, local_opt.unwrap()); + println!("{}", val); + println!("{}", v = val); + println!("{} {1}", local_i32, 42); + println!("val='{\t }'", local_i32); + println!("val='{\n }'", local_i32); + println!("val='{local_i32}'", local_i32 = local_i32); + println!("val='{local_i32}'", local_i32 = fn_arg); + println!("{0}", local_i32); + println!("{0:?}", local_i32); + println!("{0:#?}", local_i32); + println!("{0:04}", local_i32); + println!("{0:<3}", local_i32); + println!("{0:#010x}", local_i32); + println!("{0:.1}", local_f64); + println!("{0} {0}", local_i32); + println!("{1} {} {0} {}", local_i32, local_f64); + println!("{0} {1}", local_i32, local_f64); + println!("{1} {0}", local_i32, local_f64); + println!("{1} {0} {1} {0}", local_i32, local_f64); + println!("{1} {0}", "str", local_i32); + println!("{v}", v = local_i32); + println!("{local_i32:0$}", width); + println!("{local_i32:w$}", w = width); + println!("{local_i32:.0$}", prec); + println!("{local_i32:.p$}", p = prec); + println!("{:0$}", v = val); + println!("{0:0$}", v = val); + println!("{:0$.0$}", v = val); + println!("{0:0$.0$}", v = val); + println!("{0:0$.v$}", v = val); + println!("{0:v$.0$}", v = val); + println!("{v:0$.0$}", v = val); + println!("{v:v$.0$}", v = val); + println!("{v:0$.v$}", v = val); + println!("{v:v$.v$}", v = val); + println!("{:0$}", width); + println!("{:1$}", local_i32, width); + println!("{:w$}", w = width); + println!("{:w$}", local_i32, w = width); + println!("{:.0$}", prec); + println!("{:.1$}", local_i32, prec); + println!("{:.p$}", p = prec); + println!("{:.p$}", local_i32, p = prec); + println!("{:0$.1$}", width, prec); + println!("{:0$.w$}", width, w = prec); + println!("{:1$.2$}", local_f64, width, prec); + println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", + local_i32, width, prec, + ); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", + local_i32, + width, + prec, + 1 + 2 + ); + println!("Width = {}, value with width = {:0$}", local_i32, local_f64); + println!("{:w$.p$}", local_i32, w = width, p = prec); + println!("{:w$.p$}", w = width, p = prec); + println!("{}", format!("{}", local_i32)); + my_println!("{}", local_i32); + my_println_args!("{}", local_i32); + + // these should NOT be modified by the lint + println!(concat!("nope ", "{}"), local_i32); + println!("val='{local_i32}'"); + println!("val='{local_i32 }'"); + println!("val='{local_i32 }'"); // with tab + println!("val='{local_i32\n}'"); + println!("{}", usize::MAX); + println!("{}", local_opt.unwrap()); + println!( + "val='{local_i32 + }'" + ); + println!(no_param_str!(), local_i32); + + println!( + "{}", + // comment with a comma , in it + val, + ); + println!("{}", /* comment with a comma , in it */ val); + + println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); + println!("{}", with_span!(span val)); +} + +fn main() { + tester(42); +} + +fn _under_msrv() { + #![clippy::msrv = "1.57"] + let local_i32 = 1; + println!("don't expand='{}'", local_i32); +} + +fn _meets_msrv() { + #![clippy::msrv = "1.58"] + let local_i32 = 1; + println!("expand='{}'", local_i32); +} diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr new file mode 100644 index 000000000000..1b4dada28dac --- /dev/null +++ b/tests/ui/uninlined_format_args.stderr @@ -0,0 +1,894 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:41:5 + | +LL | println!("val='{}'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:42:5 + | +LL | println!("val='{ }'", local_i32); // 3 spaces + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // 3 spaces +LL + println!("val='{local_i32}'"); // 3 spaces + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:43:5 + | +LL | println!("val='{ }'", local_i32); // tab + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // tab +LL + println!("val='{local_i32}'"); // tab + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:44:5 + | +LL | println!("val='{ }'", local_i32); // space+tab + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // space+tab +LL + println!("val='{local_i32}'"); // space+tab + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:45:5 + | +LL | println!("val='{ }'", local_i32); // tab+space + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // tab+space +LL + println!("val='{local_i32}'"); // tab+space + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:46:5 + | +LL | / println!( +LL | | "val='{ +LL | | }'", +LL | | local_i32 +LL | | ); + | |_____^ + | +help: change this to + | +LL - "val='{ +LL + "val='{local_i32}'" + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:51:5 + | +LL | println!("{}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:52:5 + | +LL | println!("{}", fn_arg); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", fn_arg); +LL + println!("{fn_arg}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:53:5 + | +LL | println!("{:?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:?}", local_i32); +LL + println!("{local_i32:?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:54:5 + | +LL | println!("{:#?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:#?}", local_i32); +LL + println!("{local_i32:#?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:55:5 + | +LL | println!("{:4}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:4}", local_i32); +LL + println!("{local_i32:4}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:56:5 + | +LL | println!("{:04}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:04}", local_i32); +LL + println!("{local_i32:04}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:57:5 + | +LL | println!("{:<3}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:<3}", local_i32); +LL + println!("{local_i32:<3}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:58:5 + | +LL | println!("{:#010x}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:#010x}", local_i32); +LL + println!("{local_i32:#010x}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:59:5 + | +LL | println!("{:.1}", local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.1}", local_f64); +LL + println!("{local_f64:.1}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:60:5 + | +LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); +LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:61:5 + | +LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:62:5 + | +LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {2:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:63:5 + | +LL | println!("{} {}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{} {}", local_i32, local_f64); +LL + println!("{local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:64:5 + | +LL | println!("{}, {}", local_i32, local_opt.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}, {}", local_i32, local_opt.unwrap()); +LL + println!("{local_i32}, {}", local_opt.unwrap()); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:65:5 + | +LL | println!("{}", val); + | ^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:66:5 + | +LL | println!("{}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", v = val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:68:5 + | +LL | println!("val='{/t }'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{/t }'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:69:5 + | +LL | println!("val='{/n }'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{/n }'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:70:5 + | +LL | println!("val='{local_i32}'", local_i32 = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{local_i32}'", local_i32 = local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:71:5 + | +LL | println!("val='{local_i32}'", local_i32 = fn_arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{local_i32}'", local_i32 = fn_arg); +LL + println!("val='{fn_arg}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:72:5 + | +LL | println!("{0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0}", local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:73:5 + | +LL | println!("{0:?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:?}", local_i32); +LL + println!("{local_i32:?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:74:5 + | +LL | println!("{0:#?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:#?}", local_i32); +LL + println!("{local_i32:#?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:75:5 + | +LL | println!("{0:04}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:04}", local_i32); +LL + println!("{local_i32:04}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:76:5 + | +LL | println!("{0:<3}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:<3}", local_i32); +LL + println!("{local_i32:<3}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:77:5 + | +LL | println!("{0:#010x}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:#010x}", local_i32); +LL + println!("{local_i32:#010x}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:78:5 + | +LL | println!("{0:.1}", local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:.1}", local_f64); +LL + println!("{local_f64:.1}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:79:5 + | +LL | println!("{0} {0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0} {0}", local_i32); +LL + println!("{local_i32} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:80:5 + | +LL | println!("{1} {} {0} {}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {} {0} {}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:81:5 + | +LL | println!("{0} {1}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0} {1}", local_i32, local_f64); +LL + println!("{local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:82:5 + | +LL | println!("{1} {0}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {0}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:83:5 + | +LL | println!("{1} {0} {1} {0}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {0} {1} {0}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:85:5 + | +LL | println!("{v}", v = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v}", v = local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:86:5 + | +LL | println!("{local_i32:0$}", width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:0$}", width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:87:5 + | +LL | println!("{local_i32:w$}", w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:w$}", w = width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:88:5 + | +LL | println!("{local_i32:.0$}", prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:.0$}", prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:89:5 + | +LL | println!("{local_i32:.p$}", p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:.p$}", p = prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:90:5 + | +LL | println!("{:0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$}", v = val); +LL + println!("{val:val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:91:5 + | +LL | println!("{0:0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$}", v = val); +LL + println!("{val:val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:92:5 + | +LL | println!("{:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:93:5 + | +LL | println!("{0:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:94:5 + | +LL | println!("{0:0$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:95:5 + | +LL | println!("{0:v$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:v$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:96:5 + | +LL | println!("{v:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:97:5 + | +LL | println!("{v:v$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:v$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:98:5 + | +LL | println!("{v:0$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:0$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:99:5 + | +LL | println!("{v:v$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:v$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:100:5 + | +LL | println!("{:0$}", width); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$}", width); +LL + println!("{width:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:101:5 + | +LL | println!("{:1$}", local_i32, width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$}", local_i32, width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:102:5 + | +LL | println!("{:w$}", w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$}", w = width); +LL + println!("{width:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:103:5 + | +LL | println!("{:w$}", local_i32, w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$}", local_i32, w = width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:104:5 + | +LL | println!("{:.0$}", prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.0$}", prec); +LL + println!("{prec:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:105:5 + | +LL | println!("{:.1$}", local_i32, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.1$}", local_i32, prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:106:5 + | +LL | println!("{:.p$}", p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.p$}", p = prec); +LL + println!("{prec:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:107:5 + | +LL | println!("{:.p$}", local_i32, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.p$}", local_i32, p = prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:108:5 + | +LL | println!("{:0$.1$}", width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.1$}", width, prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:109:5 + | +LL | println!("{:0$.w$}", width, w = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.w$}", width, w = prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:110:5 + | +LL | println!("{:1$.2$}", local_f64, width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$.2$}", local_f64, width, prec); +LL + println!("{local_f64:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:111:5 + | +LL | println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); +LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:112:5 + | +LL | / println!( +LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", +LL | | local_i32, width, prec, +LL | | ); + | |_____^ + | +help: change this to + | +LL ~ "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:123:5 + | +LL | println!("Width = {}, value with width = {:0$}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Width = {}, value with width = {:0$}", local_i32, local_f64); +LL + println!("Width = {local_i32}, value with width = {local_f64:local_i32$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:124:5 + | +LL | println!("{:w$.p$}", local_i32, w = width, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$.p$}", local_i32, w = width, p = prec); +LL + println!("{local_i32:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:125:5 + | +LL | println!("{:w$.p$}", w = width, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$.p$}", w = width, p = prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:126:20 + | +LL | println!("{}", format!("{}", local_i32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", format!("{}", local_i32)); +LL + println!("{}", format!("{local_i32}")); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:144:5 + | +LL | / println!( +LL | | "{}", +LL | | // comment with a comma , in it +LL | | val, +LL | | ); + | |_____^ + | +help: change this to + | +LL - "{}", +LL + "{val}", + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:149:5 + | +LL | println!("{}", /* comment with a comma , in it */ val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", /* comment with a comma , in it */ val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:168:5 + | +LL | println!("expand='{}'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("expand='{}'", local_i32); +LL + println!("expand='{local_i32}'"); + | + +error: aborting due to 73 previous errors + diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 7bf3adc07ac5..07e70873a813 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,17 +1,16 @@ // aux-build: proc_macro_with_span.rs - #![warn(clippy::unit_arg)] +#![allow(unused_must_use, unused_variables)] #![allow( - clippy::no_effect, - unused_must_use, - unused_variables, - clippy::unused_unit, - clippy::unnecessary_wraps, - clippy::or_fun_call, - clippy::needless_question_mark, - clippy::self_named_constructors, clippy::let_unit_value, - clippy::never_loop + clippy::needless_question_mark, + clippy::never_loop, + clippy::no_effect, + clippy::or_fun_call, + clippy::self_named_constructors, + clippy::uninlined_format_args, + clippy::unnecessary_wraps, + clippy::unused_unit )] extern crate proc_macro_with_span; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 1de9d44bb0d6..74d4d2f4052f 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:63:5 + --> $DIR/unit_arg.rs:62:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:66:5 + --> $DIR/unit_arg.rs:65:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:67:5 + --> $DIR/unit_arg.rs:66:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:72:5 + --> $DIR/unit_arg.rs:71:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL ~ b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:75:5 + --> $DIR/unit_arg.rs:74:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:76:5 + --> $DIR/unit_arg.rs:75:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:80:5 + --> $DIR/unit_arg.rs:79:5 | LL | / taking_multiple_units( LL | | { @@ -146,7 +146,7 @@ LL ~ ); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:91:13 + --> $DIR/unit_arg.rs:90:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -160,7 +160,7 @@ LL ~ }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:94:5 + --> $DIR/unit_arg.rs:93:5 | LL | foo(foo(())); | ^^^^^^^^^^^^ @@ -172,7 +172,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:131:5 + --> $DIR/unit_arg.rs:130:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/tests/ui/unit_arg_empty_blocks.fixed b/tests/ui/unit_arg_empty_blocks.fixed index 9400e93cac83..5787471a32ca 100644 --- a/tests/ui/unit_arg_empty_blocks.fixed +++ b/tests/ui/unit_arg_empty_blocks.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] +#![allow(unused_must_use, unused_variables)] +#![allow(clippy::no_effect, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/unit_arg_empty_blocks.rs b/tests/ui/unit_arg_empty_blocks.rs index 5f52b6c5315f..6a42c2ccf42b 100644 --- a/tests/ui/unit_arg_empty_blocks.rs +++ b/tests/ui/unit_arg_empty_blocks.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] +#![allow(unused_must_use, unused_variables)] +#![allow(clippy::no_effect, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index d35e931697d2..c697dfb1efa2 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:16:5 + --> $DIR/unit_arg_empty_blocks.rs:17:5 | LL | foo({}); | ^^^^--^ @@ -9,7 +9,7 @@ LL | foo({}); = note: `-D clippy::unit-arg` implied by `-D warnings` error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:17:5 + --> $DIR/unit_arg_empty_blocks.rs:18:5 | LL | foo3({}, 2, 2); | ^^^^^--^^^^^^^ @@ -17,7 +17,7 @@ LL | foo3({}, 2, 2); | help: use a unit literal instead: `()` error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:18:5 + --> $DIR/unit_arg_empty_blocks.rs:19:5 | LL | taking_two_units({}, foo(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL ~ taking_two_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:19:5 + --> $DIR/unit_arg_empty_blocks.rs:20:5 | LL | taking_three_units({}, foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index ee9f157342d4..94dc96427263 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -97,4 +97,18 @@ mod fixable { let _ = -(1 + 1) as i64; } + + fn issue_9563() { + let _: f64 = (-8.0_f64).exp(); + #[allow(clippy::precedence)] + let _: f64 = -8.0_f64.exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior + } + + fn issue_9562_non_literal() { + fn foo() -> f32 { + 0. + } + + let _num = foo(); + } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 5b70412424c0..e5150256f69a 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -97,4 +97,18 @@ mod fixable { let _ = -(1 + 1) as i64; } + + fn issue_9563() { + let _: f64 = (-8.0 as f64).exp(); + #[allow(clippy::precedence)] + let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior + } + + fn issue_9562_non_literal() { + fn foo() -> f32 { + 0. + } + + let _num = foo() as f32; + } } diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index f7829ff3b0ef..e5c3dd5e53f8 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -162,5 +162,23 @@ error: casting integer literal to `i64` is unnecessary LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` -error: aborting due to 27 previous errors +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:102:22 + | +LL | let _: f64 = (-8.0 as f64).exp(); + | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:104:23 + | +LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior + | ^^^^^^^^^^^^ help: try: `8.0_f64` + +error: casting to the same type is unnecessary (`f32` -> `f32`) + --> $DIR/unnecessary_cast.rs:112:20 + | +LL | let _num = foo() as f32; + | ^^^^^^^^^^^^ help: try: `foo()` + +error: aborting due to 30 previous errors diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index 6770a7fac90f..8b1629b19a76 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -1,7 +1,7 @@ // does not test any rustfixable lints - #![warn(clippy::clone_on_ref_ptr)] -#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wraps)] +#![allow(unused)] +#![allow(clippy::redundant_clone, clippy::uninlined_format_args, clippy::unnecessary_wraps)] use std::cell::RefCell; use std::rc::{self, Rc}; diff --git a/tests/ui/unnecessary_join.fixed b/tests/ui/unnecessary_join.fixed index 7e12c6ae4be9..347953960257 100644 --- a/tests/ui/unnecessary_join.fixed +++ b/tests/ui/unnecessary_join.fixed @@ -1,6 +1,6 @@ // run-rustfix - #![warn(clippy::unnecessary_join)] +#![allow(clippy::uninlined_format_args)] fn main() { // should be linted diff --git a/tests/ui/unnecessary_join.rs b/tests/ui/unnecessary_join.rs index 0a21656a7558..344918cd2a2e 100644 --- a/tests/ui/unnecessary_join.rs +++ b/tests/ui/unnecessary_join.rs @@ -1,6 +1,6 @@ // run-rustfix - #![warn(clippy::unnecessary_join)] +#![allow(clippy::uninlined_format_args)] fn main() { // should be linted diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index eed817968832..ce4a82e02174 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -1,9 +1,13 @@ // run-rustfix +// aux-build: proc_macro_with_span.rs #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::map_identity)] +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + struct Deep(Option); #[derive(Copy, Clone)] @@ -21,6 +25,14 @@ fn some_call() -> T { T::default() } +struct Issue9427(i32); + +impl Drop for Issue9427 { + fn drop(&mut self) { + println!("{}", self.0); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -73,6 +85,9 @@ fn main() { let _ = deep.0.or_else(|| some_call()); let _ = opt.ok_or_else(|| ext_arr[0]); + // Should not lint - bool + let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); @@ -130,3 +145,9 @@ fn main() { let _: Result = res.and_then(|x| Err(x)); let _: Result = res.or_else(|err| Ok(err)); } + +#[allow(unused)] +fn issue9485() { + // should not lint, is in proc macro + with_span!(span Some(42).unwrap_or_else(|| 2);); +} diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 1588db79b38a..59cdf6628546 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -1,9 +1,13 @@ // run-rustfix +// aux-build: proc_macro_with_span.rs #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::map_identity)] +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + struct Deep(Option); #[derive(Copy, Clone)] @@ -21,6 +25,14 @@ fn some_call() -> T { T::default() } +struct Issue9427(i32); + +impl Drop for Issue9427 { + fn drop(&mut self) { + println!("{}", self.0); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -73,6 +85,9 @@ fn main() { let _ = deep.0.or_else(|| some_call()); let _ = opt.ok_or_else(|| ext_arr[0]); + // Should not lint - bool + let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); @@ -130,3 +145,9 @@ fn main() { let _: Result = res.and_then(|x| Err(x)); let _: Result = res.or_else(|err| Ok(err)); } + +#[allow(unused)] +fn issue9485() { + // should not lint, is in proc macro + with_span!(span Some(42).unwrap_or_else(|| 2);); +} diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 83dc7fd832c3..8a9ece4aa7e5 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $DIR/unnecessary_lazy_eval.rs:48:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^-------------------- @@ -9,7 +9,7 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 + --> $DIR/unnecessary_lazy_eval.rs:49:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^--------------------------------- @@ -17,7 +17,7 @@ LL | let _ = opt.unwrap_or_else(|| 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:38:13 + --> $DIR/unnecessary_lazy_eval.rs:50:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^------------------------------------- @@ -25,7 +25,7 @@ LL | let _ = opt.unwrap_or_else(|| 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:40:13 + --> $DIR/unnecessary_lazy_eval.rs:52:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^--------------------- @@ -33,7 +33,7 @@ LL | let _ = opt.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:41:13 + --> $DIR/unnecessary_lazy_eval.rs:53:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^------------------- @@ -41,7 +41,7 @@ LL | let _ = opt.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:42:13 + --> $DIR/unnecessary_lazy_eval.rs:54:13 | LL | let _ = opt.or_else(|| None); | ^^^^---------------- @@ -49,7 +49,7 @@ LL | let _ = opt.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^------------------------ @@ -57,7 +57,7 @@ LL | let _ = opt.get_or_insert_with(|| 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:44:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^---------------- @@ -65,7 +65,7 @@ LL | let _ = opt.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:45:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | ^^^^^^^^^^^^^^^^^------------------------------- @@ -73,7 +73,7 @@ LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:46:13 + --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = cond.then(|| astronomers_pi); | ^^^^^----------------------- @@ -81,7 +81,7 @@ LL | let _ = cond.then(|| astronomers_pi); | help: use `then_some(..)` instead: `then_some(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $DIR/unnecessary_lazy_eval.rs:61:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^-------------------- @@ -89,7 +89,7 @@ LL | let _ = Some(10).unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:13 + --> $DIR/unnecessary_lazy_eval.rs:62:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^--------------------- @@ -97,7 +97,7 @@ LL | let _ = Some(10).and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:28 + --> $DIR/unnecessary_lazy_eval.rs:63:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^------------------- @@ -105,7 +105,7 @@ LL | let _: Option = None.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:64:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^------------------------ @@ -113,7 +113,7 @@ LL | let _ = None.get_or_insert_with(|| 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:53:35 + --> $DIR/unnecessary_lazy_eval.rs:65:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^---------------- @@ -121,7 +121,7 @@ LL | let _: Result = None.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:28 + --> $DIR/unnecessary_lazy_eval.rs:66:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^---------------- @@ -129,7 +129,7 @@ LL | let _: Option = None.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:57:13 + --> $DIR/unnecessary_lazy_eval.rs:69:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^-------------------- @@ -137,7 +137,7 @@ LL | let _ = deep.0.unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:58:13 + --> $DIR/unnecessary_lazy_eval.rs:70:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^--------------------- @@ -145,7 +145,7 @@ LL | let _ = deep.0.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:59:13 + --> $DIR/unnecessary_lazy_eval.rs:71:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^---------------- @@ -153,7 +153,7 @@ LL | let _ = deep.0.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:60:13 + --> $DIR/unnecessary_lazy_eval.rs:72:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^------------------------ @@ -161,7 +161,7 @@ LL | let _ = deep.0.get_or_insert_with(|| 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:61:13 + --> $DIR/unnecessary_lazy_eval.rs:73:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^---------------- @@ -169,7 +169,7 @@ LL | let _ = deep.0.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:81:28 + --> $DIR/unnecessary_lazy_eval.rs:96:28 | LL | let _: Option = None.or_else(|| Some(3)); | ^^^^^------------------- @@ -177,7 +177,7 @@ LL | let _: Option = None.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:82:13 + --> $DIR/unnecessary_lazy_eval.rs:97:13 | LL | let _ = deep.0.or_else(|| Some(3)); | ^^^^^^^------------------- @@ -185,7 +185,7 @@ LL | let _ = deep.0.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:83:13 + --> $DIR/unnecessary_lazy_eval.rs:98:13 | LL | let _ = opt.or_else(|| Some(3)); | ^^^^------------------- @@ -193,7 +193,7 @@ LL | let _ = opt.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:89:13 + --> $DIR/unnecessary_lazy_eval.rs:104:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^--------------------- @@ -201,7 +201,7 @@ LL | let _ = res2.unwrap_or_else(|_| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:90:13 + --> $DIR/unnecessary_lazy_eval.rs:105:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^---------------------------------- @@ -209,7 +209,7 @@ LL | let _ = res2.unwrap_or_else(|_| 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:91:13 + --> $DIR/unnecessary_lazy_eval.rs:106:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^-------------------------------------- @@ -217,7 +217,7 @@ LL | let _ = res2.unwrap_or_else(|_| 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:113:35 + --> $DIR/unnecessary_lazy_eval.rs:128:35 | LL | let _: Result = res.and_then(|_| Err(2)); | ^^^^-------------------- @@ -225,7 +225,7 @@ LL | let _: Result = res.and_then(|_| Err(2)); | help: use `and(..)` instead: `and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:114:35 + --> $DIR/unnecessary_lazy_eval.rs:129:35 | LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | ^^^^--------------------------------- @@ -233,7 +233,7 @@ LL | let _: Result = res.and_then(|_| 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:115:35 + --> $DIR/unnecessary_lazy_eval.rs:130:35 | LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); | ^^^^------------------------------------- @@ -241,7 +241,7 @@ LL | let _: Result = res.and_then(|_| 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:117:35 + --> $DIR/unnecessary_lazy_eval.rs:132:35 | LL | let _: Result = res.or_else(|_| Ok(2)); | ^^^^------------------ @@ -249,7 +249,7 @@ LL | let _: Result = res.or_else(|_| Ok(2)); | help: use `or(..)` instead: `or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:118:35 + --> $DIR/unnecessary_lazy_eval.rs:133:35 | LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | ^^^^------------------------------- @@ -257,7 +257,7 @@ LL | let _: Result = res.or_else(|_| 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:119:35 + --> $DIR/unnecessary_lazy_eval.rs:134:35 | LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | ^^^^----------------------------------- @@ -265,7 +265,7 @@ LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:120:35 + --> $DIR/unnecessary_lazy_eval.rs:135:35 | LL | let _: Result = res. | ___________________________________^ diff --git a/tests/ui/upper_case_acronyms.rs b/tests/ui/upper_case_acronyms.rs index 48bb9e54b122..9b7c2f28e1cf 100644 --- a/tests/ui/upper_case_acronyms.rs +++ b/tests/ui/upper_case_acronyms.rs @@ -38,4 +38,13 @@ enum ParseErrorPrivate { Parse(T, String), } +// do lint here +struct JSON; + +// do lint here +enum YAML { + Num(u32), + Str(String), +} + fn main() {} diff --git a/tests/ui/upper_case_acronyms.stderr b/tests/ui/upper_case_acronyms.stderr index 250b196a99eb..74082ec16dd4 100644 --- a/tests/ui/upper_case_acronyms.stderr +++ b/tests/ui/upper_case_acronyms.stderr @@ -54,5 +54,17 @@ error: name `WASD` contains a capitalized acronym LL | WASD(u8), | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Wasd` -error: aborting due to 9 previous errors +error: name `JSON` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:42:8 + | +LL | struct JSON; + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Json` + +error: name `YAML` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:45:6 + | +LL | enum YAML { + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Yaml` + +error: aborting due to 11 previous errors diff --git a/tests/ui/used_underscore_binding.rs b/tests/ui/used_underscore_binding.rs index 322083511ac1..8c29e15b1459 100644 --- a/tests/ui/used_underscore_binding.rs +++ b/tests/ui/used_underscore_binding.rs @@ -1,9 +1,8 @@ // aux-build:proc_macro_derive.rs - #![feature(rustc_private)] #![warn(clippy::all)] -#![allow(clippy::disallowed_names, clippy::eq_op)] #![warn(clippy::used_underscore_binding)] +#![allow(clippy::disallowed_names, clippy::eq_op, clippy::uninlined_format_args)] #[macro_use] extern crate proc_macro_derive; diff --git a/tests/ui/used_underscore_binding.stderr b/tests/ui/used_underscore_binding.stderr index 61a9161d212d..875fafe438a1 100644 --- a/tests/ui/used_underscore_binding.stderr +++ b/tests/ui/used_underscore_binding.stderr @@ -1,5 +1,5 @@ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:25:5 + --> $DIR/used_underscore_binding.rs:24:5 | LL | _foo + 1 | ^^^^ @@ -7,31 +7,31 @@ LL | _foo + 1 = note: `-D clippy::used-underscore-binding` implied by `-D warnings` error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:30:20 + --> $DIR/used_underscore_binding.rs:29:20 | LL | println!("{}", _foo); | ^^^^ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:31:16 + --> $DIR/used_underscore_binding.rs:30:16 | LL | assert_eq!(_foo, _foo); | ^^^^ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:31:22 + --> $DIR/used_underscore_binding.rs:30:22 | LL | assert_eq!(_foo, _foo); | ^^^^ error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:44:5 + --> $DIR/used_underscore_binding.rs:43:5 | LL | s._underscore_field += 1; | ^^^^^^^^^^^^^^^^^^^ error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:105:16 + --> $DIR/used_underscore_binding.rs:104:16 | LL | uses_i(_i); | ^^ diff --git a/tests/ui/useless_asref.fixed b/tests/ui/useless_asref.fixed index 90cb8945e77f..38e4b9201e6d 100644 --- a/tests/ui/useless_asref.fixed +++ b/tests/ui/useless_asref.fixed @@ -1,7 +1,6 @@ // run-rustfix - #![deny(clippy::useless_asref)] -#![allow(clippy::explicit_auto_deref)] +#![allow(clippy::explicit_auto_deref, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/useless_asref.rs b/tests/ui/useless_asref.rs index cb9f8ae5909a..f1e83f9d396c 100644 --- a/tests/ui/useless_asref.rs +++ b/tests/ui/useless_asref.rs @@ -1,7 +1,6 @@ // run-rustfix - #![deny(clippy::useless_asref)] -#![allow(clippy::explicit_auto_deref)] +#![allow(clippy::explicit_auto_deref, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/useless_asref.stderr b/tests/ui/useless_asref.stderr index b21c67bb3645..67ce8b64e0e3 100644 --- a/tests/ui/useless_asref.stderr +++ b/tests/ui/useless_asref.stderr @@ -1,71 +1,71 @@ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:44:18 + --> $DIR/useless_asref.rs:43:18 | LL | foo_rstr(rstr.as_ref()); | ^^^^^^^^^^^^^ help: try this: `rstr` | note: the lint level is defined here - --> $DIR/useless_asref.rs:3:9 + --> $DIR/useless_asref.rs:2:9 | LL | #![deny(clippy::useless_asref)] | ^^^^^^^^^^^^^^^^^^^^^ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:46:20 + --> $DIR/useless_asref.rs:45:20 | LL | foo_rslice(rslice.as_ref()); | ^^^^^^^^^^^^^^^ help: try this: `rslice` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:50:21 + --> $DIR/useless_asref.rs:49:21 | LL | foo_mrslice(mrslice.as_mut()); | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:52:20 + --> $DIR/useless_asref.rs:51:20 | LL | foo_rslice(mrslice.as_ref()); | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:59:20 + --> $DIR/useless_asref.rs:58:20 | LL | foo_rslice(rrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:61:18 + --> $DIR/useless_asref.rs:60:18 | LL | foo_rstr(rrrrrstr.as_ref()); | ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:66:21 + --> $DIR/useless_asref.rs:65:21 | LL | foo_mrslice(mrrrrrslice.as_mut()); | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:68:20 + --> $DIR/useless_asref.rs:67:20 | LL | foo_rslice(mrrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:72:16 + --> $DIR/useless_asref.rs:71:16 | LL | foo_rrrrmr((&&&&MoreRef).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:122:13 + --> $DIR/useless_asref.rs:121:13 | LL | foo_mrt(mrt.as_mut()); | ^^^^^^^^^^^^ help: try this: `mrt` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:124:12 + --> $DIR/useless_asref.rs:123:12 | LL | foo_rt(mrt.as_ref()); | ^^^^^^^^^^^^ help: try this: `mrt` diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index 318f9c2dceb6..2518d8049150 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::nonstandard_macro_braces)] #![warn(clippy::useless_vec)] +#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args)] #[derive(Debug)] struct NonCopy; diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index d7673ce3e643..e1492e2f3aef 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::nonstandard_macro_braces)] #![warn(clippy::useless_vec)] +#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args)] #[derive(Debug)] struct NonCopy; diff --git a/tests/ui/while_let_loop.rs b/tests/ui/while_let_loop.rs index c42e2a79a9bf..5b8075731cb7 100644 --- a/tests/ui/while_let_loop.rs +++ b/tests/ui/while_let_loop.rs @@ -1,4 +1,5 @@ #![warn(clippy::while_let_loop)] +#![allow(clippy::uninlined_format_args)] fn main() { let y = Some(true); diff --git a/tests/ui/while_let_loop.stderr b/tests/ui/while_let_loop.stderr index 13dd0ee224c1..04808c0b3ada 100644 --- a/tests/ui/while_let_loop.stderr +++ b/tests/ui/while_let_loop.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:5:5 + --> $DIR/while_let_loop.rs:6:5 | LL | / loop { LL | | if let Some(_x) = y { @@ -13,7 +13,7 @@ LL | | } = note: `-D clippy::while-let-loop` implied by `-D warnings` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:22:5 + --> $DIR/while_let_loop.rs:23:5 | LL | / loop { LL | | match y { @@ -24,7 +24,7 @@ LL | | } | |_____^ help: try: `while let Some(_x) = y { .. }` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:29:5 + --> $DIR/while_let_loop.rs:30:5 | LL | / loop { LL | | let x = match y { @@ -36,7 +36,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:38:5 + --> $DIR/while_let_loop.rs:39:5 | LL | / loop { LL | | let x = match y { @@ -48,7 +48,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:68:5 + --> $DIR/while_let_loop.rs:69:5 | LL | / loop { LL | | let (e, l) = match "".split_whitespace().next() { diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index c57c46736342..5afa0a89f82c 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -1,14 +1,12 @@ // run-rustfix - #![warn(clippy::while_let_on_iterator)] +#![allow(dead_code, unreachable_code, unused_mut)] #![allow( - clippy::never_loop, - unreachable_code, - unused_mut, - dead_code, clippy::equatable_if_let, clippy::manual_find, - clippy::redundant_closure_call + clippy::never_loop, + clippy::redundant_closure_call, + clippy::uninlined_format_args )] fn base() { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 8b9a2dbcce3a..3de586c9d8fd 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -1,14 +1,12 @@ // run-rustfix - #![warn(clippy::while_let_on_iterator)] +#![allow(dead_code, unreachable_code, unused_mut)] #![allow( - clippy::never_loop, - unreachable_code, - unused_mut, - dead_code, clippy::equatable_if_let, clippy::manual_find, - clippy::redundant_closure_call + clippy::never_loop, + clippy::redundant_closure_call, + clippy::uninlined_format_args )] fn base() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 3236765e1db0..4d98666190d6 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:16:5 + --> $DIR/while_let_on_iterator.rs:14:5 | LL | while let Option::Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` @@ -7,151 +7,151 @@ LL | while let Option::Some(x) = iter.next() { = note: `-D clippy::while-let-on-iterator` implied by `-D warnings` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:21:5 + --> $DIR/while_let_on_iterator.rs:19:5 | LL | while let Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:26:5 + --> $DIR/while_let_on_iterator.rs:24:5 | LL | while let Some(_) = iter.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:102:9 + --> $DIR/while_let_on_iterator.rs:100:9 | LL | while let Some([..]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:109:9 + --> $DIR/while_let_on_iterator.rs:107:9 | LL | while let Some([_x]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:122:9 + --> $DIR/while_let_on_iterator.rs:120:9 | LL | while let Some(x @ [_]) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:142:9 + --> $DIR/while_let_on_iterator.rs:140:9 | LL | while let Some(_) = y.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:199:9 + --> $DIR/while_let_on_iterator.rs:197:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:210:5 + --> $DIR/while_let_on_iterator.rs:208:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:212:9 + --> $DIR/while_let_on_iterator.rs:210:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:221:9 + --> $DIR/while_let_on_iterator.rs:219:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:230:9 + --> $DIR/while_let_on_iterator.rs:228:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:247:9 + --> $DIR/while_let_on_iterator.rs:245:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:262:13 + --> $DIR/while_let_on_iterator.rs:260:13 | LL | while let Some(i) = self.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:294:13 + --> $DIR/while_let_on_iterator.rs:292:13 | LL | while let Some(i) = self.0.0.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:323:5 + --> $DIR/while_let_on_iterator.rs:321:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:335:9 + --> $DIR/while_let_on_iterator.rs:333:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:349:5 + --> $DIR/while_let_on_iterator.rs:347:5 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:360:5 + --> $DIR/while_let_on_iterator.rs:358:5 | LL | while let Some(x) = it.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:395:5 + --> $DIR/while_let_on_iterator.rs:393:5 | LL | while let Some(x) = s.x.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:402:5 + --> $DIR/while_let_on_iterator.rs:400:5 | LL | while let Some(x) = x[0].next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:410:9 + --> $DIR/while_let_on_iterator.rs:408:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:420:9 + --> $DIR/while_let_on_iterator.rs:418:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:430:9 + --> $DIR/while_let_on_iterator.rs:428:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:440:9 + --> $DIR/while_let_on_iterator.rs:438:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:450:5 + --> $DIR/while_let_on_iterator.rs:448:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index 3ee4ab48ac84..23607497841e 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -1,15 +1,13 @@ // run-rustfix // aux-build:non-exhaustive-enum.rs - #![deny(clippy::wildcard_enum_match_arm)] +#![allow(dead_code, unreachable_code, unused_variables)] #![allow( - unreachable_code, - unused_variables, - dead_code, + clippy::diverging_sub_expression, clippy::single_match, - clippy::wildcard_in_or_patterns, + clippy::uninlined_format_args, clippy::unnested_or_patterns, - clippy::diverging_sub_expression + clippy::wildcard_in_or_patterns )] extern crate non_exhaustive_enum; diff --git a/tests/ui/wildcard_enum_match_arm.rs b/tests/ui/wildcard_enum_match_arm.rs index 468865504533..decd86165f3a 100644 --- a/tests/ui/wildcard_enum_match_arm.rs +++ b/tests/ui/wildcard_enum_match_arm.rs @@ -1,15 +1,13 @@ // run-rustfix // aux-build:non-exhaustive-enum.rs - #![deny(clippy::wildcard_enum_match_arm)] +#![allow(dead_code, unreachable_code, unused_variables)] #![allow( - unreachable_code, - unused_variables, - dead_code, + clippy::diverging_sub_expression, clippy::single_match, - clippy::wildcard_in_or_patterns, + clippy::uninlined_format_args, clippy::unnested_or_patterns, - clippy::diverging_sub_expression + clippy::wildcard_in_or_patterns )] extern crate non_exhaustive_enum; diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index d63f20903531..efecc9576cc7 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -1,41 +1,41 @@ error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:42:9 + --> $DIR/wildcard_enum_match_arm.rs:40:9 | LL | _ => eprintln!("Not red"), | ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` | note: the lint level is defined here - --> $DIR/wildcard_enum_match_arm.rs:4:9 + --> $DIR/wildcard_enum_match_arm.rs:3:9 | LL | #![deny(clippy::wildcard_enum_match_arm)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:46:9 + --> $DIR/wildcard_enum_match_arm.rs:44:9 | LL | _not_red => eprintln!("Not red"), | ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan` error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:50:9 + --> $DIR/wildcard_enum_match_arm.rs:48:9 | LL | not_red => format!("{:?}", not_red), | ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan` error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:66:9 + --> $DIR/wildcard_enum_match_arm.rs:64:9 | LL | _ => "No red", | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` error: wildcard matches known variants and will also match future added variants - --> $DIR/wildcard_enum_match_arm.rs:83:9 + --> $DIR/wildcard_enum_match_arm.rs:81:9 | LL | _ => {}, | ^ help: try this: `ErrorKind::PermissionDenied | _` error: wildcard matches known variants and will also match future added variants - --> $DIR/wildcard_enum_match_arm.rs:101:13 + --> $DIR/wildcard_enum_match_arm.rs:99:13 | LL | _ => (), | ^ help: try this: `Enum::B | _` diff --git a/tests/ui/write_literal.rs b/tests/ui/write_literal.rs index 5892818aa9a6..218385ea1296 100644 --- a/tests/ui/write_literal.rs +++ b/tests/ui/write_literal.rs @@ -1,5 +1,5 @@ -#![allow(unused_must_use)] #![warn(clippy::write_literal)] +#![allow(clippy::uninlined_format_args, unused_must_use)] use std::io::Write; diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 38498ebdcf2c..9e07769a8e4f 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -8,12 +8,12 @@ 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)); + 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)) + .unwrap_or_else(|| panic!("error finding version in `{path}`")) .to_string() } @@ -83,7 +83,7 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { // we don't want our tests failing suddenly }, _ => { - panic!("Failed to parse rustc version: {:?}", vsplit); + panic!("Failed to parse rustc version: {vsplit:?}"); }, }; } From 17d78c4ef922f81e752feb077d66fbce80630c6c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 6 Oct 2022 13:36:07 +0200 Subject: [PATCH 0028/1126] poll_fn and Unpin: fix pinning --- library/core/src/future/poll_fn.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/library/core/src/future/poll_fn.rs b/library/core/src/future/poll_fn.rs index db2a523323b7..90cb797391a0 100644 --- a/library/core/src/future/poll_fn.rs +++ b/library/core/src/future/poll_fn.rs @@ -5,7 +5,9 @@ use crate::task::{Context, Poll}; /// Creates a future that wraps a function returning [`Poll`]. /// -/// Polling the future delegates to the wrapped function. +/// Polling the future delegates to the wrapped function. If the returned future is pinned, then the +/// captured environment of the wrapped function is also pinned in-place, so as long as the closure +/// does not move out of its captures it can soundly create pinned references to them. /// /// # Examples /// @@ -41,7 +43,7 @@ pub struct PollFn { } #[stable(feature = "future_poll_fn", since = "1.64.0")] -impl Unpin for PollFn {} +impl Unpin for PollFn {} #[stable(feature = "future_poll_fn", since = "1.64.0")] impl fmt::Debug for PollFn { @@ -57,7 +59,8 @@ where { type Output = T; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - (&mut self.f)(cx) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // SAFETY: We are not moving out of the pinned field. + (unsafe { &mut self.get_unchecked_mut().f })(cx) } } From 13dbc33d8f129453212116b90cbd96b0a013d23a Mon Sep 17 00:00:00 2001 From: ouz-a Date: Tue, 4 Oct 2022 21:39:43 +0300 Subject: [PATCH 0029/1126] Remove `mir::CastKind::Misc` --- clippy_utils/src/qualify_min_const_fn.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index f7ce71917726..7af27ebb9883 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -129,7 +129,12 @@ fn check_rvalue<'tcx>( | Rvalue::Use(operand) | Rvalue::Cast( CastKind::PointerFromExposedAddress - | CastKind::Misc + | CastKind::IntToInt + | CastKind::FloatToInt + | CastKind::IntToFloat + | CastKind::FloatToFloat + | CastKind::FnPtrToPtr + | CastKind::PtrToPtr | CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _, From d3c041a08696b722da3276846e0064dd03742940 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Mon, 3 Oct 2022 17:29:38 +0200 Subject: [PATCH 0030/1126] extend `box-default` lint, add suggestion --- clippy_lints/src/box_default.rs | 94 ++++++++++++++++++++++++++++----- clippy_utils/src/lib.rs | 26 ++++++++- src/docs/box_default.txt | 6 --- tests/ui/box_default.fixed | 43 +++++++++++++++ tests/ui/box_default.rs | 14 ++++- tests/ui/box_default.stderr | 77 +++++++++++++++++---------- 6 files changed, 212 insertions(+), 48 deletions(-) create mode 100644 tests/ui/box_default.fixed diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 792183ac4081..f35a79dcc739 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -1,5 +1,12 @@ -use clippy_utils::{diagnostics::span_lint_and_help, is_default_equivalent, path_def_id}; -use rustc_hir::{Expr, ExprKind, QPath}; +use clippy_utils::{ + diagnostics::span_lint_and_sugg, get_parent_node, is_default_equivalent, macros::macro_backtrace, match_path, + path_def_id, paths, ty::expr_sig, +}; +use rustc_errors::Applicability; +use rustc_hir::{ + intravisit::{walk_ty, Visitor}, + Block, Expr, ExprKind, Local, Node, QPath, TyKind, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -15,12 +22,6 @@ declare_clippy_lint! { /// Second, `Box::default()` can be faster /// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). /// - /// ### Known problems - /// The lint may miss some cases (e.g. Box::new(String::from(""))). - /// On the other hand, it will trigger on cases where the `default` - /// code comes from a macro that does something different based on - /// e.g. target operating system. - /// /// ### Example /// ```rust /// let x: Box = Box::new(Default::default()); @@ -41,21 +42,88 @@ impl LateLintPass<'_> for BoxDefault { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(box_new, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind - && let ExprKind::Call(..) = arg.kind + && let ExprKind::Call(arg_path, ..) = arg.kind && !in_external_macro(cx.sess(), expr.span) - && expr.span.eq_ctxt(arg.span) + && (expr.span.eq_ctxt(arg.span) || is_vec_expn(cx, arg)) && seg.ident.name == sym::new && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box() && is_default_equivalent(cx, arg) { - span_lint_and_help( + let arg_ty = cx.typeck_results().expr_ty(arg); + span_lint_and_sugg( cx, BOX_DEFAULT, expr.span, "`Box::new(_)` of default value", - None, - "use `Box::default()` instead", + "try", + if is_plain_default(arg_path) || given_type(cx, expr) { + "Box::default()".into() + } else { + format!("Box::<{arg_ty}>::default()") + }, + Applicability::MachineApplicable ); } } } + +fn is_plain_default(arg_path: &Expr<'_>) -> bool { + // we need to match the actual path so we don't match e.g. "u8::default" + if let ExprKind::Path(QPath::Resolved(None, path)) = &arg_path.kind { + // avoid generic parameters + match_path(path, &paths::DEFAULT_TRAIT_METHOD) && path.segments.iter().all(|seg| seg.args.is_none()) + } else { + false + } +} + +fn is_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + macro_backtrace(expr.span) + .next() + .map_or(false, |call| cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id)) +} + +#[derive(Default)] +struct InferVisitor(bool); + +impl<'tcx> Visitor<'tcx> for InferVisitor { + fn visit_ty(&mut self, t: &rustc_hir::Ty<'_>) { + self.0 |= matches!(t.kind, TyKind::Infer); + if !self.0 { + walk_ty(self, t); + } + } +} + +fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Local(Local { ty: Some(ty), .. })) => { + let mut v = InferVisitor::default(); + v.visit_ty(ty); + !v.0 + }, + Some( + Node::Expr(Expr { + kind: ExprKind::Call(path, args), + .. + }) | Node::Block(Block { + expr: + Some(Expr { + kind: ExprKind::Call(path, args), + .. + }), + .. + }), + ) => { + if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) && + let Some(sig) = expr_sig(cx, path) && + let Some(input) = sig.input(index) + { + input.no_bound_vars().is_some() + } else { + false + } + }, + _ => false, + } +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 42374fdd7baf..e6492d76260e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -815,13 +815,37 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { false } }, - ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func), + ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func), + ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg), ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), _ => false, } } +fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool { + if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind && + seg.ident.name == sym::from + { + match arg.kind { + ExprKind::Lit(hir::Lit { + node: LitKind::Str(ref sym, _), + .. + }) => return sym.is_empty() && is_path_diagnostic_item(cx, ty, sym::String), + ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), + ExprKind::Repeat(_, ArrayLen::Body(len)) => { + if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind && + let LitKind::Int(v, _) = const_lit.node + { + return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec); + } + } + _ => (), + } + } + false +} + /// Checks if the top level expression can be moved into a closure as is. /// Currently checks for: /// * Break/Continue outside the given loop HIR ids. diff --git a/src/docs/box_default.txt b/src/docs/box_default.txt index ffac894d0c50..1c670c773337 100644 --- a/src/docs/box_default.txt +++ b/src/docs/box_default.txt @@ -7,12 +7,6 @@ First, it's more complex, involving two calls instead of one. Second, `Box::default()` can be faster [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). -### Known problems -The lint may miss some cases (e.g. Box::new(String::from(""))). -On the other hand, it will trigger on cases where the `default` -code comes from a macro that does something different based on -e.g. target operating system. - ### Example ``` let x: Box = Box::new(Default::default()); diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed new file mode 100644 index 000000000000..7fbb272ce5a3 --- /dev/null +++ b/tests/ui/box_default.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![warn(clippy::box_default)] + +#[derive(Default)] +struct ImplementsDefault; + +struct OwnDefault; + +impl OwnDefault { + fn default() -> Self { + Self + } +} + +macro_rules! outer { + ($e: expr) => { + $e + }; +} + +fn main() { + let _string: Box = Box::default(); + let _byte = Box::::default(); + let _vec = Box::>::default(); + let _impl = Box::::default(); + let _impl2 = Box::::default(); + let _impl3: Box = Box::default(); + let _own = Box::new(OwnDefault::default()); // should not lint + let _in_macro = outer!(Box::::default()); + let _string_default = outer!(Box::::default()); + let _vec2: Box> = Box::default(); + let _vec3: Box> = Box::default(); + let _vec4: Box<_> = Box::>::default(); + let _more = ret_ty_fn(); + call_ty_fn(Box::default()); +} + +fn ret_ty_fn() -> Box { + Box::::default() +} + +#[allow(clippy::boxed_local)] +fn call_ty_fn(_b: Box) {} diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index dc522705bc62..64c4f3887af7 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::box_default)] #[derive(Default)] @@ -26,6 +27,17 @@ fn main() { let _impl3: Box = Box::new(Default::default()); let _own = Box::new(OwnDefault::default()); // should not lint let _in_macro = outer!(Box::new(String::new())); - // false negative: default is from different expansion + let _string_default = outer!(Box::new(String::from(""))); let _vec2: Box> = Box::new(vec![]); + let _vec3: Box> = Box::new(Vec::from([])); + let _vec4: Box<_> = Box::new(Vec::from([false; 0])); + let _more = ret_ty_fn(); + call_ty_fn(Box::new(u8::default())); } + +fn ret_ty_fn() -> Box { + Box::new(bool::default()) +} + +#[allow(clippy::boxed_local)] +fn call_ty_fn(_b: Box) {} diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index b2030e95acb1..313255fc950e 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -1,59 +1,82 @@ error: `Box::new(_)` of default value - --> $DIR/box_default.rs:21:32 + --> $DIR/box_default.rs:22:32 | LL | let _string: Box = Box::new(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` | - = help: use `Box::default()` instead = note: `-D clippy::box-default` implied by `-D warnings` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:22:17 + --> $DIR/box_default.rs:23:17 | LL | let _byte = Box::new(u8::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:23:16 + --> $DIR/box_default.rs:24:16 | LL | let _vec = Box::new(Vec::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:24:17 + --> $DIR/box_default.rs:25:17 | LL | let _impl = Box::new(ImplementsDefault::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:25:18 + --> $DIR/box_default.rs:26:18 | LL | let _impl2 = Box::new(::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:26:42 + --> $DIR/box_default.rs:27:42 | LL | let _impl3: Box = Box::new(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:28:28 + --> $DIR/box_default.rs:29:28 | LL | let _in_macro = outer!(Box::new(String::new())); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:30:34 | - = help: use `Box::default()` instead +LL | let _string_default = outer!(Box::new(String::from(""))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` -error: aborting due to 7 previous errors +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:31:46 + | +LL | let _vec2: Box> = Box::new(vec![]); + | ^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:32:33 + | +LL | let _vec3: Box> = Box::new(Vec::from([])); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:33:25 + | +LL | let _vec4: Box<_> = Box::new(Vec::from([false; 0])); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:35:16 + | +LL | call_ty_fn(Box::new(u8::default())); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:39:5 + | +LL | Box::new(bool::default()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: aborting due to 13 previous errors From 09a554db25840a35bbfc13d531e288801b10fe6a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 6 Oct 2022 17:41:53 +0200 Subject: [PATCH 0031/1126] Merge commit '8f1ebdd18bdecc621f16baaf779898cc08cc2766' into clippyup --- clippy_lints/src/attrs.rs | 3 +- clippy_lints/src/format_args.rs | 17 +++------ clippy_utils/src/macros.rs | 2 +- tests/ui/uninlined_format_args.fixed | 11 ++++-- tests/ui/uninlined_format_args.stderr | 53 +-------------------------- tests/ui/unsafe_removed_from_name.rs | 3 ++ 6 files changed, 20 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 5f45c69d7f98..0bd1f8b784e8 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -357,7 +357,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate" - | "macro_use_imports", + | "macro_use_imports" + | "unsafe_removed_from_name", ) }) { diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index cefebc2a98a2..99bef62f8143 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -8,7 +8,7 @@ use if_chain::if_chain; use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; use rustc_semver::RustcVersion; @@ -173,17 +173,10 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si return; } - // FIXME: Properly ignore a rare case where the format string is wrapped in a macro. - // Example: `format!(indoc!("{}"), foo);` - // If inlined, they will cause a compilation error: - // > to avoid ambiguity, `format_args!` cannot capture variables - // > when the format string is expanded from a macro - // @Alexendoo explanation: - // > indoc! is a proc macro that is producing a string literal with its span - // > set to its input it's not marked as from expansion, and since it's compatible - // > tokenization wise clippy_utils::is_from_proc_macro wouldn't catch it either - // This might be a relatively expensive test, so do it only we are ready to replace. - // See more examples in tests/ui/uninlined_format_args.rs + // Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 + if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) { + return; + } span_lint_and_then( cx, diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index dd0ce1da6575..5a63c290a315 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -414,7 +414,7 @@ impl FormatString { struct FormatArgsValues<'tcx> { /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for - /// `format!("{x} {} {y}", 1, z + 2)`. + /// `format!("{x} {} {}", 1, z + 2)`. value_args: Vec<&'tcx Expr<'tcx>>, /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in /// `value_args` diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index dcf10ed60a25..3ca7a4019025 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -44,7 +44,9 @@ fn tester(fn_arg: i32) { println!("val='{local_i32}'"); // space+tab println!("val='{local_i32}'"); // tab+space println!( - "val='{local_i32}'" + "val='{ + }'", + local_i32 ); println!("{local_i32}"); println!("{fn_arg}"); @@ -108,7 +110,8 @@ fn tester(fn_arg: i32) { println!("{local_f64:width$.prec$}"); println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); println!( - "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", + local_i32, width, prec, ); println!( "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", @@ -139,7 +142,9 @@ fn tester(fn_arg: i32) { println!(no_param_str!(), local_i32); println!( - "{val}", + "{}", + // comment with a comma , in it + val, ); println!("{val}"); diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index 1b4dada28dac..d1a774926342 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -59,22 +59,6 @@ LL - println!("val='{ }'", local_i32); // tab+space LL + println!("val='{local_i32}'"); // tab+space | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:46:5 - | -LL | / println!( -LL | | "val='{ -LL | | }'", -LL | | local_i32 -LL | | ); - | |_____^ - | -help: change this to - | -LL - "val='{ -LL + "val='{local_i32}'" - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:51:5 | @@ -783,25 +767,6 @@ LL - println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:112:5 - | -LL | / println!( -LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", -LL | | local_i32, width, prec, -LL | | ); - | |_____^ - | -help: change this to - | -LL ~ "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:123:5 | @@ -850,22 +815,6 @@ LL - println!("{}", format!("{}", local_i32)); LL + println!("{}", format!("{local_i32}")); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:144:5 - | -LL | / println!( -LL | | "{}", -LL | | // comment with a comma , in it -LL | | val, -LL | | ); - | |_____^ - | -help: change this to - | -LL - "{}", -LL + "{val}", - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:149:5 | @@ -890,5 +839,5 @@ LL - println!("expand='{}'", local_i32); LL + println!("expand='{local_i32}'"); | -error: aborting due to 73 previous errors +error: aborting due to 70 previous errors diff --git a/tests/ui/unsafe_removed_from_name.rs b/tests/ui/unsafe_removed_from_name.rs index cde4e96d668c..d29888ac62f6 100644 --- a/tests/ui/unsafe_removed_from_name.rs +++ b/tests/ui/unsafe_removed_from_name.rs @@ -24,4 +24,7 @@ use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; use mod_with_some_unsafe_things::Safe as IPromiseItsSafeThisTime; use mod_with_some_unsafe_things::Unsafe as SuperUnsafeModThing; +#[allow(clippy::unsafe_removed_from_name)] +use mod_with_some_unsafe_things::Unsafe as SuperSafeThing; + fn main() {} From 493684888f0f68b43d46d70b89c2f2a75091601f Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 6 Oct 2022 12:12:55 -0400 Subject: [PATCH 0032/1126] Change uninlined_format_args into a style lint --- clippy_lints/src/format_args.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 + 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 45ed21e066ad..f8da7f222788 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -111,7 +111,7 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.65.0"] pub UNINLINED_FORMAT_ARGS, - pedantic, + style, "using non-inlined variables in `format!` calls" } diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 5d26e4b33601..d09ee6443702 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -71,6 +71,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(format::USELESS_FORMAT), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), LintId::of(formatting::POSSIBLE_MISSING_COMMA), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index dd3e2b7d29c1..1b9d575f3e4c 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -29,7 +29,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), - LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(functions::MUST_USE_CANDIDATE), LintId::of(functions::TOO_MANY_LINES), LintId::of(if_not_else::IF_NOT_ELSE), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 8e1390167dc8..81fefb84e0da 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -25,6 +25,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(enum_variants::MODULE_INCEPTION), LintId::of(eta_reduction::REDUNDANT_CLOSURE), LintId::of(float_literal::EXCESSIVE_PRECISION), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(from_over_into::FROM_OVER_INTO), LintId::of(from_str_radix_10::FROM_STR_RADIX_10), LintId::of(functions::DOUBLE_MUST_USE), From 23b16998c35674e74511bfe70a7324ca12119976 Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Thu, 6 Oct 2022 15:14:55 -0400 Subject: [PATCH 0033/1126] Add curlies to scrutinees with side effects --- .../src/matches/match_single_binding.rs | 31 +++++++++++++------ tests/ui/match_single_binding.fixed | 9 ++++++ tests/ui/match_single_binding.rs | 8 +++++ tests/ui/match_single_binding.stderr | 19 +++++++++++- 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 68682cedf1de..b64c45887483 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -58,6 +58,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, Some(span), + false, ); span_lint_and_sugg( @@ -90,6 +91,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, None, + false, ); (expr.span, sugg) }, @@ -107,10 +109,14 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e }, PatKind::Wild => { if ex.can_have_side_effects() { - let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0)); - let sugg = format!( - "{};\n{indent}{snippet_body}", - snippet_with_applicability(cx, ex.span, "..", &mut applicability) + let sugg = sugg_with_curlies( + cx, + (ex, expr), + (bind_names, matched_vars), + &snippet_body, + &mut applicability, + None, + true, ); span_lint_and_sugg( @@ -169,6 +175,7 @@ fn sugg_with_curlies<'a>( snippet_body: &str, applicability: &mut Applicability, assignment: Option, + needs_var_binding: bool, ) -> String { let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); @@ -200,9 +207,15 @@ fn sugg_with_curlies<'a>( s }); - format!( - "{cbrace_start}let {} = {};\n{indent}{assignment_str}{snippet_body}{cbrace_end}", - snippet_with_applicability(cx, bind_names, "..", applicability), - snippet_with_applicability(cx, matched_vars, "..", applicability) - ) + let scrutinee = if needs_var_binding { + snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() + } else { + format!( + "let {} = {}", + snippet_with_applicability(cx, bind_names, "..", applicability), + snippet_with_applicability(cx, matched_vars, "..", applicability) + ) + }; + + format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") } diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 951f552eb32b..a6e315e4773a 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -124,3 +124,12 @@ fn issue_8723() { let _ = val; } + +#[allow(dead_code)] +fn issue_9575() { + fn side_effects() {} + let _ = || { + side_effects(); + println!("Needs curlies"); + }; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 19c0fee8fd68..cecbd703e566 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -140,3 +140,11 @@ fn issue_8723() { let _ = val; } + +#[allow(dead_code)] +fn issue_9575() { + fn side_effects() {} + let _ = || match side_effects() { + _ => println!("Needs curlies"), + }; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 5d4e7314b213..2b9ec7ee7026 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -196,5 +196,22 @@ LL + suf LL ~ }; | -error: aborting due to 13 previous errors +error: this match could be replaced by its scrutinee and body + --> $DIR/match_single_binding.rs:147:16 + | +LL | let _ = || match side_effects() { + | ________________^ +LL | | _ => println!("Needs curlies"), +LL | | }; + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ let _ = || { +LL + side_effects(); +LL + println!("Needs curlies"); +LL ~ }; + | + +error: aborting due to 14 previous errors From 39164acf6e62a3ff55d6c210acc1095d9ccc95bb Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Thu, 6 Oct 2022 15:36:28 -0400 Subject: [PATCH 0034/1126] Fix flipped variable that made it through --- clippy_lints/src/matches/match_single_binding.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index b64c45887483..1bf8d4e96ad4 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -58,7 +58,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, Some(span), - false, + true, ); span_lint_and_sugg( @@ -91,7 +91,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, None, - false, + true, ); (expr.span, sugg) }, @@ -116,7 +116,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, None, - true, + false, ); span_lint_and_sugg( @@ -208,13 +208,13 @@ fn sugg_with_curlies<'a>( }); let scrutinee = if needs_var_binding { - snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() - } else { format!( "let {} = {}", snippet_with_applicability(cx, bind_names, "..", applicability), snippet_with_applicability(cx, matched_vars, "..", applicability) ) + } else { + snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() }; format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") From 037f698147eeb97699c06a270e8774d2f7a6fbbc Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 25 Aug 2022 00:14:21 +0000 Subject: [PATCH 0035/1126] `needless_borrow` uses `used_exactly_once` --- clippy_lints/src/dereference.rs | 99 ++++- clippy_lints/src/lib.rs | 1 - clippy_lints/src/redundant_clone.rs | 454 ++------------------ clippy_utils/src/lib.rs | 3 + clippy_utils/src/mir/maybe_storage_live.rs | 52 +++ clippy_utils/src/mir/mod.rs | 165 +++++++ clippy_utils/src/mir/possible_borrower.rs | 241 +++++++++++ clippy_utils/src/mir/possible_origin.rs | 59 +++ clippy_utils/src/mir/transitive_relation.rs | 29 ++ clippy_utils/src/paths.rs | 6 - tests/ui/needless_borrow.fixed | 62 ++- tests/ui/needless_borrow.rs | 60 ++- tests/ui/needless_borrow.stderr | 96 +++-- tests/ui/unnecessary_to_owned.fixed | 2 +- tests/ui/unnecessary_to_owned.rs | 2 +- 15 files changed, 855 insertions(+), 476 deletions(-) create mode 100644 clippy_utils/src/mir/maybe_storage_live.rs create mode 100644 clippy_utils/src/mir/mod.rs create mode 100644 clippy_utils/src/mir/possible_borrower.rs create mode 100644 clippy_utils/src/mir/possible_origin.rs create mode 100644 clippy_utils/src/mir/transitive_relation.rs diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 3cd8f236e7a5..45ee2fce4e47 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res}; @@ -11,13 +12,16 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, - GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, - Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp, + self as hir, + def_id::{DefId, LocalDefId}, + BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, ImplItem, + ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem, + TraitItemKind, TyKind, UnOp, }; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ self, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, @@ -141,7 +145,7 @@ declare_clippy_lint! { "dereferencing when the compiler would automatically dereference" } -impl_lint_pass!(Dereferencing => [ +impl_lint_pass!(Dereferencing<'_> => [ EXPLICIT_DEREF_METHODS, NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE, @@ -149,7 +153,7 @@ impl_lint_pass!(Dereferencing => [ ]); #[derive(Default)] -pub struct Dereferencing { +pub struct Dereferencing<'tcx> { state: Option<(State, StateData)>, // While parsing a `deref` method call in ufcs form, the path to the function is itself an @@ -170,11 +174,16 @@ pub struct Dereferencing { /// e.g. `m!(x) | Foo::Bar(ref x)` ref_locals: FxIndexMap>, + /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by + /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead + /// be moved. + possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + // `IntoIterator` for arrays requires Rust 1.53. msrv: Option, } -impl Dereferencing { +impl<'tcx> Dereferencing<'tcx> { #[must_use] pub fn new(msrv: Option) -> Self { Self { @@ -244,7 +253,7 @@ struct RefPat { hir_id: HirId, } -impl<'tcx> LateLintPass<'tcx> for Dereferencing { +impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Skip path expressions from deref calls. e.g. `Deref::deref(e)` @@ -278,7 +287,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); - let (position, adjustments) = walk_parents(cx, expr, self.msrv); + let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv); match kind { RefOp::Deref => { if let Position::FieldAccess { @@ -550,6 +559,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { } fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| { + local_def_id == cx.tcx.hir().body_owner_def_id(body.id()) + }) { + self.possible_borrowers.pop(); + } + if Some(body.id()) == self.current_body { for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { let replacements = pat.replacements; @@ -682,6 +697,7 @@ impl Position { #[expect(clippy::too_many_lines)] fn walk_parents<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, e: &'tcx Expr<'_>, msrv: Option, ) -> (Position, &'tcx [Adjustment<'tcx>]) { @@ -796,7 +812,16 @@ fn walk_parents<'tcx>( Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), None => { if let ty::Param(param_ty) = ty.skip_binder().kind() { - needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv) + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i, + *param_ty, + e, + precedence, + msrv, + ) } else { ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) .position_for_arg() @@ -844,7 +869,16 @@ fn walk_parents<'tcx>( args.iter().position(|arg| arg.hir_id == child_id).map(|i| { let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1]; if let ty::Param(param_ty) = ty.kind() { - needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv) + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i + 1, + *param_ty, + e, + precedence, + msrv, + ) } else { ty_auto_deref_stability( cx, @@ -1018,8 +1052,10 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { // If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`. // The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to // be moved, but it cannot be. +#[expect(clippy::too_many_arguments)] fn needless_borrow_impl_arg_position<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, parent: &Expr<'tcx>, arg_index: usize, param_ty: ParamTy, @@ -1082,10 +1118,13 @@ fn needless_borrow_impl_arg_position<'tcx>( // elements are modified each time `check_referent` is called. let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); - let mut check_referent = |referent| { + let mut check_reference_and_referent = |reference, referent| { let referent_ty = cx.typeck_results().expr_ty(referent); - if !is_copy(cx, referent_ty) { + if !is_copy(cx, referent_ty) + && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) + || !referent_used_exactly_once(cx, possible_borrowers, reference)) + { return false; } @@ -1127,7 +1166,7 @@ fn needless_borrow_impl_arg_position<'tcx>( let mut needless_borrow = false; while let ExprKind::AddrOf(_, _, referent) = expr.kind { - if !check_referent(referent) { + if !check_reference_and_referent(expr, referent) { break; } expr = referent; @@ -1155,6 +1194,36 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { }) } +fn referent_used_exactly_once<'a, 'tcx>( + cx: &'a LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + reference: &Expr<'tcx>, +) -> bool { + let mir = enclosing_mir(cx.tcx, reference.hir_id); + if let Some(local) = expr_local(cx.tcx, reference) + && let [location] = *local_assignments(mir, local).as_slice() + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = + mir.basic_blocks[location.block].statements[location.statement_index].kind + && !place.has_deref() + { + let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); + if possible_borrowers + .last() + .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) + { + possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); + } + let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; + // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is + // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of + // itself. See the comment in that method for an explanation as to why. + possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) + && used_exactly_once(mir, place.local).unwrap_or(false) + } else { + false + } +} + // Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting // projected type that is a type parameter. Returns `false` if replacing the types would have an // effect on the function signature beyond substituting `new_ty` for `param_ty`. @@ -1439,8 +1508,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data } } -impl Dereferencing { - fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { +impl<'tcx> Dereferencing<'tcx> { + fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2dcefd78763b..d9dfb4fc4e0e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -38,7 +38,6 @@ extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; -extern crate rustc_mir_dataflow; extern crate rustc_parse; extern crate rustc_session; extern crate rustc_span; diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 9fd86331ec75..aedbe08e3e46 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,25 +1,18 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use if_chain::if_chain; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{def_id, Body, FnDecl, HirId}; -use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::{ - self, traversal, - visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _}, - Mutability, -}; -use rustc_middle::ty::{self, visit::TypeVisitor, Ty}; -use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis, ResultsCursor}; +use rustc_middle::mir; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::sym; -use std::ops::ControlFlow; macro_rules! unwrap_or_continue { ($x:expr) => { @@ -89,21 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let mir = cx.tcx.optimized_mir(def_id.to_def_id()); - let possible_origin = { - let mut vis = PossibleOriginVisitor::new(mir); - vis.visit_body(mir); - vis.into_map(cx) - }; - let maybe_storage_live_result = MaybeStorageLive - .into_engine(cx.tcx, mir) - .pass_name("redundant_clone") - .iterate_to_fixpoint() - .into_results_cursor(mir); - let mut possible_borrower = { - let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); - vis.visit_body(mir); - vis.into_map(cx, maybe_storage_live_result) - }; + let mut possible_borrower = PossibleBorrowerMap::new(cx, mir); for (bb, bbdata) in mir.basic_blocks.iter_enumerated() { let terminator = bbdata.terminator(); @@ -374,403 +353,40 @@ struct CloneUsage { /// Whether the clone value is mutated. clone_consumed_or_mutated: bool, } + fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage { - struct V { - cloned: mir::Local, - clone: mir::Local, - result: CloneUsage, - } - impl<'tcx> mir::visit::Visitor<'tcx> for V { - fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) { - let statements = &data.statements; - for (statement_index, statement) in statements.iter().enumerate() { - self.visit_statement(statement, mir::Location { block, statement_index }); - } - - self.visit_terminator( - data.terminator(), - mir::Location { - block, - statement_index: statements.len(), - }, - ); - } - - fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) { - let local = place.local; - - if local == self.cloned - && !matches!( - ctx, - PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) - ) - { - self.result.cloned_used = true; - self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| { - matches!( - ctx, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) - ) - .then(|| loc) - }); - } else if local == self.clone { - match ctx { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { - self.result.clone_consumed_or_mutated = true; - }, - _ => {}, - } - } - } - } - - let init = CloneUsage { - cloned_used: false, - cloned_consume_or_mutate_loc: None, - // Consider non-temporary clones consumed. - // TODO: Actually check for mutation of non-temporaries. - clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp, - }; - traversal::ReversePostorder::new(mir, bb) - .skip(1) - .fold(init, |usage, (tbb, tdata)| { - // Short-circuit - if (usage.cloned_used && usage.clone_consumed_or_mutated) || - // Give up on loops - tdata.terminator().successors().any(|s| s == bb) - { - return CloneUsage { - cloned_used: true, - clone_consumed_or_mutated: true, - ..usage - }; - } - - let mut v = V { - cloned, - clone, - result: usage, - }; - v.visit_basic_block_data(tbb, tdata); - v.result - }) -} - -/// Determines liveness of each local purely based on `StorageLive`/`Dead`. -#[derive(Copy, Clone)] -struct MaybeStorageLive; - -impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { - type Domain = BitSet; - const NAME: &'static str = "maybe_storage_live"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = dead - BitSet::new_empty(body.local_decls.len()) - } - - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { - for arg in body.args_iter() { - state.insert(arg); - } - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { - type Idx = mir::Local; - - fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { - match stmt.kind { - mir::StatementKind::StorageLive(l) => trans.gen(l), - mir::StatementKind::StorageDead(l) => trans.kill(l), - _ => (), - } - } - - fn terminator_effect( - &self, - _trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - _loc: mir::Location, - ) { - } - - fn call_return_effect( - &self, - _trans: &mut impl GenKill, - _block: mir::BasicBlock, - _return_places: CallReturnPlaces<'_, 'tcx>, - ) { - // Nothing to do when a call returns successfully - } -} - -/// Collects the possible borrowers of each local. -/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` -/// possible borrowers of `a`. -struct PossibleBorrowerVisitor<'a, 'tcx> { - possible_borrower: TransitiveRelation, - body: &'a mir::Body<'tcx>, - cx: &'a LateContext<'tcx>, - possible_origin: FxHashMap>, -} - -impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> { - fn new( - cx: &'a LateContext<'tcx>, - body: &'a mir::Body<'tcx>, - possible_origin: FxHashMap>, - ) -> Self { - Self { - possible_borrower: TransitiveRelation::default(), - cx, - body, - possible_origin, - } - } - - fn into_map( - self, - cx: &LateContext<'tcx>, - maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>, - ) -> PossibleBorrowerMap<'a, 'tcx> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } - - let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); - if !borrowers.is_empty() { - map.insert(row, borrowers); - } - } - - let bs = BitSet::new_empty(self.body.local_decls.len()); - PossibleBorrowerMap { - map, - maybe_live, - bitset: (bs.clone(), bs), - } - } -} - -impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - mir::Rvalue::Ref(_, _, borrowed) => { - self.possible_borrower.add(borrowed.local, lhs); - }, - other => { - if ContainsRegion - .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) - .is_continue() - { - return; - } - rvalue_locals(other, |rhs| { - if lhs != rhs { - self.possible_borrower.add(rhs, lhs); - } - }); - }, - } - } - - fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { - if let mir::TerminatorKind::Call { - args, - destination: mir::Place { local: dest, .. }, - .. - } = &terminator.kind - { - // TODO add doc - // If the call returns something with lifetimes, - // let's conservatively assume the returned value contains lifetime of all the arguments. - // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. - - let mut immutable_borrowers = vec![]; - let mut mutable_borrowers = vec![]; - - for op in args { - match op { - mir::Operand::Copy(p) | mir::Operand::Move(p) => { - if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() { - mutable_borrowers.push(p.local); - } else { - immutable_borrowers.push(p.local); - } - }, - mir::Operand::Constant(..) => (), - } - } - - let mut mutable_variables: Vec = mutable_borrowers - .iter() - .filter_map(|r| self.possible_origin.get(r)) - .flat_map(HybridBitSet::iter) - .collect(); - - if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { - mutable_variables.push(*dest); - } - - for y in mutable_variables { - for x in &immutable_borrowers { - self.possible_borrower.add(*x, y); - } - for x in &mutable_borrowers { - self.possible_borrower.add(*x, y); - } - } - } - } -} - -/// Collect possible borrowed for every `&mut` local. -/// For example, `_1 = &mut _2` generate _1: {_2,...} -/// Known Problems: not sure all borrowed are tracked -struct PossibleOriginVisitor<'a, 'tcx> { - possible_origin: TransitiveRelation, - body: &'a mir::Body<'tcx>, -} - -impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { - fn new(body: &'a mir::Body<'tcx>) -> Self { - Self { - possible_origin: TransitiveRelation::default(), - body, - } - } - - fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } - - let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); - if !borrowers.is_empty() { - map.insert(row, borrowers); - } - } - map - } -} - -impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - // Only consider `&mut`, which can modify origin place - mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) | - // _2: &mut _; - // _3 = move _2 - mir::Rvalue::Use(mir::Operand::Move(borrowed)) | - // _3 = move _2 as &mut _; - mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _) - => { - self.possible_origin.add(lhs, borrowed.local); - }, - _ => {}, - } - } -} - -struct ContainsRegion; - -impl TypeVisitor<'_> for ContainsRegion { - type BreakTy = (); - - fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow { - ControlFlow::BREAK - } -} - -fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { - use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; - - let mut visit_op = |op: &mir::Operand<'_>| match op { - mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), - mir::Operand::Constant(..) => (), - }; - - match rvalue { - Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), - Aggregate(_, ops) => ops.iter().for_each(visit_op), - BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { - visit_op(lhs); - visit_op(rhs); + if let Some(( + LocalUsage { + local_use_locs: cloned_use_locs, + local_consume_or_mutate_locs: cloned_consume_or_mutate_locs, }, - _ => (), - } -} - -/// Result of `PossibleBorrowerVisitor`. -struct PossibleBorrowerMap<'a, 'tcx> { - /// Mapping `Local -> its possible borrowers` - map: FxHashMap>, - maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>, - // Caches to avoid allocation of `BitSet` on every query - bitset: (BitSet, BitSet), -} - -impl PossibleBorrowerMap<'_, '_> { - /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. - fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after_primary_effect(at); - - self.bitset.0.clear(); - let maybe_live = &mut self.maybe_live; - if let Some(bitset) = self.map.get(&borrowed) { - for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { - self.bitset.0.insert(b); - } - } else { - return false; + LocalUsage { + local_use_locs: _, + local_consume_or_mutate_locs: clone_consume_or_mutate_locs, + }, + )) = visit_local_usage( + &[cloned, clone], + mir, + mir::Location { + block: bb, + statement_index: mir.basic_blocks[bb].statements.len(), + }, + ) + .map(|mut vec| (vec.remove(0), vec.remove(0))) + { + CloneUsage { + cloned_used: !cloned_use_locs.is_empty(), + cloned_consume_or_mutate_loc: cloned_consume_or_mutate_locs.first().copied(), + // Consider non-temporary clones consumed. + // TODO: Actually check for mutation of non-temporaries. + clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp + || !clone_consume_or_mutate_locs.is_empty(), } - - self.bitset.1.clear(); - for b in borrowers { - self.bitset.1.insert(*b); + } else { + CloneUsage { + cloned_used: true, + cloned_consume_or_mutate_loc: None, + clone_consumed_or_mutated: true, } - - self.bitset.0 == self.bitset.1 - } - - fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after_primary_effect(at); - self.maybe_live.contains(local) - } -} - -#[derive(Default)] -struct TransitiveRelation { - relations: FxHashMap>, -} -impl TransitiveRelation { - fn add(&mut self, a: mir::Local, b: mir::Local) { - self.relations.entry(a).or_default().push(b); - } - - fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet { - let mut seen = HybridBitSet::new_empty(domain_size); - let mut stack = vec![a]; - while let Some(u) = stack.pop() { - if let Some(edges) = self.relations.get(&u) { - for &v in edges { - if seen.insert(v) { - stack.push(v); - } - } - } - } - seen } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 42374fdd7baf..3597c58e072f 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -25,10 +25,12 @@ extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_analysis; +extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; +extern crate rustc_mir_dataflow; extern crate rustc_parse_format; extern crate rustc_session; extern crate rustc_span; @@ -48,6 +50,7 @@ pub mod eager_or_lazy; pub mod higher; mod hir_utils; pub mod macros; +pub mod mir; pub mod msrvs; pub mod numeric_literal; pub mod paths; diff --git a/clippy_utils/src/mir/maybe_storage_live.rs b/clippy_utils/src/mir/maybe_storage_live.rs new file mode 100644 index 000000000000..d262b335d99d --- /dev/null +++ b/clippy_utils/src/mir/maybe_storage_live.rs @@ -0,0 +1,52 @@ +use rustc_index::bit_set::BitSet; +use rustc_middle::mir; +use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis}; + +/// Determines liveness of each local purely based on `StorageLive`/`Dead`. +#[derive(Copy, Clone)] +pub(super) struct MaybeStorageLive; + +impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { + type Domain = BitSet; + const NAME: &'static str = "maybe_storage_live"; + + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) + } + + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { + for arg in body.args_iter() { + state.insert(arg); + } + } +} + +impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { + type Idx = mir::Local; + + fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { + match stmt.kind { + mir::StatementKind::StorageLive(l) => trans.gen(l), + mir::StatementKind::StorageDead(l) => trans.kill(l), + _ => (), + } + } + + fn terminator_effect( + &self, + _trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + _loc: mir::Location, + ) { + } + + fn call_return_effect( + &self, + _trans: &mut impl GenKill, + _block: mir::BasicBlock, + _return_places: CallReturnPlaces<'_, 'tcx>, + ) { + // Nothing to do when a call returns successfully + } +} diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs new file mode 100644 index 000000000000..c8aa6f3e595d --- /dev/null +++ b/clippy_utils/src/mir/mod.rs @@ -0,0 +1,165 @@ +use rustc_hir::{Expr, HirId}; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{ + traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, +}; +use rustc_middle::ty::TyCtxt; + +mod maybe_storage_live; + +mod possible_borrower; +pub use possible_borrower::PossibleBorrowerMap; + +mod possible_origin; + +mod transitive_relation; + +#[derive(Clone, Debug, Default)] +pub struct LocalUsage { + /// The locations where the local is used, if any. + pub local_use_locs: Vec, + /// The locations where the local is consumed or mutated, if any. + pub local_consume_or_mutate_locs: Vec, +} + +pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -> Option> { + let init = vec![ + LocalUsage { + local_use_locs: Vec::new(), + local_consume_or_mutate_locs: Vec::new(), + }; + locals.len() + ]; + + traversal::ReversePostorder::new(mir, location.block).try_fold(init, |usage, (tbb, tdata)| { + // Give up on loops + if tdata.terminator().successors().any(|s| s == location.block) { + return None; + } + + let mut v = V { + locals, + location, + results: usage, + }; + v.visit_basic_block_data(tbb, tdata); + Some(v.results) + }) +} + +struct V<'a> { + locals: &'a [Local], + location: Location, + results: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for V<'a> { + fn visit_place(&mut self, place: &Place<'tcx>, ctx: PlaceContext, loc: Location) { + if loc.block == self.location.block && loc.statement_index <= self.location.statement_index { + return; + } + + let local = place.local; + + for (i, self_local) in self.locals.iter().enumerate() { + if local == *self_local { + if !matches!( + ctx, + PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) + ) { + self.results[i].local_use_locs.push(loc); + } + if matches!( + ctx, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) { + self.results[i].local_consume_or_mutate_locs.push(loc); + } + } + } + } +} + +/// Convenience wrapper around `visit_local_usage`. +pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option { + visit_local_usage( + &[local], + mir, + Location { + block: START_BLOCK, + statement_index: 0, + }, + ) + .map(|mut vec| { + let LocalUsage { local_use_locs, .. } = vec.remove(0); + local_use_locs + .into_iter() + .filter(|location| !is_local_assignment(mir, local, *location)) + .count() + == 1 + }) +} + +/// Returns the `mir::Body` containing the node associated with `hir_id`. +#[allow(clippy::module_name_repetitions)] +pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> { + let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id); + tcx.optimized_mir(body_owner_local_def_id.to_def_id()) +} + +/// Tries to determine the `Local` corresponding to `expr`, if any. +/// This function is expensive and should be used sparingly. +pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option { + let mir = enclosing_mir(tcx, expr.hir_id); + mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| { + if local_decl.source_info.span == expr.span { + Some(local) + } else { + None + } + }) +} + +/// Returns a vector of `mir::Location` where `local` is assigned. Each statement referred to has +/// kind `StatementKind::Assign`. +pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec { + let mut locations = Vec::new(); + for (block, data) in mir.basic_blocks.iter_enumerated() { + for statement_index in 0..=data.statements.len() { + let location = Location { block, statement_index }; + if is_local_assignment(mir, local, location) { + locations.push(location); + } + } + } + locations +} + +// `is_local_assignment` is based on `is_place_assignment`: +// https://github.com/rust-lang/rust/blob/b7413511dc85ec01ef4b91785f86614589ac6103/compiler/rustc_middle/src/mir/visit.rs#L1350 +fn is_local_assignment(mir: &Body<'_>, local: Local, location: Location) -> bool { + let Location { block, statement_index } = location; + let basic_block = &mir.basic_blocks[block]; + if statement_index < basic_block.statements.len() { + let statement = &basic_block.statements[statement_index]; + if let StatementKind::Assign(box (place, _)) = statement.kind { + place.as_local() == Some(local) + } else { + false + } + } else { + let terminator = basic_block.terminator(); + match &terminator.kind { + TerminatorKind::Call { destination, .. } => destination.as_local() == Some(local), + TerminatorKind::InlineAsm { operands, .. } => operands.iter().any(|operand| { + if let InlineAsmOperand::Out { place: Some(place), .. } = operand { + place.as_local() == Some(local) + } else { + false + } + }), + _ => false, + } + } +} diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs new file mode 100644 index 000000000000..25717bf3d2fe --- /dev/null +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -0,0 +1,241 @@ +use super::{ + maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor, + transitive_relation::TransitiveRelation, +}; +use crate::ty::is_copy; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_lint::LateContext; +use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; +use rustc_middle::ty::{self, visit::TypeVisitor}; +use rustc_mir_dataflow::{Analysis, ResultsCursor}; +use std::ops::ControlFlow; + +/// Collects the possible borrowers of each local. +/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` +/// possible borrowers of `a`. +#[allow(clippy::module_name_repetitions)] +struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { + possible_borrower: TransitiveRelation, + body: &'b mir::Body<'tcx>, + cx: &'a LateContext<'tcx>, + possible_origin: FxHashMap>, +} + +impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn new( + cx: &'a LateContext<'tcx>, + body: &'b mir::Body<'tcx>, + possible_origin: FxHashMap>, + ) -> Self { + Self { + possible_borrower: TransitiveRelation::default(), + cx, + body, + possible_origin, + } + } + + fn into_map( + self, + cx: &'a LateContext<'tcx>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + ) -> PossibleBorrowerMap<'b, 'tcx> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } + + let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); + if !borrowers.is_empty() { + map.insert(row, borrowers); + } + } + + let bs = BitSet::new_empty(self.body.local_decls.len()); + PossibleBorrowerMap { + map, + maybe_live, + bitset: (bs.clone(), bs), + } + } +} + +impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + mir::Rvalue::Ref(_, _, borrowed) => { + self.possible_borrower.add(borrowed.local, lhs); + }, + other => { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) + .is_continue() + { + return; + } + rvalue_locals(other, |rhs| { + if lhs != rhs { + self.possible_borrower.add(rhs, lhs); + } + }); + }, + } + } + + fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { + if let mir::TerminatorKind::Call { + args, + destination: mir::Place { local: dest, .. }, + .. + } = &terminator.kind + { + // TODO add doc + // If the call returns something with lifetimes, + // let's conservatively assume the returned value contains lifetime of all the arguments. + // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. + + let mut immutable_borrowers = vec![]; + let mut mutable_borrowers = vec![]; + + for op in args { + match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => { + if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() { + mutable_borrowers.push(p.local); + } else { + immutable_borrowers.push(p.local); + } + }, + mir::Operand::Constant(..) => (), + } + } + + let mut mutable_variables: Vec = mutable_borrowers + .iter() + .filter_map(|r| self.possible_origin.get(r)) + .flat_map(HybridBitSet::iter) + .collect(); + + if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { + mutable_variables.push(*dest); + } + + for y in mutable_variables { + for x in &immutable_borrowers { + self.possible_borrower.add(*x, y); + } + for x in &mutable_borrowers { + self.possible_borrower.add(*x, y); + } + } + } + } +} + +struct ContainsRegion; + +impl TypeVisitor<'_> for ContainsRegion { + type BreakTy = (); + + fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow { + ControlFlow::BREAK + } +} + +fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { + use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; + + let mut visit_op = |op: &mir::Operand<'_>| match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), + mir::Operand::Constant(..) => (), + }; + + match rvalue { + Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), + Aggregate(_, ops) => ops.iter().for_each(visit_op), + BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { + visit_op(lhs); + visit_op(rhs); + }, + _ => (), + } +} + +/// Result of `PossibleBorrowerVisitor`. +#[allow(clippy::module_name_repetitions)] +pub struct PossibleBorrowerMap<'b, 'tcx> { + /// Mapping `Local -> its possible borrowers` + pub map: FxHashMap>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + // Caches to avoid allocation of `BitSet` on every query + pub bitset: (BitSet, BitSet), +} + +impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { + pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { + let possible_origin = { + let mut vis = PossibleOriginVisitor::new(mir); + vis.visit_body(mir); + vis.into_map(cx) + }; + let maybe_storage_live_result = MaybeStorageLive + .into_engine(cx.tcx, mir) + .pass_name("redundant_clone") + .iterate_to_fixpoint() + .into_results_cursor(mir); + let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); + vis.visit_body(mir); + vis.into_map(cx, maybe_storage_live_result) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. + pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { + self.bounded_borrowers(borrowers, borrowers, borrowed, at) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below` + /// but no more than `above`. + pub fn bounded_borrowers( + &mut self, + below: &[mir::Local], + above: &[mir::Local], + borrowed: mir::Local, + at: mir::Location, + ) -> bool { + self.maybe_live.seek_after_primary_effect(at); + + self.bitset.0.clear(); + let maybe_live = &mut self.maybe_live; + if let Some(bitset) = self.map.get(&borrowed) { + for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { + self.bitset.0.insert(b); + } + } else { + return false; + } + + self.bitset.1.clear(); + for b in below { + self.bitset.1.insert(*b); + } + + if !self.bitset.0.superset(&self.bitset.1) { + return false; + } + + for b in above { + self.bitset.0.remove(*b); + } + + self.bitset.0.is_empty() + } + + pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { + self.maybe_live.seek_after_primary_effect(at); + self.maybe_live.contains(local) + } +} diff --git a/clippy_utils/src/mir/possible_origin.rs b/clippy_utils/src/mir/possible_origin.rs new file mode 100644 index 000000000000..8e7513d740ab --- /dev/null +++ b/clippy_utils/src/mir/possible_origin.rs @@ -0,0 +1,59 @@ +use super::transitive_relation::TransitiveRelation; +use crate::ty::is_copy; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::HybridBitSet; +use rustc_lint::LateContext; +use rustc_middle::mir; + +/// Collect possible borrowed for every `&mut` local. +/// For example, `_1 = &mut _2` generate _1: {_2,...} +/// Known Problems: not sure all borrowed are tracked +#[allow(clippy::module_name_repetitions)] +pub(super) struct PossibleOriginVisitor<'a, 'tcx> { + possible_origin: TransitiveRelation, + body: &'a mir::Body<'tcx>, +} + +impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { + pub fn new(body: &'a mir::Body<'tcx>) -> Self { + Self { + possible_origin: TransitiveRelation::default(), + body, + } + } + + pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } + + let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); + if !borrowers.is_empty() { + map.insert(row, borrowers); + } + } + map + } +} + +impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + // Only consider `&mut`, which can modify origin place + mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) | + // _2: &mut _; + // _3 = move _2 + mir::Rvalue::Use(mir::Operand::Move(borrowed)) | + // _3 = move _2 as &mut _; + mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _) + => { + self.possible_origin.add(lhs, borrowed.local); + }, + _ => {}, + } + } +} diff --git a/clippy_utils/src/mir/transitive_relation.rs b/clippy_utils/src/mir/transitive_relation.rs new file mode 100644 index 000000000000..7fe2960739fa --- /dev/null +++ b/clippy_utils/src/mir/transitive_relation.rs @@ -0,0 +1,29 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::HybridBitSet; +use rustc_middle::mir; + +#[derive(Default)] +pub(super) struct TransitiveRelation { + relations: FxHashMap>, +} + +impl TransitiveRelation { + pub fn add(&mut self, a: mir::Local, b: mir::Local) { + self.relations.entry(a).or_default().push(b); + } + + pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet { + let mut seen = HybridBitSet::new_empty(domain_size); + let mut stack = vec![a]; + while let Some(u) = stack.pop() { + if let Some(edges) = self.relations.get(&u) { + for &v in edges { + if seen.insert(v) { + stack.push(v); + } + } + } + } + seen + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 13938645fc3e..78adc4536543 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -119,17 +119,11 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; /// Preferably use the diagnostic item `sym::Result` where possible pub const RESULT: [&str; 3] = ["core", "result", "Result"]; diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index aa2687159ef4..340e89d2db1d 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -3,7 +3,11 @@ #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables)] -#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] +#[allow( + clippy::uninlined_format_args, + clippy::unnecessary_mut_passed, + clippy::unnecessary_to_owned +)] fn main() { let a = 5; let ref_a = &a; @@ -134,6 +138,7 @@ fn main() { multiple_constraints([[""]]); multiple_constraints_normalizes_to_same(X, X); let _ = Some("").unwrap_or(""); + let _ = std::fs::write("x", "".to_string()); only_sized(&""); // Don't lint. `Sized` is only bound let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound @@ -276,8 +281,9 @@ mod copyable_iterator { fn dont_warn(mut x: Iter) { takes_iter(&mut x); } + #[allow(unused_mut)] fn warn(mut x: &mut Iter) { - takes_iter(&mut x) + takes_iter(x) } } @@ -327,3 +333,55 @@ fn issue9383() { ManuallyDrop::drop(&mut ocean.coral); } } + +#[allow(dead_code)] +fn closure_test() { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", arg); + let _ = std::fs::write("x", loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); +} + +#[allow(dead_code)] +mod significant_drop { + #[derive(Debug)] + struct X; + + #[derive(Debug)] + struct Y; + + impl Drop for Y { + fn drop(&mut self) {} + } + + fn foo(x: X, y: Y) { + debug(x); + debug(&y); // Don't lint. Has significant drop + } + + fn debug(_: impl std::fmt::Debug) {} +} + +#[allow(dead_code)] +mod used_exactly_once { + fn foo(x: String) { + use_x(x); + } + fn use_x(_: impl AsRef) {} +} + +#[allow(dead_code)] +mod used_more_than_once { + fn foo(x: String) { + use_x(&x); + use_x_again(&x); + } + fn use_x(_: impl AsRef) {} + fn use_x_again(_: impl AsRef) {} +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index d41251e8f6aa..c93711ac8e28 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -3,7 +3,11 @@ #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables)] -#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] +#[allow( + clippy::uninlined_format_args, + clippy::unnecessary_mut_passed, + clippy::unnecessary_to_owned +)] fn main() { let a = 5; let ref_a = &a; @@ -134,6 +138,7 @@ fn main() { multiple_constraints(&[[""]]); multiple_constraints_normalizes_to_same(&X, X); let _ = Some("").unwrap_or(&""); + let _ = std::fs::write("x", &"".to_string()); only_sized(&""); // Don't lint. `Sized` is only bound let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound @@ -276,6 +281,7 @@ mod copyable_iterator { fn dont_warn(mut x: Iter) { takes_iter(&mut x); } + #[allow(unused_mut)] fn warn(mut x: &mut Iter) { takes_iter(&mut x) } @@ -327,3 +333,55 @@ fn issue9383() { ManuallyDrop::drop(&mut ocean.coral); } } + +#[allow(dead_code)] +fn closure_test() { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", &arg); + let _ = std::fs::write("x", &loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); +} + +#[allow(dead_code)] +mod significant_drop { + #[derive(Debug)] + struct X; + + #[derive(Debug)] + struct Y; + + impl Drop for Y { + fn drop(&mut self) {} + } + + fn foo(x: X, y: Y) { + debug(&x); + debug(&y); // Don't lint. Has significant drop + } + + fn debug(_: impl std::fmt::Debug) {} +} + +#[allow(dead_code)] +mod used_exactly_once { + fn foo(x: String) { + use_x(&x); + } + fn use_x(_: impl AsRef) {} +} + +#[allow(dead_code)] +mod used_more_than_once { + fn foo(x: String) { + use_x(&x); + use_x_again(&x); + } + fn use_x(_: impl AsRef) {} + fn use_x_again(_: impl AsRef) {} +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 5af68706d4ba..8b593268bec2 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,5 +1,5 @@ error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:11:15 + --> $DIR/needless_borrow.rs:15:15 | LL | let _ = x(&&a); // warn | ^^^ help: change this to: `&a` @@ -7,172 +7,208 @@ LL | let _ = x(&&a); // warn = note: `-D clippy::needless-borrow` implied by `-D warnings` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:15:13 + --> $DIR/needless_borrow.rs:19:13 | LL | mut_ref(&mut &mut b); // warn | ^^^^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:27:13 + --> $DIR/needless_borrow.rs:31:13 | LL | &&a | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:29:15 + --> $DIR/needless_borrow.rs:33:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:35:27 + --> $DIR/needless_borrow.rs:39:27 | LL | break &ref_a; | ^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:42:15 + --> $DIR/needless_borrow.rs:46:15 | LL | let _ = x(&&&a); | ^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:43:15 + --> $DIR/needless_borrow.rs:47:15 | LL | let _ = x(&mut &&a); | ^^^^^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:44:15 + --> $DIR/needless_borrow.rs:48:15 | LL | let _ = x(&&&mut b); | ^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:45:15 + --> $DIR/needless_borrow.rs:49:15 | LL | let _ = x(&&ref_a); | ^^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:48:11 + --> $DIR/needless_borrow.rs:52:11 | LL | x(&b); | ^^ help: change this to: `b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:55:13 + --> $DIR/needless_borrow.rs:59:13 | LL | mut_ref(&mut x); | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:56:13 + --> $DIR/needless_borrow.rs:60:13 | LL | mut_ref(&mut &mut x); | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:57:23 + --> $DIR/needless_borrow.rs:61:23 | LL | let y: &mut i32 = &mut x; | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:58:23 + --> $DIR/needless_borrow.rs:62:23 | LL | let y: &mut i32 = &mut &mut x; | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:67:14 + --> $DIR/needless_borrow.rs:71:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:73:14 + --> $DIR/needless_borrow.rs:77:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:85:13 + --> $DIR/needless_borrow.rs:89:13 | LL | let _ = (&x).0; | ^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:87:22 + --> $DIR/needless_borrow.rs:91:22 | LL | let _ = unsafe { (&*x).0 }; | ^^^^^ help: change this to: `(*x)` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:97:5 + --> $DIR/needless_borrow.rs:101:5 | LL | (&&()).foo(); | ^^^^^^ help: change this to: `(&())` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:106:5 + --> $DIR/needless_borrow.rs:110:5 | LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:131:51 + --> $DIR/needless_borrow.rs:135:51 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:132:44 + --> $DIR/needless_borrow.rs:136:44 | LL | let _ = std::path::Path::new(".").join(&&"."); | ^^^^^ help: change this to: `"."` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:133:23 + --> $DIR/needless_borrow.rs:137:23 | LL | deref_target_is_x(&X); | ^^ help: change this to: `X` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:134:26 + --> $DIR/needless_borrow.rs:138:26 | LL | multiple_constraints(&[[""]]); | ^^^^^^^ help: change this to: `[[""]]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:135:45 + --> $DIR/needless_borrow.rs:139:45 | LL | multiple_constraints_normalizes_to_same(&X, X); | ^^ help: change this to: `X` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:136:32 + --> $DIR/needless_borrow.rs:140:32 | LL | let _ = Some("").unwrap_or(&""); | ^^^ help: change this to: `""` +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:141:33 + | +LL | let _ = std::fs::write("x", &"".to_string()); + | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` + error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:187:13 + --> $DIR/needless_borrow.rs:192:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:196:13 + --> $DIR/needless_borrow.rs:201:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:298:55 + --> $DIR/needless_borrow.rs:286:20 + | +LL | takes_iter(&mut x) + | ^^^^^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:304:55 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` -error: aborting due to 29 previous errors +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:344:37 + | +LL | let _ = std::fs::write("x", &arg); + | ^^^^ help: change this to: `arg` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:345:37 + | +LL | let _ = std::fs::write("x", &loc); + | ^^^^ help: change this to: `loc` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:364:15 + | +LL | debug(&x); + | ^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:374:15 + | +LL | use_x(&x); + | ^^ help: change this to: `x` + +error: aborting due to 35 previous errors diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index f97583aa22f9..fe09aad06bc8 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] #![feature(custom_inner_attributes)] diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index aa5394a56579..3de6d0903c0f 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] #![feature(custom_inner_attributes)] From 9cc8da222b3893bc13bc13c8827e93f8ea246854 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 7 Oct 2022 05:07:09 -0400 Subject: [PATCH 0036/1126] Fix adjacent code --- clippy_dev/src/serve.rs | 2 +- clippy_dev/src/update_lints.rs | 2 +- clippy_lints/src/lib.rs | 8 +++--- clippy_lints/src/nonstandard_macro_braces.rs | 2 +- clippy_utils/src/attrs.rs | 2 +- clippy_utils/src/lib.rs | 2 +- lintcheck/src/main.rs | 30 +++++++++----------- tests/compile-test.rs | 2 +- tests/versioncheck.rs | 2 +- 9 files changed, 24 insertions(+), 28 deletions(-) diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 2e0794f12fa1..535c25e69f1b 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -49,7 +49,7 @@ fn mtime(path: impl AsRef) -> SystemTime { .into_iter() .flatten() .flatten() - .map(|entry| mtime(&entry.path())) + .map(|entry| mtime(entry.path())) .max() .unwrap_or(SystemTime::UNIX_EPOCH) } else { diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 0eb443167ecf..8d1bfacd1dc3 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -128,7 +128,7 @@ fn generate_lint_files( for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { let content = gen_lint_group_list(&lint_group, lints.iter()); process_file( - &format!("clippy_lints/src/lib.register_{lint_group}.rs"), + format!("clippy_lints/src/lib.register_{lint_group}.rs"), update_mode, &content, ); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d9dfb4fc4e0e..ebb0f14fef52 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -417,7 +417,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None @@ -433,7 +433,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { .and_then(|v| parse_msrv(&v, None, None)); let clippy_msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None @@ -444,7 +444,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { if let Some(clippy_msrv) = clippy_msrv { // if both files have an msrv, let's compare them and emit a warning if they differ if clippy_msrv != cargo_msrv { - sess.warn(&format!( + sess.warn(format!( "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" )); } @@ -473,7 +473,7 @@ pub fn read_conf(sess: &Session) -> Conf { let TryConf { conf, errors, warnings } = utils::conf::read(&file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file `{}`: {}", file_name.display(), format_error(error) diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 0ca0befc1351..6c909e5ed73e 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -266,7 +266,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { .iter() .find(|b| b.0 == brace) .map(|(o, c)| ((*o).to_owned(), (*c).to_owned())) - .ok_or_else(|| de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, + .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, }) } } diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index d9b22664fd25..cd8575c90e86 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -136,7 +136,7 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s .emit(); }, ast::AttrStyle::Outer => { - sess.span_err(attr.span, &format!("`{name}` cannot be an outer attribute")); + sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute")); }, } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3597c58e072f..5c8ffffc8c8a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -125,7 +125,7 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt return Some(version); } else if let Some(sess) = sess { if let Some(span) = span { - sess.span_err(span, &format!("`{msrv}` is not a valid Rust version")); + sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); } } None diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index cc2b3e1acec7..95b20d7f0242 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -345,7 +345,7 @@ impl Crate { clippy_args.push(opt); } } else { - clippy_args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) + clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]) } if lint_filter.is_empty() { @@ -457,15 +457,11 @@ fn build_clippy() { /// Read a `lintcheck_crates.toml` file fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = - std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); + std::fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e)); // parse the hashmap of the toml file into a list of crates - let tomlcrates: Vec = crate_list - .crates - .into_iter() - .map(|(_cratename, tomlcrate)| tomlcrate) - .collect(); + let tomlcrates: Vec = crate_list.crates.into_values().collect(); // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => // multiple Cratesources) @@ -602,10 +598,10 @@ fn main() { ) { let shared_target_dir = "target/lintcheck/shared_target_dir"; // if we get an Err here, the shared target dir probably does simply not exist - if let Ok(metadata) = std::fs::metadata(&shared_target_dir) { + if let Ok(metadata) = std::fs::metadata(shared_target_dir) { if metadata.is_dir() { println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir..."); - std::fs::remove_dir_all(&shared_target_dir) + std::fs::remove_dir_all(shared_target_dir) .expect("failed to remove target/lintcheck/shared_target_dir"); } } @@ -779,7 +775,7 @@ fn read_stats_from_file(file_path: &Path) -> HashMap { fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, usize>, lint_filter: &Vec) { let same_in_both_hashmaps = old_stats .iter() - .filter(|(old_key, old_val)| new_stats.get::<&String>(&old_key) == Some(old_val)) + .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val)) .map(|(k, v)| (k.to_string(), *v)) .collect::>(); @@ -797,7 +793,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us // list all new counts (key is in new stats but not in old stats) new_stats_deduped .iter() - .filter(|(new_key, _)| old_stats_deduped.get::(&new_key).is_none()) + .filter(|(new_key, _)| old_stats_deduped.get::(new_key).is_none()) .for_each(|(new_key, new_value)| { println!("{} 0 => {}", new_key, new_value); }); @@ -805,16 +801,16 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us // list all changed counts (key is in both maps but value differs) new_stats_deduped .iter() - .filter(|(new_key, _new_val)| old_stats_deduped.get::(&new_key).is_some()) + .filter(|(new_key, _new_val)| old_stats_deduped.get::(new_key).is_some()) .for_each(|(new_key, new_val)| { - let old_val = old_stats_deduped.get::(&new_key).unwrap(); + let old_val = old_stats_deduped.get::(new_key).unwrap(); println!("{} {} => {}", new_key, old_val, new_val); }); // list all gone counts (key is in old status but not in new stats) old_stats_deduped .iter() - .filter(|(old_key, _)| new_stats_deduped.get::<&String>(&old_key).is_none()) + .filter(|(old_key, _)| new_stats_deduped.get::<&String>(old_key).is_none()) .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key)) .for_each(|(old_key, old_value)| { println!("{} {} => 0", old_key, old_value); @@ -832,12 +828,12 @@ fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { panic!("cannot create lintcheck target dir"); } }); - std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| { + std::fs::create_dir(krate_download_dir).unwrap_or_else(|err| { if err.kind() != ErrorKind::AlreadyExists { panic!("cannot create crate download dir"); } }); - std::fs::create_dir(&extract_dir).unwrap_or_else(|err| { + std::fs::create_dir(extract_dir).unwrap_or_else(|err| { if err.kind() != ErrorKind::AlreadyExists { panic!("cannot create crate extraction dir"); } @@ -863,7 +859,7 @@ fn lintcheck_test() { "lintcheck/test_sources.toml", ]; let status = std::process::Command::new("cargo") - .args(&args) + .args(args) .current_dir("..") // repo root .status(); //.output(); diff --git a/tests/compile-test.rs b/tests/compile-test.rs index fa769222d1af..c10ee969c014 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -283,7 +283,7 @@ fn run_ui_cargo() { env::set_current_dir(&src_path)?; let cargo_toml_path = case.path().join("Cargo.toml"); - let cargo_content = fs::read(&cargo_toml_path)?; + let cargo_content = fs::read(cargo_toml_path)?; let cargo_parsed: toml::Value = toml::from_str( std::str::from_utf8(&cargo_content).expect("`Cargo.toml` is not a valid utf-8 file!"), ) diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 9e07769a8e4f..a6d8d0307ce5 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -48,7 +48,7 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`. let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string()); let rustc_version = String::from_utf8( - std::process::Command::new(&rustc) + std::process::Command::new(rustc) .arg("--version") .output() .expect("failed to run `rustc --version`") From 3b328e704953e34de401166cd76ca062e21d83d8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 9 Sep 2022 15:08:06 -0500 Subject: [PATCH 0037/1126] Introduce TypeErrCtxt TypeErrCtxt optionally has a TypeckResults so that InferCtxt doesn't need to. --- clippy_lints/src/future_not_send.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index eb2eefe0d5a1..406c842a6d05 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{EarlyBinder, Opaque, PredicateKind::Trait}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; -use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use rustc_trait_selection::traits::{self, FulfillmentError}; declare_clippy_lint! { @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { |db| { cx.tcx.infer_ctxt().enter(|infcx| { for FulfillmentError { obligation, .. } in send_errors { - infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); + infcx.err_ctxt().maybe_note_obligation_cause_for_async_await(db, &obligation); if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { db.note(&format!( "`{}` doesn't implement `{}`", From 6819e85501348c6fab3c5d40d0e31d5c7d00bb6a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 19 Sep 2022 22:03:59 -0500 Subject: [PATCH 0038/1126] Change InferCtxtBuilder from enter to build --- clippy_lints/src/dereference.rs | 14 +++--- clippy_lints/src/escape.rs | 5 +- clippy_lints/src/future_not_send.rs | 29 ++++++----- clippy_lints/src/loops/mut_range_bound.rs | 19 ++++---- .../src/methods/unnecessary_to_owned.rs | 4 +- clippy_lints/src/needless_pass_by_value.rs | 6 +-- .../src/operators/assign_op_pattern.rs | 38 +++++++-------- clippy_utils/src/sugg.rs | 7 ++- clippy_utils/src/ty.rs | 48 +++++++++---------- clippy_utils/src/usage.rs | 19 ++++---- 10 files changed, 87 insertions(+), 102 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 3cd8f236e7a5..02a16f765b73 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -831,11 +831,10 @@ fn walk_parents<'tcx>( // Trait methods taking `self` arg_ty } && impl_ty.is_ref() - && cx.tcx.infer_ctxt().enter(|infcx| - infcx - .type_implements_trait(trait_id, impl_ty, subs, cx.param_env) - .must_apply_modulo_regions() - ) + && let infcx = cx.tcx.infer_ctxt().build() + && infcx + .type_implements_trait(trait_id, impl_ty, subs, cx.param_env) + .must_apply_modulo_regions() { return Some(Position::MethodReceiverRefImpl) } @@ -1119,9 +1118,8 @@ fn needless_borrow_impl_arg_position<'tcx>( let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty); let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); - cx.tcx - .infer_ctxt() - .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) + let infcx = cx.tcx.infer_ctxt().build(); + infcx.predicate_must_hold_modulo_regions(&obligation) }) }; diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 2e608fe527fd..eb0455ae404c 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -106,9 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { }; let fn_def_id = cx.tcx.hir().local_def_id(hir_id); - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); for node in v.set { span_lint_hir( diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 406c842a6d05..0519f9ac2468 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -77,10 +77,9 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { if is_future { let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap(); let span = decl.output.span(); - let send_errors = cx.tcx.infer_ctxt().enter(|infcx| { - let cause = traits::ObligationCause::misc(span, hir_id); - traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait) - }); + let infcx = cx.tcx.infer_ctxt().build(); + let cause = traits::ObligationCause::misc(span, hir_id); + let send_errors = traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait); if !send_errors.is_empty() { span_lint_and_then( cx, @@ -88,18 +87,18 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { span, "future cannot be sent between threads safely", |db| { - cx.tcx.infer_ctxt().enter(|infcx| { - for FulfillmentError { obligation, .. } in send_errors { - infcx.err_ctxt().maybe_note_obligation_cause_for_async_await(db, &obligation); - if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { - db.note(&format!( - "`{}` doesn't implement `{}`", - trait_pred.self_ty(), - trait_pred.trait_ref.print_only_trait_path(), - )); - } + for FulfillmentError { obligation, .. } in send_errors { + infcx + .err_ctxt() + .maybe_note_obligation_cause_for_async_await(db, &obligation); + if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { + db.note(&format!( + "`{}` doesn't implement `{}`", + trait_pred.self_ty(), + trait_pred.trait_ref.print_only_trait_path(), + )); } - }); + } }, ); } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 0ee42b61c9a5..db73ab55b37c 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -65,16 +65,15 @@ fn check_for_mutation<'tcx>( span_low: None, span_high: None, }; - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new( - &mut delegate, - &infcx, - body.hir_id.owner.def_id, - cx.param_env, - cx.typeck_results(), - ) - .walk_expr(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new( + &mut delegate, + &infcx, + body.hir_id.owner.def_id, + cx.param_env, + cx.typeck_results(), + ) + .walk_expr(body); delegate.mutation_span() } diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 9ab0d6141146..6017941452c0 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -420,9 +420,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< if trait_predicates.any(|predicate| { let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst); let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); - !cx.tcx - .infer_ctxt() - .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) + !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) }) { return false; } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 178c973981b1..7f881e27dd27 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -138,10 +138,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { .. } = { let mut ctx = MovedVariablesCtxt::default(); - cx.tcx.infer_ctxt().enter(|infcx| { - euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) - .consume_body(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); ctx }; diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 26bca7c306a8..c7e964cf23e2 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -123,16 +123,15 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet } let mut s = S(hir::HirIdSet::default()); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); - v.consume_expr(e); - }); + let infcx = cx.tcx.infer_ctxt().build(); + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); s.0 } @@ -156,15 +155,14 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet } let mut s = S(hir::HirIdSet::default()); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); - v.consume_expr(e); - }); + let infcx = cx.tcx.infer_ctxt().build(); + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); s.0 } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index ef836e84829b..3c5dd92b9cd6 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -821,10 +821,9 @@ pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<' }; let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id); - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) - .consume_body(closure_body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) + .consume_body(closure_body); if !visitor.suggestion_start.is_empty() { return Some(DerefClosure { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 934470bd135b..a15daec7c3ce 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -172,11 +172,10 @@ pub fn implements_trait_with_env<'tcx>( return false; } let ty_params = tcx.mk_substs(ty_params.iter()); - tcx.infer_ctxt().enter(|infcx| { - infcx - .type_implements_trait(trait_id, ty, ty_params, param_env) - .must_apply_modulo_regions() - }) + let infcx = tcx.infer_ctxt().build(); + infcx + .type_implements_trait(trait_id, ty, ty_params, param_env) + .must_apply_modulo_regions() } /// Checks whether this type implements `Drop`. @@ -242,27 +241,26 @@ fn is_normalizable_helper<'tcx>( } // prevent recursive loops, false-negative is better than endless loop leading to stack overflow cache.insert(ty, false); - let result = cx.tcx.infer_ctxt().enter(|infcx| { - let cause = rustc_middle::traits::ObligationCause::dummy(); - if infcx.at(&cause, param_env).normalize(ty).is_ok() { - match ty.kind() { - ty::Adt(def, substs) => def.variants().iter().all(|variant| { - variant - .fields - .iter() - .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) - }), - _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { - GenericArgKind::Type(inner_ty) if inner_ty != ty => { - is_normalizable_helper(cx, param_env, inner_ty, cache) - }, - _ => true, // if inner_ty == ty, we've already checked it - }), - } - } else { - false + let infcx = cx.tcx.infer_ctxt().build(); + let cause = rustc_middle::traits::ObligationCause::dummy(); + let result = if infcx.at(&cause, param_env).normalize(ty).is_ok() { + match ty.kind() { + ty::Adt(def, substs) => def.variants().iter().all(|variant| { + variant + .fields + .iter() + .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) + }), + _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { + GenericArgKind::Type(inner_ty) if inner_ty != ty => { + is_normalizable_helper(cx, param_env, inner_ty, cache) + }, + _ => true, // if inner_ty == ty, we've already checked it + }), } - }); + } else { + false + }; cache.insert(ty, result); result } diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index b5ec3fef3e0b..e32bae6ed1fd 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -18,16 +18,15 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> used_mutably: HirIdSet::default(), skip: false, }; - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new( - &mut delegate, - &infcx, - expr.hir_id.owner.def_id, - cx.param_env, - cx.typeck_results(), - ) - .walk_expr(expr); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new( + &mut delegate, + &infcx, + expr.hir_id.owner.def_id, + cx.param_env, + cx.typeck_results(), + ) + .walk_expr(expr); if delegate.skip { return None; From 33bdddb75c4daf708da24c363c1ea6375bf9c9ae Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 7 Oct 2022 11:41:56 +0200 Subject: [PATCH 0039/1126] remove FFI support for macOS --- src/tools/miri/Cargo.toml | 4 +++- src/tools/miri/src/machine.rs | 11 ++++++++--- src/tools/miri/src/shims/ffi_support.rs | 2 -- src/tools/miri/src/shims/foreign_items.rs | 5 ++--- src/tools/miri/src/shims/mod.rs | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 0c547d585d19..b63186f98a70 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -31,8 +31,10 @@ smallvec = "1.7" rustc-workspace-hack = "1.0.0" measureme = "10.0.0" -[target."cfg(unix)".dependencies] +[target.'cfg(unix)'.dependencies] libc = "0.2" + +[target.'cfg(target_os = "linux")'.dependencies] libffi = "3.0.0" libloading = "0.7" diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 20ae908fce87..fc9a1170d294 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -421,8 +421,10 @@ pub struct MiriMachine<'mir, 'tcx> { pub(crate) basic_block_count: u64, /// Handle of the optional shared object file for external functions. - #[cfg(unix)] + #[cfg(target_os = "linux")] pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>, + #[cfg(not(target_os = "linux"))] + pub external_so_lib: Option, /// Run a garbage collector for SbTags every N basic blocks. pub(crate) gc_interval: u32, @@ -485,7 +487,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { report_progress: config.report_progress, basic_block_count: 0, clock: Clock::new(config.isolated_op == IsolatedOp::Allow), - #[cfg(unix)] + #[cfg(target_os = "linux")] external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| { let target_triple = layout_cx.tcx.sess.opts.target_triple.triple(); // Check if host target == the session target. @@ -507,6 +509,10 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { lib_file_path.clone(), ) }), + #[cfg(not(target_os = "linux"))] + external_so_lib: config.external_so_file.as_ref().map(|_| { + panic!("loading external .so files is only supported on Linux") + }), gc_interval: config.gc_interval, since_gc: 0, num_cpus: config.num_cpus, @@ -648,7 +654,6 @@ impl VisitTags for MiriMachine<'_, '_> { preemption_rate: _, report_progress: _, basic_block_count: _, - #[cfg(unix)] external_so_lib: _, gc_interval: _, since_gc: _, diff --git a/src/tools/miri/src/shims/ffi_support.rs b/src/tools/miri/src/shims/ffi_support.rs index 0813554e9d24..c5db868cdc7c 100644 --- a/src/tools/miri/src/shims/ffi_support.rs +++ b/src/tools/miri/src/shims/ffi_support.rs @@ -183,9 +183,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 // using the `libc` crate where this interface is public. // No `libc::dladdr` on windows. - #[cfg(unix)] let mut info = std::mem::MaybeUninit::::uninit(); - #[cfg(unix)] unsafe { if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index bb62a2a7ec1b..26184fdc3c09 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -23,8 +23,6 @@ use rustc_target::{ use super::backtrace::EvalContextExt as _; use crate::helpers::{convert::Truncate, target_os_is_unix}; -#[cfg(unix)] -use crate::shims::ffi_support::EvalContextExt as _; use crate::*; /// Returned by `emulate_foreign_item_by_name`. @@ -372,8 +370,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); // First deal with any external C functions in linked .so file. - #[cfg(unix)] + #[cfg(target_os = "linux")] if this.machine.external_so_lib.as_ref().is_some() { + use crate::shims::ffi_support::EvalContextExt as _; // An Ok(false) here means that the function being called was not exported // by the specified `.so` file; we should continue and check if it corresponds to // a provided shim. diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index 8cb648e51732..dcb99a276682 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -1,7 +1,7 @@ #![warn(clippy::integer_arithmetic)] mod backtrace; -#[cfg(unix)] +#[cfg(target_os = "linux")] pub mod ffi_support; pub mod foreign_items; pub mod intrinsics; From c2adc2e2343ebed2dba1490b842719d4541bbf48 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 7 Oct 2022 17:29:08 +0200 Subject: [PATCH 0040/1126] rustup --- src/tools/miri/rust-version | 2 +- src/tools/miri/tests/fail/invalid_bool.rs | 1 - src/tools/miri/tests/pass/issues/issue-miri-2433.rs | 4 +++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 78b9a110aa7c..7729a184d327 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -acb8934fd57b3c2740c4abac0a5728c2c9b1423b +e42c4d7218b2596276152c5eb1e69335621f3086 diff --git a/src/tools/miri/tests/fail/invalid_bool.rs b/src/tools/miri/tests/fail/invalid_bool.rs index dde414f41774..fe9bb3bed7f0 100644 --- a/src/tools/miri/tests/fail/invalid_bool.rs +++ b/src/tools/miri/tests/fail/invalid_bool.rs @@ -2,7 +2,6 @@ // Make sure we find these even with many checks disabled. //@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation - fn main() { let b = unsafe { std::mem::transmute::(2) }; let _x = b == std::hint::black_box(true); //~ ERROR: interpreting an invalid 8-bit value as a bool diff --git a/src/tools/miri/tests/pass/issues/issue-miri-2433.rs b/src/tools/miri/tests/pass/issues/issue-miri-2433.rs index de719df0f1f3..a8281d30bac4 100644 --- a/src/tools/miri/tests/pass/issues/issue-miri-2433.rs +++ b/src/tools/miri/tests/pass/issues/issue-miri-2433.rs @@ -1,6 +1,8 @@ #![feature(type_alias_impl_trait)] -trait T { type Item; } +trait T { + type Item; +} type Alias<'a> = impl T; From 58b13b7b882da8785fbadada06a2c5dc714cf6e8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 7 Oct 2022 17:53:18 +0200 Subject: [PATCH 0041/1126] clippy learned some new tricks --- src/tools/miri/src/lib.rs | 1 + src/tools/miri/src/shims/unix/android/dlsym.rs | 2 +- src/tools/miri/src/shims/unix/fs.rs | 6 +++--- src/tools/miri/src/stacked_borrows/mod.rs | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 461f6e4c0f6a..8e2222c39a24 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -28,6 +28,7 @@ clippy::type_complexity, clippy::single_element_loop, clippy::needless_return, + clippy::bool_to_int_with_if, // We are not implementing queries here so it's fine rustc::potential_query_instability )] diff --git a/src/tools/miri/src/shims/unix/android/dlsym.rs b/src/tools/miri/src/shims/unix/android/dlsym.rs index 4cb78d4dabdc..b0c9d729c9d9 100644 --- a/src/tools/miri/src/shims/unix/android/dlsym.rs +++ b/src/tools/miri/src/shims/unix/android/dlsym.rs @@ -42,7 +42,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ); } - let &[ref _sig, ref _func] = check_arg_count(args)?; + let [_sig, _func] = check_arg_count(args)?; this.write_null(dest)?; } } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 9713cd9265e5..ed68976773d1 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1073,7 +1073,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { mask |= this.eval_libc("STATX_ATIME")?.to_u32()?; InterpResult::Ok(tup) }) - .unwrap_or(Ok((0, 0)))?; + .unwrap_or_else(|| Ok((0, 0)))?; let (created_sec, created_nsec) = metadata .created @@ -1081,7 +1081,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { mask |= this.eval_libc("STATX_BTIME")?.to_u32()?; InterpResult::Ok(tup) }) - .unwrap_or(Ok((0, 0)))?; + .unwrap_or_else(|| Ok((0, 0)))?; let (modified_sec, modified_nsec) = metadata .modified @@ -1089,7 +1089,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { mask |= this.eval_libc("STATX_MTIME")?.to_u32()?; InterpResult::Ok(tup) }) - .unwrap_or(Ok((0, 0)))?; + .unwrap_or_else(|| Ok((0, 0)))?; // Now we write everything to `statxbuf`. We write a zero for the unavailable fields. this.write_int_fields_named( diff --git a/src/tools/miri/src/stacked_borrows/mod.rs b/src/tools/miri/src/stacked_borrows/mod.rs index 2888f8e81fb5..09d36ca9dfdb 100644 --- a/src/tools/miri/src/stacked_borrows/mod.rs +++ b/src/tools/miri/src/stacked_borrows/mod.rs @@ -45,6 +45,7 @@ impl SbTag { } // The default to be used when SB is disabled + #[allow(clippy::should_implement_trait)] pub fn default() -> Self { Self::new(1).unwrap() } From e91746ed8271850de512fb765a21aa5ddb18a25c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Sep 2022 13:05:20 +0200 Subject: [PATCH 0042/1126] make const_err a hard error --- clippy_lints/src/indexing_slicing.rs | 1 - tests/ui/crashes/ice-9463.rs | 2 +- tests/ui/crashes/ice-9463.stderr | 2 +- tests/ui/indexing_slicing_index.rs | 2 +- tests/ui/indexing_slicing_index.stderr | 8 +++++++- tests/ui/out_of_bounds_indexing/issue-3102.rs | 2 +- tests/ui/out_of_bounds_indexing/simple.rs | 2 +- 7 files changed, 12 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 4a375752e1d3..af40a5a8187e 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -19,7 +19,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,no_run - /// # #![allow(const_err)] /// let x = [1, 2, 3, 4]; /// /// x[9]; diff --git a/tests/ui/crashes/ice-9463.rs b/tests/ui/crashes/ice-9463.rs index 41ef930d3233..9564e77c24b1 100644 --- a/tests/ui/crashes/ice-9463.rs +++ b/tests/ui/crashes/ice-9463.rs @@ -1,4 +1,4 @@ -#![deny(arithmetic_overflow, const_err)] +#![deny(arithmetic_overflow)] fn main() { let _x = -1_i32 >> -1; let _y = 1u32 >> 10000000000000u32; diff --git a/tests/ui/crashes/ice-9463.stderr b/tests/ui/crashes/ice-9463.stderr index b0ce306d6838..2b425e85a27b 100644 --- a/tests/ui/crashes/ice-9463.stderr +++ b/tests/ui/crashes/ice-9463.stderr @@ -7,7 +7,7 @@ LL | let _x = -1_i32 >> -1; note: the lint level is defined here --> $DIR/ice-9463.rs:1:9 | -LL | #![deny(arithmetic_overflow, const_err)] +LL | #![deny(arithmetic_overflow)] | ^^^^^^^^^^^^^^^^^^^ error: this arithmetic operation will overflow diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index 7ebf6ee993cb..4476e0eb9220 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -3,7 +3,7 @@ // 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(const_err, unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] const ARR: [i32; 2] = [1, 2]; const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr. diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index a8d8b38163d0..da5bc38b3b66 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -59,6 +59,12 @@ LL | v[M]; | = help: consider using `.get(n)` or `.get_mut(n)` instead -error: aborting due to 8 previous errors +error[E0080]: evaluation of constant value failed + --> $DIR/indexing_slicing_index.rs:10:24 + | +LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/out_of_bounds_indexing/issue-3102.rs b/tests/ui/out_of_bounds_indexing/issue-3102.rs index f20a0ede1137..edd2123d48a5 100644 --- a/tests/ui/out_of_bounds_indexing/issue-3102.rs +++ b/tests/ui/out_of_bounds_indexing/issue-3102.rs @@ -1,5 +1,5 @@ #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, const_err)] +#![allow(clippy::no_effect)] fn main() { let x = [1, 2, 3, 4]; diff --git a/tests/ui/out_of_bounds_indexing/simple.rs b/tests/ui/out_of_bounds_indexing/simple.rs index 590e578d758e..4c541c23f5f4 100644 --- a/tests/ui/out_of_bounds_indexing/simple.rs +++ b/tests/ui/out_of_bounds_indexing/simple.rs @@ -1,5 +1,5 @@ #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, const_err)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] fn main() { let x = [1, 2, 3, 4]; From 5243ae96fe6e82a6a03041d798fae59b5c0c3886 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 8 Oct 2022 09:04:54 +0200 Subject: [PATCH 0043/1126] bless cargo-miri output I think cargo has a bug here: https://github.com/rust-lang/cargo/issues/11191 but for now we bless its output so that we can keep CI green --- src/tools/miri/README.md | 9 ++++----- src/tools/miri/test-cargo-miri/run-test.py | 5 ++++- .../test.filter.cross-target.stdout.ref | 5 +++++ src/tools/miri/test-cargo-miri/test.filter.stdout.ref | 10 +++++----- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index d75f0cc1e80d..7ad51520a428 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -435,11 +435,10 @@ Moreover, Miri recognizes some environment variables: purpose. * `MIRI_NO_STD` (recognized by `cargo miri` and the test suite) makes sure that the target's sysroot is built without libstd. This allows testing and running no_std programs. -* `MIRI_BLESS` (recognized by the test suite) overwrite all `stderr` and `stdout` files - instead of checking whether the output matches. -* `MIRI_SKIP_UI_CHECKS` (recognized by the test suite) don't check whether the - `stderr` or `stdout` files match the actual output. Useful for the rustc test suite - which has subtle differences that we don't care about. +* `MIRI_BLESS` (recognized by the test suite and `cargo-miri-test/run-test.py`): overwrite all + `stderr` and `stdout` files instead of checking whether the output matches. +* `MIRI_SKIP_UI_CHECKS` (recognized by the test suite): don't check whether the + `stderr` or `stdout` files match the actual output. The following environment variables are *internal* and must not be used by anyone but Miri itself. They are used to communicate between different Miri diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 4485d3252ccc..c611b9c44be9 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -33,10 +33,13 @@ def normalize_stderr(str): return str def check_output(actual, path, name): + if 'MIRI_BLESS' in os.environ: + open(path, mode='w').write(actual) + return True expected = open(path).read() if expected == actual: return True - print(f"{path} did not match reference!") + print(f"{name} output did not match reference in {path}!") print(f"--- BEGIN diff {name} ---") for text in difflib.unified_diff(expected.split("\n"), actual.split("\n")): print(text) diff --git a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref index bb0282d6c916..39e1857060d0 100644 --- a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref @@ -1,4 +1,9 @@ +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out + + running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out diff --git a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref index c618956656a8..39e1857060d0 100644 --- a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref @@ -1,4 +1,9 @@ +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out + + running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out @@ -10,8 +15,3 @@ test simple ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out - -running 0 tests - -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in $TIME - From 5f6e1d397a940447af5e4279718668db473c683c Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 24 Sep 2022 17:22:04 +0200 Subject: [PATCH 0044/1126] Stabilize half_open_range_patterns --- tests/ui/match_overlapping_arm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 2f85e6357135..22b04b208f87 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -1,5 +1,5 @@ #![feature(exclusive_range_pattern)] -#![feature(half_open_range_patterns)] + #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let)] From 6f4546a4be7b65da15b3170d72d3339ed1d2aeec Mon Sep 17 00:00:00 2001 From: kraktus Date: Sat, 8 Oct 2022 16:15:18 +0200 Subject: [PATCH 0045/1126] [`unnecessary_cast`] Do not lint negative hexadecimal literals when cast as float Floats cannot be expressed as hexadecimal literals --- clippy_lints/src/casts/unnecessary_cast.rs | 3 --- clippy_utils/src/numeric_literal.rs | 7 ++++--- tests/ui/unnecessary_cast.fixed | 4 ++++ tests/ui/unnecessary_cast.rs | 4 ++++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 21ed7f4844cc..c8596987e4d7 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -59,9 +59,6 @@ pub(super) fn check<'tcx>( lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); return false; }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => { - return false; - }, LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 80098d9766c6..c5dcd7b31f58 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -69,12 +69,13 @@ impl<'a> NumericLiteral<'a> { #[must_use] pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self { + let unsigned_lit = lit.trim_start_matches('-'); // Determine delimiter for radix prefix, if present, and radix. - let radix = if lit.starts_with("0x") { + let radix = if unsigned_lit.starts_with("0x") { Radix::Hexadecimal - } else if lit.starts_with("0b") { + } else if unsigned_lit.starts_with("0b") { Radix::Binary - } else if lit.starts_with("0o") { + } else if unsigned_lit.starts_with("0o") { Radix::Octal } else { Radix::Decimal diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 94dc96427263..ec8c6abfab91 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -111,4 +111,8 @@ mod fixable { let _num = foo(); } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index e5150256f69a..5213cdc269bd 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -111,4 +111,8 @@ mod fixable { let _num = foo() as f32; } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } From 39a7d000b640523772ea709bc85d2c9460cf07bf Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 8 Oct 2022 11:23:05 -0400 Subject: [PATCH 0046/1126] Don't suggest moving tuple structs with a significant drop to late evaluation. --- clippy_utils/src/eager_or_lazy.rs | 2 +- tests/ui/or_fun_call.fixed | 11 +++++++++++ tests/ui/or_fun_call.rs | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 8724a4cd651d..95b3e651e2b5 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -120,7 +120,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .expr_ty(e) .has_significant_drop(self.cx.tcx, self.cx.param_env) { - self.eagerness = Lazy; + self.eagerness = ForceNoChange; return; } }, diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 896430780ea8..23b1aa8bebd5 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -225,4 +225,15 @@ mod issue8239 { } } +mod issue9608 { + fn sig_drop() { + enum X { + X(std::fs::File), + Y(u32), + } + + let _ = None.unwrap_or(X::Y(0)); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 2473163d4fd2..039998f22dd7 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -225,4 +225,15 @@ mod issue8239 { } } +mod issue9608 { + fn sig_drop() { + enum X { + X(std::fs::File), + Y(u32), + } + + let _ = None.unwrap_or(X::Y(0)); + } +} + fn main() {} From 8e76d6687e48d9cfaa28553278540682cca6602c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 9 Oct 2022 07:09:57 +0000 Subject: [PATCH 0047/1126] ImplItemKind::TyAlias => ImplItemKind::Type --- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/types/mod.rs | 2 +- clippy_utils/src/check_proc_macro.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 9d5764ac0926..ef6d1da552bf 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -148,7 +148,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let desc = match impl_item.kind { hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(_) => return, + hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(_) => return, }; let assoc_item = cx.tcx.associated_item(impl_item.def_id); diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index aca55817c525..33eee2a03784 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -372,7 +372,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { // Methods are covered by check_fn. // Type aliases are ignored because oftentimes it's impossible to // make type alias declaration in trait simpler, see #1013 - ImplItemKind::Fn(..) | ImplItemKind::TyAlias(..) => (), + ImplItemKind::Fn(..) | ImplItemKind::Type(..) => (), } } diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 7a8d4e8068ed..c6bf98b7b8bb 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -220,7 +220,7 @@ fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) { fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) { let (start_pat, end_pat) = match &item.kind { ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")), - ImplItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")), + ImplItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")), ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")), }; if item.vis_span.is_empty() { From 000e94692b59f2d0ff2eb38b0227bc51436889fd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 8 Oct 2022 16:26:49 +0200 Subject: [PATCH 0048/1126] add josh instructions --- src/tools/miri/CONTRIBUTING.md | 53 ++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index a88e69115ba3..c9a7d708f1e0 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -104,7 +104,7 @@ MIRI_LOG=rustc_mir::interpret=info,miri::stacked_borrows ./miri run tests/pass/v In addition, you can set `MIRI_BACKTRACE=1` to get a backtrace of where an evaluation error was originally raised. -#### UI testing +### UI testing We use ui-testing in Miri, meaning we generate `.stderr` and `.stdout` files for the output produced by Miri. You can use `./miri bless` to automatically (re)generate these files when @@ -257,7 +257,7 @@ Note: When you are working with a locally built rustc or any other toolchain tha is not the same as the one in `rust-version`, you should not have `.auto-everything` or `.auto-toolchain` as that will keep resetting your toolchain. -``` +```sh rm -f .auto-everything .auto-toolchain ``` @@ -275,3 +275,52 @@ see . With this, you should now have a working development setup! See [above](#building-and-testing-miri) for how to proceed working on Miri. + +## Advanced topic: Syncing with the rustc repo + +We use the [`josh` proxy](https://github.com/josh-project/josh) to transmit +changes between the rustc and Miri repositories. For now, josh needs to be built +from source. This downloads and runs josh: + +```sh +git clone https://github.com/josh-project/josh +cd josh +git checkout @changes/master/christian.schilling.de@gmail.com/start-filter +cargo run --release -p josh-proxy -- --local=$(pwd)/local --remote=https://github.com --no-background +``` + +### Importing changes from the rustc repo + +We assume we start on an up-to-date master branch in the Miri repo. + +```sh +# Fetch rustc side of the history. Takes ca 5 min the first time. +# Do NOT change that commit ID, it needs to be exactly this! +git fetch http://localhost:8000/rust-lang/rust.git:at_commit=75dd959a3a40eb5b4574f8d2e23aa6efbeb33573[:prefix=src/tools/miri]:/src/tools/miri.git master +# Include that history into ours. +git merge FETCH_HEAD -m "merge rustc history" +# Update toolchain reference and apply formatting. +./rustup-toolchain HEAD && ./miri fmt +git commit -am "rustup" +``` + +Now push this to a new branch in your Miri fork, and create a PR. It is worth +running `./miri test` locally in parallel, since the test suite in the Miri repo +is stricter than the one on the rustc side, so some small tweaks might be +needed. + +### Exporting changes to the rustc repo + +We will use the josh proxy to push to your fork of rustc. You need to make sure +that the master branch of your fork is up-to-date. Also make sure that there +exists no branch called `miri` in your fork. Then run the following in the Miri +repo, assuming we are on an up-to-date master branch: + +```sh +# Push the Miri changes to your rustc fork (substitute your github handle for YOUR_NAME). +# Do NOT change that commit ID, it needs to be exactly this! +git push http://localhost:8000/YOUR_NAME/rust.git:at_commit=75dd959a3a40eb5b4574f8d2e23aa6efbeb33573[:prefix=src/tools/miri]:/src/tools/miri.git -o base=master HEAD:miri +``` + +This will create a new branch in your fork, and the output should include a link +to create a rustc PR that will integrate those changes into the main repository. From 8b1caaae5db69e098854ebd51bb9fe93310328d5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 9 Oct 2022 12:42:28 +0200 Subject: [PATCH 0049/1126] rustup --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 7729a184d327..028fcd55e9cf 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -e42c4d7218b2596276152c5eb1e69335621f3086 +f382c2748aec2ada91eff88840c996644ff0f70d From eb29c9e1303b3ce29bcaf908ccacfb7d9b6c5602 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 9 Oct 2022 13:13:17 +0200 Subject: [PATCH 0050/1126] cargo got fixed, re-bless its tests --- .../test.filter.cross-target.stdout.ref | 5 ----- src/tools/miri/test-cargo-miri/test.filter.stdout.ref | 10 +++++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref index 39e1857060d0..bb0282d6c916 100644 --- a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref @@ -1,9 +1,4 @@ -running 0 tests - -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out - - running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out diff --git a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref index 39e1857060d0..c618956656a8 100644 --- a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref @@ -1,9 +1,4 @@ -running 0 tests - -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out - - running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out @@ -15,3 +10,8 @@ test simple ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in $TIME + From ccbc63be5ef43a508f16bdbfcdbc71c15114ca8e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 9 Oct 2022 13:40:49 +0200 Subject: [PATCH 0051/1126] 1/1000 events do happen sometimes... --- src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.rs | 2 +- .../miri/tests/fail/unaligned_pointers/reference_to_packed.rs | 2 +- src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr3.rs | 2 +- src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr4.rs | 2 +- .../miri/tests/fail/unaligned_pointers/unaligned_ptr_zst.rs | 2 +- src/tools/miri/tests/pass/align.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.rs b/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.rs index b943c7db7ccd..ca8590cc6b3f 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.rs +++ b/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.rs @@ -7,7 +7,7 @@ struct MuchAlign; fn main() { // Try many times as this might work by chance. - for _ in 0..10 { + for _ in 0..20 { let buf = [0u32; 256]; // `buf` is sufficiently aligned for `layout.align` on a `dyn Debug`, but not // for the actual alignment required by `MuchAlign`. diff --git a/src/tools/miri/tests/fail/unaligned_pointers/reference_to_packed.rs b/src/tools/miri/tests/fail/unaligned_pointers/reference_to_packed.rs index 752210dca46e..a807200771d6 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/reference_to_packed.rs +++ b/src/tools/miri/tests/fail/unaligned_pointers/reference_to_packed.rs @@ -11,7 +11,7 @@ struct Foo { fn main() { // Try many times as this might work by chance. - for _ in 0..10 { + for _ in 0..20 { let foo = Foo { x: 42, y: 99 }; let p = &foo.x; let i = *p; //~ERROR: alignment 4 is required diff --git a/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr3.rs b/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr3.rs index 7605dd175a98..3aa8cb492a13 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr3.rs +++ b/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr3.rs @@ -3,7 +3,7 @@ fn main() { // Try many times as this might work by chance. - for _ in 0..10 { + for _ in 0..20 { let x = [2u16, 3, 4, 5]; // Make it big enough so we don't get an out-of-bounds error. let x = &x[0] as *const _ as *const *const u8; // cast to ptr-to-ptr, so that we load a ptr // This must fail because alignment is violated. Test specifically for loading pointers, diff --git a/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr4.rs b/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr4.rs index 852febe4c04c..606316120d6e 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr4.rs +++ b/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr4.rs @@ -6,7 +6,7 @@ fn main() { // (This would be missed if u8 allocations are *always* at odd addresses.) // // Try many times as this might work by chance. - for _ in 0..10 { + for _ in 0..20 { let x = [0u8; 4]; let ptr = x.as_ptr().wrapping_offset(1).cast::(); let _val = unsafe { *ptr }; //~ERROR: but alignment diff --git a/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr_zst.rs b/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr_zst.rs index 9076581b55bb..eff423759560 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr_zst.rs +++ b/src/tools/miri/tests/fail/unaligned_pointers/unaligned_ptr_zst.rs @@ -4,7 +4,7 @@ fn main() { // Try many times as this might work by chance. - for i in 0..10 { + for i in 0..20 { let x = i as u8; let x = &x as *const _ as *const [u32; 0]; // This must fail because alignment is violated. Test specifically for loading ZST. diff --git a/src/tools/miri/tests/pass/align.rs b/src/tools/miri/tests/pass/align.rs index 997abd733922..2b6e83891d6e 100644 --- a/src/tools/miri/tests/pass/align.rs +++ b/src/tools/miri/tests/pass/align.rs @@ -22,7 +22,7 @@ fn align_to() { fn main() { // Do this a couple times in a loop because it may work "by chance". - for _ in 0..10 { + for _ in 0..20 { manual_alignment(); align_to(); } From 3fc903eb9578287f8e619b597e98b26e7a27b000 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 25 Aug 2022 14:03:13 +0400 Subject: [PATCH 0052/1126] Fix clippy tests that trigger `for_loop_over_fallibles` lint --- tests/ui/for_loop_unfixable.rs | 1 + tests/ui/for_loop_unfixable.stderr | 2 +- tests/ui/for_loops_over_fallibles.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ui/for_loop_unfixable.rs b/tests/ui/for_loop_unfixable.rs index efcaffce24ea..203656fa4d6c 100644 --- a/tests/ui/for_loop_unfixable.rs +++ b/tests/ui/for_loop_unfixable.rs @@ -8,6 +8,7 @@ clippy::for_kv_map )] #[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] +#[allow(for_loop_over_fallibles)] fn main() { let vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loop_unfixable.stderr b/tests/ui/for_loop_unfixable.stderr index f769b4bdc941..50a86eaa68f7 100644 --- a/tests/ui/for_loop_unfixable.stderr +++ b/tests/ui/for_loop_unfixable.stderr @@ -1,5 +1,5 @@ error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loop_unfixable.rs:14:15 + --> $DIR/for_loop_unfixable.rs:15:15 | LL | for _v in vec.iter().next() {} | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index 4b2a9297d084..54661ff94f24 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -1,5 +1,6 @@ #![warn(clippy::for_loops_over_fallibles)] #![allow(clippy::uninlined_format_args)] +#![allow(for_loop_over_fallibles)] fn for_loops_over_fallibles() { let option = Some(1); From 05dcfd971a2736cf0eac04bb04f6539f36d05a44 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 15:59:39 +0000 Subject: [PATCH 0053/1126] fixup lint name --- tests/ui/for_loop_unfixable.rs | 2 +- tests/ui/for_loops_over_fallibles.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/for_loop_unfixable.rs b/tests/ui/for_loop_unfixable.rs index 203656fa4d6c..55fb3788a8b1 100644 --- a/tests/ui/for_loop_unfixable.rs +++ b/tests/ui/for_loop_unfixable.rs @@ -8,7 +8,7 @@ clippy::for_kv_map )] #[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] -#[allow(for_loop_over_fallibles)] +#[allow(for_loops_over_fallibles)] fn main() { let vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index 54661ff94f24..75cdcc02353f 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -1,6 +1,6 @@ #![warn(clippy::for_loops_over_fallibles)] #![allow(clippy::uninlined_format_args)] -#![allow(for_loop_over_fallibles)] +#![allow(for_loops_over_fallibles)] fn for_loops_over_fallibles() { let option = Some(1); From 7cfc6fa1f0c847c749929925f9def0fdc690d417 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 17:08:29 +0000 Subject: [PATCH 0054/1126] deprecate `clippy::for_loops_over_fallibles` --- clippy_lints/src/lib.register_all.rs | 1 - clippy_lints/src/lib.register_lints.rs | 1 - clippy_lints/src/lib.register_suspicious.rs | 1 - .../src/loops/for_loops_over_fallibles.rs | 65 ------------- clippy_lints/src/loops/iter_next_loop.rs | 5 +- clippy_lints/src/loops/mod.rs | 57 +---------- clippy_lints/src/renamed_lints.rs | 5 +- src/docs.rs | 1 - src/docs/for_loops_over_fallibles.txt | 32 ------- tests/ui/for_loops_over_fallibles.rs | 74 --------------- tests/ui/for_loops_over_fallibles.stderr | 95 ------------------- tests/ui/manual_map_option.fixed | 2 +- tests/ui/manual_map_option.rs | 2 +- tests/ui/rename.fixed | 7 +- tests/ui/rename.rs | 3 +- tests/ui/rename.stderr | 34 ++++--- 16 files changed, 34 insertions(+), 351 deletions(-) delete mode 100644 clippy_lints/src/loops/for_loops_over_fallibles.rs delete mode 100644 src/docs/for_loops_over_fallibles.txt delete mode 100644 tests/ui/for_loops_over_fallibles.rs delete mode 100644 tests/ui/for_loops_over_fallibles.stderr diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 5d26e4b33601..fe1f0b56646c 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -109,7 +109,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(loops::EMPTY_LOOP), LintId::of(loops::EXPLICIT_COUNTER_LOOP), LintId::of(loops::FOR_KV_MAP), - LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::MANUAL_FIND), LintId::of(loops::MANUAL_FLATTEN), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 05d927dbea79..306cb6a61c94 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -227,7 +227,6 @@ store.register_lints(&[ loops::EXPLICIT_INTO_ITER_LOOP, loops::EXPLICIT_ITER_LOOP, loops::FOR_KV_MAP, - loops::FOR_LOOPS_OVER_FALLIBLES, loops::ITER_NEXT_LOOP, loops::MANUAL_FIND, loops::MANUAL_FLATTEN, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 6125d0f7a862..d6d95c95c85d 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -21,7 +21,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(loops::EMPTY_LOOP), - LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::MUT_RANGE_BOUND), LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::SUSPICIOUS_MAP), diff --git a/clippy_lints/src/loops/for_loops_over_fallibles.rs b/clippy_lints/src/loops/for_loops_over_fallibles.rs deleted file mode 100644 index 77de90fd7b94..000000000000 --- a/clippy_lints/src/loops/for_loops_over_fallibles.rs +++ /dev/null @@ -1,65 +0,0 @@ -use super::FOR_LOOPS_OVER_FALLIBLES; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use rustc_hir::{Expr, Pat}; -use rustc_lint::LateContext; -use rustc_span::symbol::sym; - -/// Checks for `for` loops over `Option`s and `Result`s. -pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, method_name: Option<&str>) { - let ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, ty, sym::Option) { - let help_string = if let Some(method_name) = method_name { - format!( - "consider replacing `for {0} in {1}.{method_name}()` with `if let Some({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - } else { - format!( - "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - }; - span_lint_and_help( - cx, - FOR_LOOPS_OVER_FALLIBLES, - arg.span, - &format!( - "for loop over `{0}`, which is an `Option`. This is more readably written as an \ - `if let` statement", - snippet(cx, arg.span, "_") - ), - None, - &help_string, - ); - } else if is_type_diagnostic_item(cx, ty, sym::Result) { - let help_string = if let Some(method_name) = method_name { - format!( - "consider replacing `for {0} in {1}.{method_name}()` with `if let Ok({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - } else { - format!( - "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - }; - span_lint_and_help( - cx, - FOR_LOOPS_OVER_FALLIBLES, - arg.span, - &format!( - "for loop over `{0}`, which is a `Result`. This is more readably written as an \ - `if let` statement", - snippet(cx, arg.span, "_") - ), - None, - &help_string, - ); - } -} diff --git a/clippy_lints/src/loops/iter_next_loop.rs b/clippy_lints/src/loops/iter_next_loop.rs index e640c62ebdac..b8a263817d29 100644 --- a/clippy_lints/src/loops/iter_next_loop.rs +++ b/clippy_lints/src/loops/iter_next_loop.rs @@ -5,7 +5,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) { if is_trait_method(cx, arg, sym::Iterator) { span_lint( cx, @@ -14,8 +14,5 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { "you are iterating over `Iterator::next()` which is an Option; this will compile but is \ probably not what you want", ); - true - } else { - false } } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index c0a0444485e3..bcf278d9c833 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -3,7 +3,6 @@ mod explicit_counter_loop; mod explicit_into_iter_loop; mod explicit_iter_loop; mod for_kv_map; -mod for_loops_over_fallibles; mod iter_next_loop; mod manual_find; mod manual_flatten; @@ -173,49 +172,6 @@ declare_clippy_lint! { "for-looping over `_.next()` which is probably not intended" } -declare_clippy_lint! { - /// ### What it does - /// Checks for `for` loops over `Option` or `Result` values. - /// - /// ### Why is this bad? - /// Readability. This is more clearly expressed as an `if - /// let`. - /// - /// ### Example - /// ```rust - /// # let opt = Some(1); - /// # let res: Result = Ok(1); - /// for x in opt { - /// // .. - /// } - /// - /// for x in &res { - /// // .. - /// } - /// - /// for x in res.iter() { - /// // .. - /// } - /// ``` - /// - /// Use instead: - /// ```rust - /// # let opt = Some(1); - /// # let res: Result = Ok(1); - /// if let Some(x) = opt { - /// // .. - /// } - /// - /// if let Ok(x) = res { - /// // .. - /// } - /// ``` - #[clippy::version = "1.45.0"] - pub FOR_LOOPS_OVER_FALLIBLES, - suspicious, - "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" -} - declare_clippy_lint! { /// ### What it does /// Detects `loop + match` combinations that are easier @@ -648,7 +604,6 @@ declare_lint_pass!(Loops => [ EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, - FOR_LOOPS_OVER_FALLIBLES, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -739,30 +694,22 @@ fn check_for_loop<'tcx>( manual_find::check(cx, pat, arg, body, span, expr); } -fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { - let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used - +fn check_for_loop_arg(cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) { if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind { let method_name = method.ident.as_str(); // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x match method_name { "iter" | "iter_mut" => { explicit_iter_loop::check(cx, self_arg, arg, method_name); - for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); }, "into_iter" => { explicit_iter_loop::check(cx, self_arg, arg, method_name); explicit_into_iter_loop::check(cx, self_arg, arg); - for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); }, "next" => { - next_loop_linted = iter_next_loop::check(cx, arg); + iter_next_loop::check(cx, arg); }, _ => {}, } } - - if !next_loop_linted { - for_loops_over_fallibles::check(cx, pat, arg, None); - } } diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index d320eea1c377..76d6ad0b23e6 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -11,8 +11,8 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::disallowed_method", "clippy::disallowed_methods"), ("clippy::disallowed_type", "clippy::disallowed_types"), ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"), - ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"), + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::identity_conversion", "clippy::useless_conversion"), ("clippy::if_let_some_result", "clippy::match_result_ok"), ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), @@ -31,6 +31,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::to_string_in_display", "clippy::recursive_format_impl"), ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::drop_bounds", "drop_bounds"), + ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), diff --git a/src/docs.rs b/src/docs.rs index 3bf488ab4779..bd27bc7938f8 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -170,7 +170,6 @@ docs! { "fn_to_numeric_cast_any", "fn_to_numeric_cast_with_truncation", "for_kv_map", - "for_loops_over_fallibles", "forget_copy", "forget_non_drop", "forget_ref", diff --git a/src/docs/for_loops_over_fallibles.txt b/src/docs/for_loops_over_fallibles.txt deleted file mode 100644 index c5a7508e45d4..000000000000 --- a/src/docs/for_loops_over_fallibles.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for `for` loops over `Option` or `Result` values. - -### Why is this bad? -Readability. This is more clearly expressed as an `if -let`. - -### Example -``` -for x in opt { - // .. -} - -for x in &res { - // .. -} - -for x in res.iter() { - // .. -} -``` - -Use instead: -``` -if let Some(x) = opt { - // .. -} - -if let Ok(x) = res { - // .. -} -``` \ No newline at end of file diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs deleted file mode 100644 index 75cdcc02353f..000000000000 --- a/tests/ui/for_loops_over_fallibles.rs +++ /dev/null @@ -1,74 +0,0 @@ -#![warn(clippy::for_loops_over_fallibles)] -#![allow(clippy::uninlined_format_args)] -#![allow(for_loops_over_fallibles)] - -fn for_loops_over_fallibles() { - let option = Some(1); - let mut result = option.ok_or("x not found"); - let v = vec![0, 1, 2]; - - // check over an `Option` - for x in option { - println!("{}", x); - } - - // check over an `Option` - for x in option.iter() { - println!("{}", x); - } - - // check over a `Result` - for x in result { - println!("{}", x); - } - - // check over a `Result` - for x in result.iter_mut() { - println!("{}", x); - } - - // check over a `Result` - for x in result.into_iter() { - println!("{}", x); - } - - for x in option.ok_or("x not found") { - println!("{}", x); - } - - // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call - // in the chain - for x in v.iter().next() { - println!("{}", x); - } - - // make sure we lint when next() is not the last call in the chain - for x in v.iter().next().and(Some(0)) { - println!("{}", x); - } - - for x in v.iter().next().ok_or("x not found") { - println!("{}", x); - } - - // check for false positives - - // for loop false positive - for x in v { - println!("{}", x); - } - - // while let false positive for Option - while let Some(x) = option { - println!("{}", x); - break; - } - - // while let false positive for Result - while let Ok(x) = result { - println!("{}", x); - break; - } -} - -fn main() {} diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr deleted file mode 100644 index f09adccabd1a..000000000000 --- a/tests/ui/for_loops_over_fallibles.stderr +++ /dev/null @@ -1,95 +0,0 @@ -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:10:14 - | -LL | for x in option { - | ^^^^^^ - | - = help: consider replacing `for x in option` with `if let Some(x) = option` - = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` - -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:15:14 - | -LL | for x in option.iter() { - | ^^^^^^ - | - = help: consider replacing `for x in option.iter()` with `if let Some(x) = option` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:20:14 - | -LL | for x in result { - | ^^^^^^ - | - = help: consider replacing `for x in result` with `if let Ok(x) = result` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:25:14 - | -LL | for x in result.iter_mut() { - | ^^^^^^ - | - = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:30:14 - | -LL | for x in result.into_iter() { - | ^^^^^^ - | - = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result` - -error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:34:14 - | -LL | for x in option.ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` - -error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loops_over_fallibles.rs:40:14 - | -LL | for x in v.iter().next() { - | ^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::iter_next_loop)]` on by default - -error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:45:14 - | -LL | for x in v.iter().next().and(Some(0)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` - -error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:49:14 - | -LL | for x in v.iter().next().ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` - -error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:61:5 - | -LL | / while let Some(x) = option { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - | - = note: `#[deny(clippy::never_loop)]` on by default - -error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:67:5 - | -LL | / while let Ok(x) = result { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - -error: aborting due to 11 previous errors - diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index a59da4ae10bc..e12ea7ec1450 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -7,7 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, - clippy::for_loops_over_fallibles, + for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 0bdbefa51e8b..325a6db06c4e 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -7,7 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, - clippy::for_loops_over_fallibles, + for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index a6e7bdba77c6..8beae8dee085 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -12,7 +12,7 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::for_loops_over_fallibles)] +#![allow(for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] #![allow(clippy::overly_complex_bool_expr)] @@ -45,8 +45,8 @@ #![warn(clippy::disallowed_methods)] #![warn(clippy::disallowed_types)] #![warn(clippy::mixed_read_write_in_expression)] -#![warn(clippy::for_loops_over_fallibles)] -#![warn(clippy::for_loops_over_fallibles)] +#![warn(for_loops_over_fallibles)] +#![warn(for_loops_over_fallibles)] #![warn(clippy::useless_conversion)] #![warn(clippy::match_result_ok)] #![warn(clippy::overly_complex_bool_expr)] @@ -65,6 +65,7 @@ #![warn(clippy::recursive_format_impl)] #![warn(clippy::invisible_characters)] #![warn(drop_bounds)] +#![warn(for_loops_over_fallibles)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index e8f57597d02b..9e665047baae 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -12,7 +12,7 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::for_loops_over_fallibles)] +#![allow(for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] #![allow(clippy::overly_complex_bool_expr)] @@ -65,6 +65,7 @@ #![warn(clippy::to_string_in_display)] #![warn(clippy::zero_width_space)] #![warn(clippy::drop_bounds)] +#![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] #![warn(clippy::invalid_ref)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 31865a7f66d6..63eb565185f0 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -54,17 +54,17 @@ error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_r LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` -error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` --> $DIR/rename.rs:49:9 | LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` --> $DIR/rename.rs:50:9 @@ -174,59 +174,65 @@ error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` +error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` --> $DIR/rename.rs:68:9 | +LL | #![warn(clippy::for_loops_over_fallibles)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` + +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` + --> $DIR/rename.rs:69:9 + | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 38 previous errors +error: aborting due to 39 previous errors From 1688368b33fa3bc24913440078c415057d659bb4 Mon Sep 17 00:00:00 2001 From: unvalley Date: Sun, 9 Oct 2022 23:35:52 +0900 Subject: [PATCH 0055/1126] feat: add Default to Lint groups --- util/gh-pages/index.html | 6 ++++++ util/gh-pages/script.js | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index c5d602ea3035..8dc4513325d1 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -448,6 +448,12 @@ Otherwise, have a great day =^.^= None +
  • + +
  • -
  • - -
  • +
  • + +
  • where P: DispatchFromDyn {} /// 8 | let x: Pin<&mut Foo> = { /// | - borrow later stored here /// 9 | let x: Pin<&mut Foo> = pin!(Foo { /* … */ }); -/// | ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use +/// | ^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use /// 10 | x /// 11 | }; // <- Foo is dropped /// | - temporary value is freed at the end of this statement diff --git a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr index a6af129bf39f..4eeec09b9104 100644 --- a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr +++ b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x = defer(&vec!["Goodbye", "world!"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | x.x[0]; | ------ borrow later used here | diff --git a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr index dea8ac90bec2..c62d5f903c8d 100644 --- a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr +++ b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | buggy_map.insert(42, &*Box::new(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | buggy_map.insert(43, &*tmp); | --------------------------- borrow later used here diff --git a/src/test/ui/borrowck/issue-11493.stderr b/src/test/ui/borrowck/issue-11493.stderr index a5d1f2816f1c..2720b09b0fc5 100644 --- a/src/test/ui/borrowck/issue-11493.stderr +++ b/src/test/ui/borrowck/issue-11493.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let y = x.as_ref().unwrap_or(&id(5)); | ^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | let _ = &y; | -- borrow later used here | diff --git a/src/test/ui/borrowck/issue-17545.stderr b/src/test/ui/borrowck/issue-17545.stderr index 79a1e09bd7cc..3ae7e64d202b 100644 --- a/src/test/ui/borrowck/issue-17545.stderr +++ b/src/test/ui/borrowck/issue-17545.stderr @@ -5,7 +5,7 @@ LL | pub fn foo<'a, F: Fn(&'a ())>(bar: F) { | -- lifetime `'a` defined here LL | / bar.call(( LL | | &id(()), - | | ^^^^^^ creates a temporary which is freed while still in use + | | ^^^^^^ creates a temporary value which is freed while still in use LL | | )); | | -- temporary value is freed at the end of this statement | |______| diff --git a/src/test/ui/borrowck/issue-36082.fixed b/src/test/ui/borrowck/issue-36082.fixed index 8640ca7a5096..8fc963a85664 100644 --- a/src/test/ui/borrowck/issue-36082.fixed +++ b/src/test/ui/borrowck/issue-36082.fixed @@ -10,7 +10,7 @@ fn main() { let val: &_ = binding.0; //~^ ERROR temporary value dropped while borrowed [E0716] //~| NOTE temporary value is freed at the end of this statement - //~| NOTE creates a temporary which is freed while still in use + //~| NOTE creates a temporary value which is freed while still in use //~| HELP consider using a `let` binding to create a longer lived value println!("{}", val); //~^ borrow later used here diff --git a/src/test/ui/borrowck/issue-36082.rs b/src/test/ui/borrowck/issue-36082.rs index 877d372fb848..20f66b4d45de 100644 --- a/src/test/ui/borrowck/issue-36082.rs +++ b/src/test/ui/borrowck/issue-36082.rs @@ -9,7 +9,7 @@ fn main() { let val: &_ = x.borrow().0; //~^ ERROR temporary value dropped while borrowed [E0716] //~| NOTE temporary value is freed at the end of this statement - //~| NOTE creates a temporary which is freed while still in use + //~| NOTE creates a temporary value which is freed while still in use //~| HELP consider using a `let` binding to create a longer lived value println!("{}", val); //~^ borrow later used here diff --git a/src/test/ui/borrowck/issue-36082.stderr b/src/test/ui/borrowck/issue-36082.stderr index 4bd586db1cdc..a6357f8182fc 100644 --- a/src/test/ui/borrowck/issue-36082.stderr +++ b/src/test/ui/borrowck/issue-36082.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let val: &_ = x.borrow().0; | ^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | println!("{}", val); | --- borrow later used here diff --git a/src/test/ui/cleanup-rvalue-scopes-cf.stderr b/src/test/ui/cleanup-rvalue-scopes-cf.stderr index 40f14c389842..425cd75141ce 100644 --- a/src/test/ui/cleanup-rvalue-scopes-cf.stderr +++ b/src/test/ui/cleanup-rvalue-scopes-cf.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x1 = arg(&AddFlags(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -21,7 +21,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x2 = AddFlags(1).get(); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -38,7 +38,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x3 = &*arg(&AddFlags(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -55,7 +55,7 @@ error[E0716]: temporary value dropped while borrowed LL | let ref x4 = *arg(&AddFlags(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -72,7 +72,7 @@ error[E0716]: temporary value dropped while borrowed LL | let &ref x5 = arg(&AddFlags(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -89,7 +89,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x6 = AddFlags(1).get(); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here @@ -106,7 +106,7 @@ error[E0716]: temporary value dropped while borrowed LL | let StackBox { f: x7 } = StackBox { f: AddFlags(1).get() }; | ^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here diff --git a/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr b/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr index 78143042ece7..ed6a6ee6e0fd 100644 --- a/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr +++ b/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let x: &'static usize = | -------------- type annotation requires that borrow lasts for `'static` LL | &std::intrinsics::size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr index 69e3ca716a90..2e697b219c5a 100644 --- a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr +++ b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr @@ -10,7 +10,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/dont_promote_unstable_const_fn.rs:17:28 | LL | let _: &'static u32 = &foo(); - | ------------ ^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } @@ -20,7 +20,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/dont_promote_unstable_const_fn.rs:21:28 | LL | let _: &'static u32 = &meh(); - | ------------ ^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -31,7 +31,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/dont_promote_unstable_const_fn.rs:22:26 | LL | let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis(); - | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.stderr b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.stderr index 129f06151074..aa742d784e03 100644 --- a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.stderr +++ b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/dont_promote_unstable_const_fn_cross_crate.rs:8:28 | LL | let _: &'static u32 = &foo(); - | ------------ ^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | let _x: &'static u32 = &foo(); @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/dont_promote_unstable_const_fn_cross_crate.rs:9:29 | LL | let _x: &'static u32 = &foo(); - | ------------ ^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr b/src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr index 596fa090d976..2d4e7c83d3e4 100644 --- a/src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr +++ b/src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_const_fn_fail.rs:17:27 | LL | let x: &'static u8 = &(bar() + 1); - | ----------- ^^^^^^^^^^^ creates a temporary which is freed while still in use + | ----------- ^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... diff --git a/src/test/ui/consts/const-eval/promoted_const_fn_fail_deny_const_err.stderr b/src/test/ui/consts/const-eval/promoted_const_fn_fail_deny_const_err.stderr index 63dc43a41a8f..9ebae3a18a32 100644 --- a/src/test/ui/consts/const-eval/promoted_const_fn_fail_deny_const_err.stderr +++ b/src/test/ui/consts/const-eval/promoted_const_fn_fail_deny_const_err.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_const_fn_fail_deny_const_err.rs:18:27 | LL | let x: &'static u8 = &(bar() + 1); - | ----------- ^^^^^^^^^^^ creates a temporary which is freed while still in use + | ----------- ^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... diff --git a/src/test/ui/consts/const-eval/promoted_raw_ptr_ops.stderr b/src/test/ui/consts/const-eval/promoted_raw_ptr_ops.stderr index 8ac60da38634..01fcf2ec2134 100644 --- a/src/test/ui/consts/const-eval/promoted_raw_ptr_ops.stderr +++ b/src/test/ui/consts/const-eval/promoted_raw_ptr_ops.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_raw_ptr_ops.rs:2:29 | LL | let x: &'static bool = &(42 as *const i32 == 43 as *const i32); - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_raw_ptr_ops.rs:4:30 | LL | let y: &'static usize = &(&1 as *const i32 as usize + 1); - | -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_raw_ptr_ops.rs:6:28 | LL | let z: &'static i32 = &(unsafe { *(42 as *const i32) }); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -35,7 +35,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted_raw_ptr_ops.rs:8:29 | LL | let a: &'static bool = &(main as fn() == main as fn()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-eval/transmute-const-promotion.stderr b/src/test/ui/consts/const-eval/transmute-const-promotion.stderr index 15b9b56ea660..434a957f6484 100644 --- a/src/test/ui/consts/const-eval/transmute-const-promotion.stderr +++ b/src/test/ui/consts/const-eval/transmute-const-promotion.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/transmute-const-promotion.rs:4:37 | LL | let x: &'static u32 = unsafe { &mem::transmute(3.0f32) }; - | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-eval/union_promotion.stderr b/src/test/ui/consts/const-eval/union_promotion.stderr index 70808c520d3f..42f17de20034 100644 --- a/src/test/ui/consts/const-eval/union_promotion.stderr +++ b/src/test/ui/consts/const-eval/union_promotion.stderr @@ -7,7 +7,7 @@ LL | let x: &'static bool = &unsafe { | | type annotation requires that borrow lasts for `'static` LL | | Foo { a: &1 }.b == Foo { a: &2 }.b LL | | }; - | |_____^ creates a temporary which is freed while still in use + | |_____^ creates a temporary value which is freed while still in use LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/consts/const-int-conversion.stderr b/src/test/ui/consts/const-int-conversion.stderr index 61162a792262..5dd757e3f5ee 100644 --- a/src/test/ui/consts/const-int-conversion.stderr +++ b/src/test/ui/consts/const-int-conversion.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:2:28 | LL | let x: &'static i32 = &(5_i32.reverse_bits()); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:4:28 | LL | let y: &'static i32 = &(i32::from_be_bytes([0x12, 0x34, 0x56, 0x78])); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:6:28 | LL | let z: &'static i32 = &(i32::from_le_bytes([0x12, 0x34, 0x56, 0x78])); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -35,7 +35,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:8:28 | LL | let a: &'static i32 = &(i32::from_be(i32::from_ne_bytes([0x80, 0, 0, 0]))); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -46,7 +46,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:10:29 | LL | let b: &'static [u8] = &(0x12_34_56_78_i32.to_be_bytes()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -57,7 +57,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:12:29 | LL | let c: &'static [u8] = &(0x12_34_56_78_i32.to_le_bytes()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -68,7 +68,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:14:29 | LL | let d: &'static [u8] = &(i32::MIN.to_be().to_ne_bytes()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-int-overflowing.stderr b/src/test/ui/consts/const-int-overflowing.stderr index 56c7f7f092d6..7d3689e6ec7d 100644 --- a/src/test/ui/consts/const-int-overflowing.stderr +++ b/src/test/ui/consts/const-int-overflowing.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-overflowing.rs:2:36 | LL | let x: &'static (i32, bool) = &(5_i32.overflowing_add(3)); - | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-overflowing.rs:4:36 | LL | let y: &'static (i32, bool) = &(5_i32.overflowing_sub(3)); - | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-overflowing.rs:6:36 | LL | let z: &'static (i32, bool) = &(5_i32.overflowing_mul(3)); - | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-int-rotate.stderr b/src/test/ui/consts/const-int-rotate.stderr index ed265804bbc6..039da1c31c57 100644 --- a/src/test/ui/consts/const-int-rotate.stderr +++ b/src/test/ui/consts/const-int-rotate.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-rotate.rs:2:28 | LL | let x: &'static i32 = &(5_i32.rotate_left(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-rotate.rs:4:28 | LL | let y: &'static i32 = &(5_i32.rotate_right(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-int-sign.stderr b/src/test/ui/consts/const-int-sign.stderr index 5f8fd4141804..fc23d9d2b294 100644 --- a/src/test/ui/consts/const-int-sign.stderr +++ b/src/test/ui/consts/const-int-sign.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-sign.rs:2:29 | LL | let x: &'static bool = &(5_i32.is_negative()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-sign.rs:4:29 | LL | let y: &'static bool = &(5_i32.is_positive()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-int-wrapping.stderr b/src/test/ui/consts/const-int-wrapping.stderr index 5174b72659cd..1342fadc4055 100644 --- a/src/test/ui/consts/const-int-wrapping.stderr +++ b/src/test/ui/consts/const-int-wrapping.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-wrapping.rs:2:28 | LL | let x: &'static i32 = &(5_i32.wrapping_add(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-wrapping.rs:4:28 | LL | let y: &'static i32 = &(5_i32.wrapping_sub(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-wrapping.rs:6:28 | LL | let z: &'static i32 = &(5_i32.wrapping_mul(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -35,7 +35,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-wrapping.rs:8:28 | LL | let a: &'static i32 = &(5_i32.wrapping_shl(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -46,7 +46,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-wrapping.rs:10:28 | LL | let b: &'static i32 = &(5_i32.wrapping_shr(3)); - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-mut-refs/mut_ref_in_final.stderr b/src/test/ui/consts/const-mut-refs/mut_ref_in_final.stderr index 3a9ce79f10ef..78c58b5ab092 100644 --- a/src/test/ui/consts/const-mut-refs/mut_ref_in_final.stderr +++ b/src/test/ui/consts/const-mut-refs/mut_ref_in_final.stderr @@ -11,7 +11,7 @@ LL | const B3: Option<&mut i32> = Some(&mut 42); | ----------^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -21,7 +21,7 @@ LL | const B4: Option<&mut i32> = helper(&mut 42); | ------------^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -31,7 +31,7 @@ LL | const FOO: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -41,7 +41,7 @@ LL | static FOO2: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a static requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -51,7 +51,7 @@ LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a static requires that borrow lasts for `'static` error: aborting due to 6 previous errors diff --git a/src/test/ui/consts/const-ptr-nonnull.stderr b/src/test/ui/consts/const-ptr-nonnull.stderr index 26946fb99024..dbcb0c86052e 100644 --- a/src/test/ui/consts/const-ptr-nonnull.stderr +++ b/src/test/ui/consts/const-ptr-nonnull.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-ptr-nonnull.rs:4:37 | LL | let x: &'static NonNull = &(NonNull::dangling()); - | --------------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | --------------------- ^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-ptr-nonnull.rs:9:37 | LL | let x: &'static NonNull = &(non_null.cast()); - | --------------------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | --------------------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-ptr-unique.stderr b/src/test/ui/consts/const-ptr-unique.stderr index 3644cf4cec7d..83448c3e8d87 100644 --- a/src/test/ui/consts/const-ptr-unique.stderr +++ b/src/test/ui/consts/const-ptr-unique.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/const-ptr-unique.rs:8:33 | LL | let x: &'static *mut u32 = &(unique.as_ptr()); - | ----------------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ----------------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/control-flow/interior-mutability.stderr b/src/test/ui/consts/control-flow/interior-mutability.stderr index 4f9c7d34c35f..db2ffb91b988 100644 --- a/src/test/ui/consts/control-flow/interior-mutability.stderr +++ b/src/test/ui/consts/control-flow/interior-mutability.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/interior-mutability.rs:40:26 | LL | let x: &'static _ = &X; - | ---------- ^ creates a temporary which is freed while still in use + | ---------- ^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/interior-mutability.rs:41:26 | LL | let y: &'static _ = &Y; - | ---------- ^ creates a temporary which is freed while still in use + | ---------- ^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | let z: &'static _ = &Z; @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/interior-mutability.rs:42:26 | LL | let z: &'static _ = &Z; - | ---------- ^ creates a temporary which is freed while still in use + | ---------- ^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/consts/issue-54224.stderr b/src/test/ui/consts/issue-54224.stderr index 8dcb4daca3b7..55fe55759df5 100644 --- a/src/test/ui/consts/issue-54224.stderr +++ b/src/test/ui/consts/issue-54224.stderr @@ -5,7 +5,7 @@ LL | const FOO: Option<&[[u8; 3]]> = Some(&[*b"foo"]); | ------^^^^^^^^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -15,7 +15,7 @@ LL | pub const Z: Cow<'static, [ [u8; 3] ]> = Cow::Borrowed(&[*b"ABC"]); | ---------------^^^^^^^^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/min_const_fn/promotion.stderr b/src/test/ui/consts/min_const_fn/promotion.stderr index 550423c2d933..0b8dc0ce0e90 100644 --- a/src/test/ui/consts/min_const_fn/promotion.stderr +++ b/src/test/ui/consts/min_const_fn/promotion.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:11:27 | LL | let x: &'static () = &foo1(); - | ----------- ^^^^^^ creates a temporary which is freed while still in use + | ----------- ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:12:28 | LL | let y: &'static i32 = &foo2(42); - | ------------ ^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:13:28 | LL | let z: &'static i32 = &foo3(); - | ------------ ^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -35,7 +35,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:14:34 | LL | let a: &'static Cell = &foo4(); - | ------------------ ^^^^^^ creates a temporary which is freed while still in use + | ------------------ ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -46,7 +46,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:15:42 | LL | let a: &'static Option> = &foo5(); - | -------------------------- ^^^^^^ creates a temporary which is freed while still in use + | -------------------------- ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | let a: &'static Option> = &foo6(); @@ -57,7 +57,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promotion.rs:16:42 | LL | let a: &'static Option> = &foo6(); - | -------------------------- ^^^^^^ creates a temporary which is freed while still in use + | -------------------------- ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/consts/promote-not.stderr b/src/test/ui/consts/promote-not.stderr index 0d0b0f9c689b..b93358e8dcce 100644 --- a/src/test/ui/consts/promote-not.stderr +++ b/src/test/ui/consts/promote-not.stderr @@ -5,14 +5,14 @@ LL | static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]); | ----------^^^^^^^^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a static requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:11:18 | LL | let x = &mut [1,2,3]; - | ^^^^^^^ creates a temporary which is freed while still in use + | ^^^^^^^ creates a temporary value which is freed while still in use LL | x | - using this value as a static requires that borrow lasts for `'static` LL | }; @@ -22,7 +22,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:20:32 | LL | let _x: &'static () = &foo(); - | ----------- ^^^^^ creates a temporary which is freed while still in use + | ----------- ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } @@ -32,7 +32,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:28:29 | LL | let _x: &'static i32 = &unsafe { U { x: 0 }.x }; - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } @@ -42,7 +42,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:33:29 | LL | let _x: &'static i32 = &unsafe { U { x: 0 }.x }; - | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | }; @@ -52,7 +52,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:39:29 | LL | let _val: &'static _ = &(Cell::new(1), 2).1; - | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | }; @@ -62,7 +62,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:46:29 | LL | let _val: &'static _ = &(Cell::new(1), 2).0; - | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -73,7 +73,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:47:29 | LL | let _val: &'static _ = &(Cell::new(1), 2).1; - | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -84,7 +84,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:50:29 | LL | let _val: &'static _ = &(1/0); - | ---------- ^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -95,7 +95,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:51:29 | LL | let _val: &'static _ = &(1/(1-1)); - | ---------- ^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -106,7 +106,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:52:29 | LL | let _val: &'static _ = &(1%0); - | ---------- ^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -117,7 +117,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:53:29 | LL | let _val: &'static _ = &(1%(1-1)); - | ---------- ^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -128,7 +128,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:54:29 | LL | let _val: &'static _ = &([1,2,3][4]+1); - | ---------- ^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -139,7 +139,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:57:29 | LL | let _val: &'static _ = &TEST_DROP; - | ---------- ^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -150,7 +150,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:59:29 | LL | let _val: &'static _ = &&TEST_DROP; - | ---------- ^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -161,7 +161,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:59:30 | LL | let _val: &'static _ = &&TEST_DROP; - | ---------- ^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -172,7 +172,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:62:29 | LL | let _val: &'static _ = &(&TEST_DROP,); - | ---------- ^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -183,7 +183,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:62:31 | LL | let _val: &'static _ = &(&TEST_DROP,); - | ---------- ^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -194,7 +194,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promote-not.rs:65:29 | LL | let _val: &'static _ = &[&TEST_DROP; 1]; - | ---------- ^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ---------- ^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -207,7 +207,7 @@ error[E0716]: temporary value dropped while borrowed LL | let _val: &'static _ = &[&TEST_DROP; 1]; | ---------- ^^^^^^^^^ - temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` error: aborting due to 20 previous errors diff --git a/src/test/ui/consts/promote_const_let.stderr b/src/test/ui/consts/promote_const_let.stderr index c47d297c9040..975a235a6495 100644 --- a/src/test/ui/consts/promote_const_let.stderr +++ b/src/test/ui/consts/promote_const_let.stderr @@ -19,7 +19,7 @@ LL | let x: &'static u32 = &{ LL | | let y = 42; LL | | y LL | | }; - | |_____^ creates a temporary which is freed while still in use + | |_____^ creates a temporary value which is freed while still in use LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/consts/promoted-const-drop.stderr b/src/test/ui/consts/promoted-const-drop.stderr index 184ba0ea3b37..4802834173fc 100644 --- a/src/test/ui/consts/promoted-const-drop.stderr +++ b/src/test/ui/consts/promoted-const-drop.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted-const-drop.rs:13:26 | LL | let _: &'static A = &A(); - | ---------- ^^^ creates a temporary which is freed while still in use + | ---------- ^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | let _: &'static [A] = &[C]; @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/promoted-const-drop.rs:14:28 | LL | let _: &'static [A] = &[C]; - | ------------ ^^^ creates a temporary which is freed while still in use + | ------------ ^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/consts/qualif-union.stderr b/src/test/ui/consts/qualif-union.stderr index 8ec68ada048a..d847cf88f505 100644 --- a/src/test/ui/consts/qualif-union.stderr +++ b/src/test/ui/consts/qualif-union.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/qualif-union.rs:28:26 | LL | let _: &'static _ = &C1; - | ---------- ^^ creates a temporary which is freed while still in use + | ---------- ^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -13,7 +13,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/qualif-union.rs:29:26 | LL | let _: &'static _ = &C2; - | ---------- ^^ creates a temporary which is freed while still in use + | ---------- ^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -24,7 +24,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/qualif-union.rs:30:26 | LL | let _: &'static _ = &C3; - | ---------- ^^ creates a temporary which is freed while still in use + | ---------- ^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` ... @@ -35,7 +35,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/qualif-union.rs:31:26 | LL | let _: &'static _ = &C4; - | ---------- ^^ creates a temporary which is freed while still in use + | ---------- ^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | let _: &'static _ = &C5; @@ -46,7 +46,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/qualif-union.rs:32:26 | LL | let _: &'static _ = &C5; - | ---------- ^^ creates a temporary which is freed while still in use + | ---------- ^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/generator/auto-trait-regions.stderr b/src/test/ui/generator/auto-trait-regions.stderr index 23324af6171a..0b1f34aeb96b 100644 --- a/src/test/ui/generator/auto-trait-regions.stderr +++ b/src/test/ui/generator/auto-trait-regions.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let a = A(&mut true, &mut true, No); | ^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | assert_foo(a); | - borrow later used here @@ -17,7 +17,7 @@ error[E0716]: temporary value dropped while borrowed LL | let a = A(&mut true, &mut true, No); | ^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | assert_foo(a); | - borrow later used here diff --git a/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr b/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr index 414999881d47..1c9abc4e837c 100644 --- a/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr +++ b/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/hrtb-implied-1.rs:31:22 | LL | let slice = &mut (); - | ^^ creates a temporary which is freed while still in use + | ^^ creates a temporary value which is freed while still in use ... LL | print_items::>(windows); | -------------------------------------- argument requires that borrow lasts for `'static` diff --git a/src/test/ui/issues/issue-47184.stderr b/src/test/ui/issues/issue-47184.stderr index f97713b4ac43..c2c7df7a333a 100644 --- a/src/test/ui/issues/issue-47184.stderr +++ b/src/test/ui/issues/issue-47184.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let _vec: Vec<&'static String> = vec![&String::new()]; | -------------------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-52049.stderr b/src/test/ui/issues/issue-52049.stderr index 55929d85da45..b25dbd1cb8b1 100644 --- a/src/test/ui/issues/issue-52049.stderr +++ b/src/test/ui/issues/issue-52049.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | foo(&unpromotable(5u32)); | -----^^^^^^^^^^^^^^^^^^- | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/lifetimes/borrowck-let-suggestion.stderr b/src/test/ui/lifetimes/borrowck-let-suggestion.stderr index bbf04c98436e..987b051b1110 100644 --- a/src/test/ui/lifetimes/borrowck-let-suggestion.stderr +++ b/src/test/ui/lifetimes/borrowck-let-suggestion.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let mut x = vec![1].iter(); | ^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | LL | x.use_mut(); | ----------- borrow later used here diff --git a/src/test/ui/nll/borrowed-temporary-error.stderr b/src/test/ui/nll/borrowed-temporary-error.stderr index 2c6bd92641f6..89781d96fab2 100644 --- a/src/test/ui/nll/borrowed-temporary-error.stderr +++ b/src/test/ui/nll/borrowed-temporary-error.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/borrowed-temporary-error.rs:8:10 | LL | &(v,) - | ^^^^ creates a temporary which is freed while still in use + | ^^^^ creates a temporary value which is freed while still in use LL | LL | }); | - temporary value is freed at the end of this statement diff --git a/src/test/ui/nll/issue-57265-return-type-wf-check.stderr b/src/test/ui/nll/issue-57265-return-type-wf-check.stderr index 20add62b91dd..bb45575fa64c 100644 --- a/src/test/ui/nll/issue-57265-return-type-wf-check.stderr +++ b/src/test/ui/nll/issue-57265-return-type-wf-check.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let (_, z) = foo(&"hello".to_string()); | -----^^^^^^^^^^^^^^^^^^^-- temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'static` error: aborting due to previous error diff --git a/src/test/ui/nll/user-annotations/patterns.stderr b/src/test/ui/nll/user-annotations/patterns.stderr index 60d6e6db3632..de6f8f80fe25 100644 --- a/src/test/ui/nll/user-annotations/patterns.stderr +++ b/src/test/ui/nll/user-annotations/patterns.stderr @@ -76,7 +76,7 @@ error[E0716]: temporary value dropped while borrowed LL | let _: Vec<&'static String> = vec![&String::new()]; | -------------------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -85,7 +85,7 @@ error[E0716]: temporary value dropped while borrowed LL | let (_, a): (Vec<&'static String>, _) = (vec![&String::new()], 44); | ------------------------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed @@ -94,7 +94,7 @@ error[E0716]: temporary value dropped while borrowed LL | let (_a, b): (Vec<&'static String>, _) = (vec![&String::new()], 44); | ------------------------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` error[E0597]: `x` does not live long enough diff --git a/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr b/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr index 4971263af08a..fc1be052fb79 100644 --- a/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr +++ b/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | let phantom_pinned = identity(pin!(PhantomPinned)); | ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | LL | stuff(phantom_pinned) | -------------- borrow later used here @@ -18,7 +18,7 @@ error[E0716]: temporary value dropped while borrowed LL | let phantom_pinned = { | -------------- borrow later stored here LL | let phantom_pinned = pin!(PhantomPinned); - | ^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use ... LL | }; | - temporary value is freed at the end of this statement diff --git a/src/test/ui/regions/regions-free-region-ordering-caller1.stderr b/src/test/ui/regions/regions-free-region-ordering-caller1.stderr index 8042b1740b14..8ef7e22536bf 100644 --- a/src/test/ui/regions/regions-free-region-ordering-caller1.stderr +++ b/src/test/ui/regions/regions-free-region-ordering-caller1.stderr @@ -5,7 +5,7 @@ LL | fn call1<'a>(x: &'a usize) { | -- lifetime `'a` defined here ... LL | let z: &'a & usize = &(&y); - | ----------- ^^^^ creates a temporary which is freed while still in use + | ----------- ^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'a` ... diff --git a/src/test/ui/regions/regions-var-type-out-of-scope.stderr b/src/test/ui/regions/regions-var-type-out-of-scope.stderr index 476e82f046f0..c32bbe0ee1fb 100644 --- a/src/test/ui/regions/regions-var-type-out-of-scope.stderr +++ b/src/test/ui/regions/regions-var-type-out-of-scope.stderr @@ -4,7 +4,7 @@ error[E0716]: temporary value dropped while borrowed LL | x = &id(3); | ^^^^^- temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use LL | assert_eq!(*x, 3); | ----------------- borrow later used here | diff --git a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs index 6240f103c99b..18abfb5c3fbe 100644 --- a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs +++ b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs @@ -18,7 +18,7 @@ fn f() { v3.push(&id('x')); // statement 6 //~^ ERROR temporary value dropped while borrowed - //~| NOTE creates a temporary which is freed while still in use + //~| NOTE creates a temporary value which is freed while still in use //~| NOTE temporary value is freed at the end of this statement //~| HELP consider using a `let` binding to create a longer lived value @@ -28,7 +28,7 @@ fn f() { v4.push(&id('y')); //~^ ERROR temporary value dropped while borrowed - //~| NOTE creates a temporary which is freed while still in use + //~| NOTE creates a temporary value which is freed while still in use //~| NOTE temporary value is freed at the end of this statement //~| NOTE consider using a `let` binding to create a longer lived value v4.use_ref(); @@ -39,7 +39,7 @@ fn f() { v5.push(&id('z')); //~^ ERROR temporary value dropped while borrowed - //~| NOTE creates a temporary which is freed while still in use + //~| NOTE creates a temporary value which is freed while still in use //~| NOTE temporary value is freed at the end of this statement //~| HELP consider using a `let` binding to create a longer lived value diff --git a/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr b/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr index a236dab3ae56..2dc29a78d204 100644 --- a/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr +++ b/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr @@ -16,7 +16,7 @@ error[E0716]: temporary value dropped while borrowed LL | v3.push(&id('x')); // statement 6 | ^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (v1, v2, v3, /* v4 is above. */ v5).use_ref(); | -- borrow later used here @@ -33,7 +33,7 @@ error[E0716]: temporary value dropped while borrowed LL | v4.push(&id('y')); | ^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | v4.use_ref(); | ------------ borrow later used here @@ -46,7 +46,7 @@ error[E0716]: temporary value dropped while borrowed LL | v5.push(&id('z')); | ^^^^^^^ - temporary value is freed at the end of this statement | | - | creates a temporary which is freed while still in use + | creates a temporary value which is freed while still in use ... LL | (v1, v2, v3, /* v4 is above. */ v5).use_ref(); | -- borrow later used here diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.stderr b/src/test/ui/span/borrowck-ref-into-rvalue.stderr index cb5289d24b4f..25e344fedfb2 100644 --- a/src/test/ui/span/borrowck-ref-into-rvalue.stderr +++ b/src/test/ui/span/borrowck-ref-into-rvalue.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/borrowck-ref-into-rvalue.rs:4:11 | LL | match Some("Hello".to_string()) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use ... LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/span/issue-15480.stderr b/src/test/ui/span/issue-15480.stderr index 460ad9ac7444..d9cce2254dd9 100644 --- a/src/test/ui/span/issue-15480.stderr +++ b/src/test/ui/span/issue-15480.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/issue-15480.rs:6:10 | LL | &id(3) - | ^^^^^ creates a temporary which is freed while still in use + | ^^^^^ creates a temporary value which is freed while still in use LL | ]; | - temporary value is freed at the end of this statement ... diff --git a/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr b/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr index ba0c45acf237..81e858fa0ce0 100644 --- a/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr +++ b/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/regions-close-over-borrowed-ref-in-obj.rs:12:27 | LL | let ss: &isize = &id(1); - | ^^^^^ creates a temporary which is freed while still in use + | ^^^^^ creates a temporary value which is freed while still in use ... LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/span/slice-borrow.stderr b/src/test/ui/span/slice-borrow.stderr index 27df25be3fa0..b70bf69d688a 100644 --- a/src/test/ui/span/slice-borrow.stderr +++ b/src/test/ui/span/slice-borrow.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/slice-borrow.rs:6:28 | LL | let x: &[isize] = &vec![1, 2, 3, 4, 5]; - | ^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | ^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use ... LL | } | - temporary value is freed at the end of this statement diff --git a/src/test/ui/static/static-drop-scope.stderr b/src/test/ui/static/static-drop-scope.stderr index 112bfc003048..cedcb7367949 100644 --- a/src/test/ui/static/static-drop-scope.stderr +++ b/src/test/ui/static/static-drop-scope.stderr @@ -13,7 +13,7 @@ LL | static PROMOTION_FAIL_S: Option<&'static WithDtor> = Some(&WithDtor); | ------^^^^^^^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a static requires that borrow lasts for `'static` error[E0493]: destructor of `WithDtor` cannot be evaluated at compile-time @@ -31,7 +31,7 @@ LL | const PROMOTION_FAIL_C: Option<&'static WithDtor> = Some(&WithDtor); | ------^^^^^^^^- | | | | | | | temporary value is freed at the end of this statement - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | using this value as a constant requires that borrow lasts for `'static` error[E0493]: destructor of `(WithDtor, i32)` cannot be evaluated at compile-time diff --git a/src/test/ui/static/static-reference-to-fn-2.stderr b/src/test/ui/static/static-reference-to-fn-2.stderr index ff15884bd445..133d8ec2e1e5 100644 --- a/src/test/ui/static/static-reference-to-fn-2.stderr +++ b/src/test/ui/static/static-reference-to-fn-2.stderr @@ -6,7 +6,7 @@ LL | fn state1(self_: &mut StateMachineIter) -> Option<&'static str> { LL | self_.statefn = &id(state2 as StateMachineFunc); | -----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | assignment requires that borrow lasts for `'1` error[E0716]: temporary value dropped while borrowed @@ -17,7 +17,7 @@ LL | fn state2(self_: &mut StateMachineIter) -> Option<(&'static str)> { LL | self_.statefn = &id(state3 as StateMachineFunc); | -----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | assignment requires that borrow lasts for `'1` error[E0716]: temporary value dropped while borrowed @@ -28,7 +28,7 @@ LL | fn state3(self_: &mut StateMachineIter) -> Option<(&'static str)> { LL | self_.statefn = &id(finished as StateMachineFunc); | -----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement | | | - | | creates a temporary which is freed while still in use + | | creates a temporary value which is freed while still in use | assignment requires that borrow lasts for `'1` error[E0515]: cannot return value referencing temporary value diff --git a/src/test/ui/static/static-region-bound.stderr b/src/test/ui/static/static-region-bound.stderr index 15261259ed41..1a607e3c014a 100644 --- a/src/test/ui/static/static-region-bound.stderr +++ b/src/test/ui/static/static-region-bound.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/static-region-bound.rs:10:14 | LL | let x = &id(3); - | ^^^^^ creates a temporary which is freed while still in use + | ^^^^^ creates a temporary value which is freed while still in use LL | f(x); | ---- argument requires that borrow lasts for `'static` LL | } diff --git a/src/test/ui/statics/issue-44373.stderr b/src/test/ui/statics/issue-44373.stderr index 6f92fbb1eb68..2d29dec888e8 100644 --- a/src/test/ui/statics/issue-44373.stderr +++ b/src/test/ui/statics/issue-44373.stderr @@ -2,7 +2,7 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/issue-44373.rs:4:42 | LL | let _val: &'static [&'static u32] = &[&FOO]; - | ----------------------- ^^^^^^ creates a temporary which is freed while still in use + | ----------------------- ^^^^^^ creates a temporary value which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | } From de195ff97cda417c61c8760bb332167b2c377078 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 20 Oct 2022 19:28:28 +0200 Subject: [PATCH 0159/1126] fix: Fix DidSaveDocument requests blocking the server on startup --- crates/ide/src/lib.rs | 10 ++ crates/ide/src/parent_module.rs | 10 +- crates/rust-analyzer/src/cargo_target_spec.rs | 6 +- crates/rust-analyzer/src/global_state.rs | 17 +- crates/rust-analyzer/src/handlers.rs | 14 +- crates/rust-analyzer/src/main_loop.rs | 153 ++++++++++-------- crates/rust-analyzer/src/reload.rs | 5 +- 7 files changed, 132 insertions(+), 83 deletions(-) diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 09a5cb03ecdc..416817ca0b42 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -486,6 +486,16 @@ impl Analysis { self.with_db(|db| parent_module::crates_for(db, file_id)) } + /// Returns crates this file belongs too. + pub fn transitive_rev_deps(&self, crate_id: CrateId) -> Cancellable> { + self.with_db(|db| db.crate_graph().transitive_rev_deps(crate_id).collect()) + } + + /// Returns crates this file *might* belong too. + pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable> { + self.with_db(|db| db.relevant_crates(file_id).iter().copied().collect()) + } + /// Returns the edition of the given crate. pub fn crate_edition(&self, crate_id: CrateId) -> Cancellable { self.with_db(|db| db.crate_graph()[crate_id].edition) diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index 9d425954e390..506f9452cf19 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs @@ -1,8 +1,9 @@ -use hir::Semantics; +use hir::{db::DefDatabase, Semantics}; use ide_db::{ base_db::{CrateId, FileId, FileLoader, FilePosition}, RootDatabase, }; +use itertools::Itertools; use syntax::{ algo::find_node_at_offset, ast::{self, AstNode}, @@ -55,7 +56,12 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec Vec { - db.relevant_crates(file_id).iter().copied().collect() + db.relevant_crates(file_id) + .iter() + .copied() + .filter(|&crate_id| db.crate_def_map(crate_id).modules_for_file(file_id).next().is_some()) + .sorted() + .collect() } #[cfg(test)] diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index cbde73547619..6ede194babc2 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs @@ -118,7 +118,11 @@ impl CargoTargetSpec { global_state_snapshot: &GlobalStateSnapshot, file_id: FileId, ) -> Result> { - let (cargo_ws, target) = match global_state_snapshot.cargo_target_for_file_id(file_id) { + let crate_id = match &*global_state_snapshot.analysis.crates_for(file_id)? { + &[crate_id, ..] => crate_id, + _ => return Ok(None), + }; + let (cargo_ws, target) = match global_state_snapshot.cargo_target_for_crate_root(crate_id) { Some(it) => it, None => return Ok(None), }; diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 4cddb12083a1..3fb06c31f7ca 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -8,7 +8,7 @@ use std::{sync::Arc, time::Instant}; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId}; -use ide_db::base_db::{FileLoader, SourceDatabase}; +use ide_db::base_db::{CrateId, FileLoader, SourceDatabase}; use lsp_types::{SemanticTokens, Url}; use parking_lot::{Mutex, RwLock}; use proc_macro_api::ProcMacroServer; @@ -64,7 +64,7 @@ pub(crate) struct GlobalState { pub(crate) source_root_config: SourceRootConfig, pub(crate) proc_macro_clients: Vec>, - pub(crate) flycheck: Vec, + pub(crate) flycheck: Arc<[FlycheckHandle]>, pub(crate) flycheck_sender: Sender, pub(crate) flycheck_receiver: Receiver, @@ -117,6 +117,7 @@ pub(crate) struct GlobalStateSnapshot { vfs: Arc)>>, pub(crate) workspaces: Arc>, pub(crate) proc_macros_loaded: bool, + pub(crate) flycheck: Arc<[FlycheckHandle]>, } impl std::panic::UnwindSafe for GlobalStateSnapshot {} @@ -155,7 +156,7 @@ impl GlobalState { source_root_config: SourceRootConfig::default(), proc_macro_clients: vec![], - flycheck: Vec::new(), + flycheck: Arc::new([]), flycheck_sender, flycheck_receiver, @@ -295,6 +296,7 @@ impl GlobalState { mem_docs: self.mem_docs.clone(), semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache), proc_macros_loaded: !self.fetch_build_data_queue.last_op_result().0.is_empty(), + flycheck: self.flycheck.clone(), } } @@ -398,10 +400,15 @@ impl GlobalStateSnapshot { url_from_abs_path(path) } - pub(crate) fn cargo_target_for_file_id( + pub(crate) fn file_id_to_file_path(&self, file_id: FileId) -> vfs::VfsPath { + self.vfs.read().0.file_path(file_id) + } + + pub(crate) fn cargo_target_for_crate_root( &self, - file_id: FileId, + crate_id: CrateId, ) -> Option<(&CargoWorkspace, Target)> { + let file_id = self.analysis.crate_root(crate_id).ok()?; let path = self.vfs.read().0.file_path(file_id); let path = path.as_path()?; self.workspaces.iter().find_map(|ws| match ws { diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 701a009ea8bd..34795a8eb40a 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -1782,7 +1782,15 @@ fn run_rustfmt( ) -> Result>> { let file_id = from_proto::file_id(snap, &text_document.uri)?; let file = snap.analysis.file_text(file_id)?; - let crate_ids = snap.analysis.crates_for(file_id)?; + + // find the edition of the package the file belongs to + // (if it belongs to multiple we'll just pick the first one and pray) + let edition = snap + .analysis + .relevant_crates_for(file_id)? + .into_iter() + .find_map(|crate_id| snap.cargo_target_for_crate_root(crate_id)) + .map(|(ws, target)| ws[ws[target].package].edition); let line_index = snap.file_line_index(file_id)?; @@ -1808,9 +1816,7 @@ fn run_rustfmt( ); } } - if let Some(&crate_id) = crate_ids.first() { - // Assume all crates are in the same edition - let edition = snap.analysis.crate_edition(crate_id)?; + if let Some(edition) = edition { cmd.arg("--edition"); cmd.arg(edition.to_string()); } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 89189cef1499..319b86c58b5c 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -10,7 +10,7 @@ use std::{ use always_assert::always; use crossbeam_channel::{select, Receiver}; use flycheck::FlycheckHandle; -use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath}; +use ide_db::base_db::{SourceDatabaseExt, VfsPath}; use itertools::Itertools; use lsp_server::{Connection, Notification, Request}; use lsp_types::notification::Notification as _; @@ -191,7 +191,7 @@ impl GlobalState { // NOTE: don't count blocking select! call as a loop-turn time let _p = profile::span("GlobalState::handle_event"); - tracing::debug!("handle_event({:?})", event); + tracing::debug!("{:?} handle_event({:?})", loop_start, event); let task_queue_len = self.task_pool.handle.len(); if task_queue_len > 0 { tracing::info!("task queue len: {}", task_queue_len); @@ -727,7 +727,7 @@ impl GlobalState { .insert(path.clone(), DocumentData::new(params.text_document.version)) .is_err(); if already_exists { - tracing::error!("duplicate DidOpenTextDocument: {}", path) + tracing::error!("duplicate DidOpenTextDocument: {}", path); } this.vfs .write() @@ -774,69 +774,7 @@ impl GlobalState { Ok(()) })? .on::(|this, params| { - let mut updated = false; if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { - let (vfs, _) = &*this.vfs.read(); - - // Trigger flychecks for all workspaces that depend on the saved file - if let Some(file_id) = vfs.file_id(&vfs_path) { - let analysis = this.analysis_host.analysis(); - // Crates containing or depending on the saved file - let crate_ids: Vec<_> = analysis - .crates_for(file_id)? - .into_iter() - .flat_map(|id| { - this.analysis_host - .raw_database() - .crate_graph() - .transitive_rev_deps(id) - }) - .sorted() - .unique() - .collect(); - - let crate_root_paths: Vec<_> = crate_ids - .iter() - .filter_map(|&crate_id| { - analysis - .crate_root(crate_id) - .map(|file_id| { - vfs.file_path(file_id).as_path().map(ToOwned::to_owned) - }) - .transpose() - }) - .collect::>()?; - let crate_root_paths: Vec<_> = - crate_root_paths.iter().map(Deref::deref).collect(); - - // Find all workspaces that have at least one target containing the saved file - let workspace_ids = - this.workspaces.iter().enumerate().filter(|(_, ws)| match ws { - project_model::ProjectWorkspace::Cargo { cargo, .. } => { - cargo.packages().any(|pkg| { - cargo[pkg].targets.iter().any(|&it| { - crate_root_paths.contains(&cargo[it].root.as_path()) - }) - }) - } - project_model::ProjectWorkspace::Json { project, .. } => project - .crates() - .any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c)), - project_model::ProjectWorkspace::DetachedFiles { .. } => false, - }); - - // Find and trigger corresponding flychecks - for flycheck in &this.flycheck { - for (id, _) in workspace_ids.clone() { - if id == flycheck.id() { - updated = true; - flycheck.restart(); - continue; - } - } - } - } - // Re-fetch workspaces if a workspace related file has changed if let Some(abs_path) = vfs_path.as_path() { if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) { @@ -844,13 +782,90 @@ impl GlobalState { .request_op(format!("DidSaveTextDocument {}", abs_path.display())); } } + + let file_id = this.vfs.read().0.file_id(&vfs_path); + if let Some(file_id) = file_id { + let world = this.snapshot(); + let mut updated = false; + let task = move || -> std::result::Result<(), ide::Cancelled> { + // Trigger flychecks for all workspaces that depend on the saved file + // Crates containing or depending on the saved file + let crate_ids: Vec<_> = world + .analysis + .crates_for(file_id)? + .into_iter() + .flat_map(|id| world.analysis.transitive_rev_deps(id)) + .flatten() + .sorted() + .unique() + .collect(); + + let crate_root_paths: Vec<_> = crate_ids + .iter() + .filter_map(|&crate_id| { + world + .analysis + .crate_root(crate_id) + .map(|file_id| { + world + .file_id_to_file_path(file_id) + .as_path() + .map(ToOwned::to_owned) + }) + .transpose() + }) + .collect::>()?; + let crate_root_paths: Vec<_> = + crate_root_paths.iter().map(Deref::deref).collect(); + + // Find all workspaces that have at least one target containing the saved file + let workspace_ids = + world.workspaces.iter().enumerate().filter(|(_, ws)| match ws { + project_model::ProjectWorkspace::Cargo { cargo, .. } => { + cargo.packages().any(|pkg| { + cargo[pkg].targets.iter().any(|&it| { + crate_root_paths.contains(&cargo[it].root.as_path()) + }) + }) + } + project_model::ProjectWorkspace::Json { project, .. } => { + project.crates().any(|(c, _)| { + crate_ids.iter().any(|&crate_id| crate_id == c) + }) + } + project_model::ProjectWorkspace::DetachedFiles { .. } => false, + }); + + // Find and trigger corresponding flychecks + for flycheck in world.flycheck.iter() { + for (id, _) in workspace_ids.clone() { + if id == flycheck.id() { + updated = true; + flycheck.restart(); + continue; + } + } + } + // No specific flycheck was triggered, so let's trigger all of them. + if !updated { + for flycheck in world.flycheck.iter() { + flycheck.restart(); + } + } + Ok(()) + }; + this.task_pool.handle.spawn_with_sender(move |_| { + if let Err(e) = std::panic::catch_unwind(task) { + tracing::error!("DidSaveTextDocument flycheck task panicked: {e:?}") + } + }); + return Ok(()); + } } // No specific flycheck was triggered, so let's trigger all of them. - if !updated { - for flycheck in &this.flycheck { - flycheck.restart(); - } + for flycheck in this.flycheck.iter() { + flycheck.restart(); } Ok(()) })? diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index f7db62baf2c1..cc7600a2fa98 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -466,7 +466,7 @@ impl GlobalState { let config = match self.config.flycheck() { Some(it) => it, None => { - self.flycheck = Vec::new(); + self.flycheck = Arc::new([]); self.diagnostics.clear_check_all(); return; } @@ -510,7 +510,8 @@ impl GlobalState { }) .collect() } - }; + } + .into(); } } From e05df93b8ee0936c37df8e368817dc201e07fb75 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Thu, 20 Oct 2022 17:56:51 +0000 Subject: [PATCH 0160/1126] Workaround the python vscode extension's polyfill --- editors/code/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 10e243dc896c..0aa998db6250 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -191,7 +191,7 @@ export class Config { const VarRegex = new RegExp(/\$\{(.+?)\}/g); export function substituteVSCodeVariableInString(val: string): string { - return val.replaceAll(VarRegex, (substring: string, varName) => { + return val.replace(VarRegex, (substring: string, varName) => { if (typeof varName === "string") { return computeVscodeVar(varName) || substring; } else { From a8e0a20ce4396d3c77adf3b5deb48edfd27d6af2 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 20 Oct 2022 21:12:58 +0200 Subject: [PATCH 0161/1126] internal: Properly handle language configuration config changes --- editors/code/src/config.ts | 100 ++++++++++++++++++++++++++++--------- editors/code/src/ctx.ts | 8 ++- editors/code/src/main.ts | 60 ++-------------------- 3 files changed, 88 insertions(+), 80 deletions(-) diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 10e243dc896c..61125d7a83eb 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -11,13 +11,9 @@ export type RunnableEnvCfg = export class Config { readonly extensionId = "rust-lang.rust-analyzer"; + configureLang: vscode.Disposable | undefined; readonly rootSection = "rust-analyzer"; - private readonly requiresWorkspaceReloadOpts = [ - // FIXME: This shouldn't be here, changing this setting should reload - // `continueCommentsOnNewline` behavior without restart - "typing", - ].map((opt) => `${this.rootSection}.${opt}`); private readonly requiresReloadOpts = [ "cargo", "procMacro", @@ -25,9 +21,7 @@ export class Config { "server", "files", "lens", // works as lens.* - ] - .map((opt) => `${this.rootSection}.${opt}`) - .concat(this.requiresWorkspaceReloadOpts); + ].map((opt) => `${this.rootSection}.${opt}`); readonly package: { version: string; @@ -45,6 +39,11 @@ export class Config { ctx.subscriptions ); this.refreshLogging(); + this.configureLanguage(); + } + + dispose() { + this.configureLang?.dispose(); } private refreshLogging() { @@ -58,33 +57,86 @@ export class Config { private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) { this.refreshLogging(); + this.configureLanguage(); + const requiresReloadOpt = this.requiresReloadOpts.find((opt) => event.affectsConfiguration(opt) ); if (!requiresReloadOpt) return; - const requiresWorkspaceReloadOpt = this.requiresWorkspaceReloadOpts.find((opt) => - event.affectsConfiguration(opt) - ); - - if (!requiresWorkspaceReloadOpt && this.restartServerOnConfigChange) { + if (this.restartServerOnConfigChange) { await vscode.commands.executeCommand("rust-analyzer.reload"); return; } - const message = requiresWorkspaceReloadOpt - ? `Changing "${requiresWorkspaceReloadOpt}" requires a window reload` - : `Changing "${requiresReloadOpt}" requires a reload`; - const userResponse = await vscode.window.showInformationMessage(message, "Reload now"); + const message = `Changing "${requiresReloadOpt}" requires a server restart`; + const userResponse = await vscode.window.showInformationMessage(message, "Restart now"); - if (userResponse === "Reload now") { - const command = requiresWorkspaceReloadOpt - ? "workbench.action.reloadWindow" - : "rust-analyzer.reload"; - if (userResponse === "Reload now") { - await vscode.commands.executeCommand(command); - } + if (userResponse) { + const command = "rust-analyzer.reload"; + await vscode.commands.executeCommand(command); + } + } + + /** + * Sets up additional language configuration that's impossible to do via a + * separate language-configuration.json file. See [1] for more information. + * + * [1]: https://github.com/Microsoft/vscode/issues/11514#issuecomment-244707076 + */ + private configureLanguage() { + if (this.typingContinueCommentsOnNewline && !this.configureLang) { + const indentAction = vscode.IndentAction.None; + + this.configureLang = vscode.languages.setLanguageConfiguration("rust", { + onEnterRules: [ + { + // Doc single-line comment + // e.g. ///| + beforeText: /^\s*\/{3}.*$/, + action: { indentAction, appendText: "/// " }, + }, + { + // Parent doc single-line comment + // e.g. //!| + beforeText: /^\s*\/{2}\!.*$/, + action: { indentAction, appendText: "//! " }, + }, + { + // Begins an auto-closed multi-line comment (standard or parent doc) + // e.g. /** | */ or /*! | */ + beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, + afterText: /^\s*\*\/$/, + action: { + indentAction: vscode.IndentAction.IndentOutdent, + appendText: " * ", + }, + }, + { + // Begins a multi-line comment (standard or parent doc) + // e.g. /** ...| or /*! ...| + beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, + action: { indentAction, appendText: " * " }, + }, + { + // Continues a multi-line comment + // e.g. * ...| + beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/, + action: { indentAction, appendText: "* " }, + }, + { + // Dedents after closing a multi-line comment + // e.g. */| + beforeText: /^(\ \ )*\ \*\/\s*$/, + action: { indentAction, removeText: 1 }, + }, + ], + }); + } + if (!this.typingContinueCommentsOnNewline && this.configureLang) { + this.configureLang.dispose(); + this.configureLang = undefined; } } diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index e94d4365c376..75c6d4698c17 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -38,6 +38,7 @@ export class Ctx { this.dispose(); }, }); + extCtx.subscriptions.push(this); this.statusBar.text = "rust-analyzer"; this.statusBar.tooltip = "ready"; this.statusBar.command = "rust-analyzer.analyzerStatus"; @@ -48,10 +49,15 @@ export class Ctx { this.config = new Config(extCtx); } + dispose() { + this.config.dispose(); + } + clientFetcher() { + const self = this; return { get client(): lc.LanguageClient | undefined { - return this.client; + return self.client; }, }; } diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index c47680fbac51..fa7dc6fe304e 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -77,10 +77,6 @@ async function activateServer(ctx: Ctx): Promise { await initCommonContext(ctx); - if (ctx.config.typingContinueCommentsOnNewline) { - ctx.pushExtCleanup(configureLanguage()); - } - vscode.workspace.onDidChangeConfiguration( async (_) => { await ctx @@ -129,6 +125,11 @@ async function initCommonContext(ctx: Ctx) { ctx.registerCommand("stopServer", (_) => async () => { // FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed await ctx.disposeClient(); + ctx.setServerStatus({ + health: "ok", + quiescent: true, + message: "server is not running", + }); }); ctx.registerCommand("analyzerStatus", commands.analyzerStatus); ctx.registerCommand("memoryUsage", commands.memoryUsage); @@ -172,54 +173,3 @@ async function initCommonContext(ctx: Ctx) { defaultOnEnter.dispose(); ctx.registerCommand("onEnter", commands.onEnter); } - -/** - * Sets up additional language configuration that's impossible to do via a - * separate language-configuration.json file. See [1] for more information. - * - * [1]: https://github.com/Microsoft/vscode/issues/11514#issuecomment-244707076 - */ -function configureLanguage(): vscode.Disposable { - const indentAction = vscode.IndentAction.None; - return vscode.languages.setLanguageConfiguration("rust", { - onEnterRules: [ - { - // Doc single-line comment - // e.g. ///| - beforeText: /^\s*\/{3}.*$/, - action: { indentAction, appendText: "/// " }, - }, - { - // Parent doc single-line comment - // e.g. //!| - beforeText: /^\s*\/{2}\!.*$/, - action: { indentAction, appendText: "//! " }, - }, - { - // Begins an auto-closed multi-line comment (standard or parent doc) - // e.g. /** | */ or /*! | */ - beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, - afterText: /^\s*\*\/$/, - action: { indentAction: vscode.IndentAction.IndentOutdent, appendText: " * " }, - }, - { - // Begins a multi-line comment (standard or parent doc) - // e.g. /** ...| or /*! ...| - beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, - action: { indentAction, appendText: " * " }, - }, - { - // Continues a multi-line comment - // e.g. * ...| - beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/, - action: { indentAction, appendText: "* " }, - }, - { - // Dedents after closing a multi-line comment - // e.g. */| - beforeText: /^(\ \ )*\ \*\/\s*$/, - action: { indentAction, removeText: 1 }, - }, - ], - }); -} From 3244c117f8b17c27203a625c5b9e3b281ea5debf Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Mon, 17 Oct 2022 22:50:30 -0700 Subject: [PATCH 0162/1126] add windows one time initialization --- src/tools/miri/src/concurrency/sync.rs | 198 +++++++++++++++++- src/tools/miri/src/concurrency/thread.rs | 8 +- src/tools/miri/src/lib.rs | 2 +- .../miri/src/shims/windows/foreign_items.rs | 12 ++ src/tools/miri/src/shims/windows/sync.rs | 180 +++++++++++++--- 5 files changed, 368 insertions(+), 32 deletions(-) diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 464f452ca769..189209510497 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -7,6 +7,7 @@ use log::trace; use rustc_data_structures::fx::FxHashMap; use rustc_index::vec::{Idx, IndexVec}; +use super::thread::MachineCallback; use super::vector_clock::VClock; use crate::*; @@ -149,13 +150,68 @@ struct FutexWaiter { bitset: u32, } +declare_id!(InitOnceId); + +struct InitOnceWaiter<'mir, 'tcx> { + thread: ThreadId, + callback: Box + 'tcx>, +} + +impl<'mir, 'tcx> std::fmt::Debug for InitOnceWaiter<'mir, 'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InitOnce") + .field("thread", &self.thread) + .field("callback", &"dyn MachineCallback") + .finish() + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +/// The current status of a one time initialization. +pub enum InitOnceStatus { + Uninitialized, + Begun, + Complete, +} + +impl Default for InitOnceStatus { + fn default() -> Self { + Self::Uninitialized + } +} + +/// The one time initialization state. +#[derive(Default, Debug)] +struct InitOnce<'mir, 'tcx> { + status: InitOnceStatus, + waiters: VecDeque>, + data_race: VClock, +} + +impl<'mir, 'tcx> VisitTags for InitOnce<'mir, 'tcx> { + fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { + for waiter in self.waiters.iter() { + waiter.callback.visit_tags(visit); + } + } +} + /// The state of all synchronization variables. #[derive(Default, Debug)] -pub(crate) struct SynchronizationState { +pub(crate) struct SynchronizationState<'mir, 'tcx> { mutexes: IndexVec, rwlocks: IndexVec, condvars: IndexVec, futexes: FxHashMap, + init_onces: IndexVec>, +} + +impl<'mir, 'tcx> VisitTags for SynchronizationState<'mir, 'tcx> { + fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { + for init_once in self.init_onces.iter() { + init_once.visit_tags(visit); + } + } } // Private extension trait for local helper methods @@ -581,4 +637,144 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { futex.waiters.retain(|waiter| waiter.thread != thread); } } + + #[inline] + /// Create state for a new one time initialization. + fn init_once_create(&mut self) -> InitOnceId { + let this = self.eval_context_mut(); + this.machine.threads.sync.init_onces.push(Default::default()) + } + + #[inline] + /// Provides the closure with the next InitOnceId. Creates that InitOnce if the closure returns None, + /// otherwise returns the value from the closure + fn init_once_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, InitOnceId> + where + F: FnOnce( + &mut MiriInterpCx<'mir, 'tcx>, + InitOnceId, + ) -> InterpResult<'tcx, Option>, + { + let this = self.eval_context_mut(); + let next_index = this.machine.threads.sync.init_onces.next_index(); + if let Some(old) = existing(this, next_index)? { + Ok(old) + } else { + let new_index = this.machine.threads.sync.init_onces.push(Default::default()); + assert_eq!(next_index, new_index); + Ok(new_index) + } + } + + #[inline] + fn init_once_status(&mut self, id: InitOnceId) -> InitOnceStatus { + let this = self.eval_context_ref(); + this.machine.threads.sync.init_onces[id].status + } + + #[inline] + /// Put the thread into the queue waiting for the initialization. + fn init_once_enqueue_and_block( + &mut self, + id: InitOnceId, + thread: ThreadId, + callback: Box + 'tcx>, + ) { + let this = self.eval_context_mut(); + let init_once = &mut this.machine.threads.sync.init_onces[id]; + assert_ne!(init_once.status, InitOnceStatus::Complete, "queueing on complete init once"); + init_once.waiters.push_back(InitOnceWaiter { thread, callback }); + this.block_thread(thread); + } + + #[inline] + fn init_once_begin(&mut self, id: InitOnceId) { + let this = self.eval_context_mut(); + let init_once = &mut this.machine.threads.sync.init_onces[id]; + assert_eq!( + init_once.status, + InitOnceStatus::Uninitialized, + "begining already begun or complete init once" + ); + init_once.status = InitOnceStatus::Begun; + } + + #[inline] + fn init_once_complete(&mut self, id: InitOnceId) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let current_thread = this.get_active_thread(); + let init_once = &mut this.machine.threads.sync.init_onces[id]; + + assert_eq!( + init_once.status, + InitOnceStatus::Begun, + "completing already complete or uninit init once" + ); + + init_once.status = InitOnceStatus::Complete; + + // Each complete happens-before the end of the wait + if let Some(data_race) = &this.machine.data_race { + data_race.validate_lock_release(&mut init_once.data_race, current_thread); + } + + // need to take the queue to avoid having `this` be borrowed multiple times + for waiter in std::mem::take(&mut init_once.waiters) { + this.unblock_thread(waiter.thread); + + this.set_active_thread(waiter.thread); + waiter.callback.call(this)?; + this.set_active_thread(current_thread); + + if let Some(data_race) = &this.machine.data_race { + data_race.validate_lock_acquire( + &this.machine.threads.sync.init_onces[id].data_race, + waiter.thread, + ); + } + } + + Ok(()) + } + + #[inline] + fn init_once_fail(&mut self, id: InitOnceId) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let current_thread = this.get_active_thread(); + let init_once = &mut this.machine.threads.sync.init_onces[id]; + assert_eq!( + init_once.status, + InitOnceStatus::Begun, + "failing already completed or uninit init once" + ); + + // Each complete happens-before the end of the wait + if let Some(data_race) = &this.machine.data_race { + data_race.validate_lock_release(&mut init_once.data_race, current_thread); + } + + // the behavior of failing the initialization is left vague by the docs + // it had to be determined experimentally + if let Some(waiter) = init_once.waiters.pop_front() { + // try initializing again on a different thread + init_once.status = InitOnceStatus::Begun; + + this.unblock_thread(waiter.thread); + + this.set_active_thread(waiter.thread); + waiter.callback.call(this)?; + this.set_active_thread(current_thread); + + if let Some(data_race) = &this.machine.data_race { + data_race.validate_lock_acquire( + &this.machine.threads.sync.init_onces[id].data_race, + waiter.thread, + ); + } + } else { + init_once.status = InitOnceStatus::Uninitialized; + } + + Ok(()) + } } diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index ec1da4138d44..3432f10f7a92 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -30,8 +30,7 @@ pub enum SchedulingAction { Stop, } -/// Timeout callbacks can be created by synchronization primitives to tell the -/// scheduler that they should be called once some period of time passes. +/// Trait for callbacks that can be executed when some event happens, such as after a timeout. pub trait MachineCallback<'mir, 'tcx>: VisitTags { fn call(&self, ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>) -> InterpResult<'tcx>; } @@ -269,7 +268,7 @@ pub struct ThreadManager<'mir, 'tcx> { threads: IndexVec>, /// This field is pub(crate) because the synchronization primitives /// (`crate::sync`) need a way to access it. - pub(crate) sync: SynchronizationState, + pub(crate) sync: SynchronizationState<'mir, 'tcx>, /// A mapping from a thread-local static to an allocation id of a thread /// specific allocation. thread_local_alloc_ids: RefCell>>, @@ -303,7 +302,7 @@ impl VisitTags for ThreadManager<'_, '_> { timeout_callbacks, active_thread: _, yield_active_thread: _, - sync: _, + sync, } = self; for thread in threads { @@ -315,6 +314,7 @@ impl VisitTags for ThreadManager<'_, '_> { for callback in timeout_callbacks.values() { callback.callback.visit_tags(visit); } + sync.visit_tags(visit); } } diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 8de0b0413a48..f7c22b76f4f6 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -87,7 +87,7 @@ pub use crate::concurrency::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as DataRaceEvalContextExt, }, - sync::{CondvarId, EvalContextExt as SyncEvalContextExt, MutexId, RwLockId}, + sync::{CondvarId, EvalContextExt as SyncEvalContextExt, InitOnceId, MutexId, RwLockId}, thread::{ EvalContextExt as ThreadsEvalContextExt, SchedulingAction, ThreadId, ThreadManager, ThreadState, Time, diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index b0670358f9ca..d998bdf420f6 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -261,6 +261,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let ret = this.TryAcquireSRWLockShared(ptr)?; this.write_scalar(ret, dest)?; } + "InitOnceBeginInitialize" => { + let [ptr, flags, pending, context] = + this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; + let result = this.InitOnceBeginInitialize(ptr, flags, pending, context)?; + this.write_scalar(result, dest)?; + } + "InitOnceComplete" => { + let [ptr, flags, context] = + this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; + let result = this.InitOnceComplete(ptr, flags, context)?; + this.write_scalar(result, dest)?; + } // Dynamic symbol loading "GetProcAddress" => { diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 88b117c54be7..feed90fad163 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -1,20 +1,24 @@ +use crate::concurrency::sync::InitOnceStatus; +use crate::concurrency::thread::MachineCallback; use crate::*; -// Locks are pointer-sized pieces of data, initialized to 0. -// We use the first 4 bytes to store the RwLockId. +impl<'mir, 'tcx> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + // These synchronization structures are pointer-sized pieces of data, initialized to 0. + // We use the first 4 bytes to store the id. + fn get_or_create_id( + &mut self, + next_id: Scalar, + lock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Option> { + let this = self.eval_context_mut(); + let value_place = this.deref_operand_and_offset(lock_op, 0, this.machine.layouts.u32)?; -fn srwlock_get_or_create_id<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - lock_op: &OpTy<'tcx, Provenance>, -) -> InterpResult<'tcx, RwLockId> { - let value_place = ecx.deref_operand_and_offset(lock_op, 0, ecx.machine.layouts.u32)?; - - ecx.rwlock_get_or_create(|ecx, next_id| { - let (old, success) = ecx + let (old, success) = this .atomic_compare_exchange_scalar( &value_place, - &ImmTy::from_uint(0u32, ecx.machine.layouts.u32), - next_id.to_u32_scalar(), + &ImmTy::from_uint(0u32, this.machine.layouts.u32), + next_id, AtomicRwOrd::Relaxed, AtomicReadOrd::Relaxed, false, @@ -25,17 +29,38 @@ fn srwlock_get_or_create_id<'mir, 'tcx: 'mir>( // Caller of the closure needs to allocate next_id None } else { - Some(RwLockId::from_u32(old.to_u32().expect("layout is u32"))) + Some(old.to_u32().expect("layout is u32")) }) - }) + } + + fn srwlock_get_or_create_id( + &mut self, + lock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, RwLockId> { + let this = self.eval_context_mut(); + this.rwlock_get_or_create(|ecx, next_id| { + Ok(ecx.get_or_create_id(next_id.to_u32_scalar(), lock_op)?.map(RwLockId::from_u32)) + }) + } + + fn init_once_get_or_create_id( + &mut self, + lock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, InitOnceId> { + let this = self.eval_context_mut(); + this.init_once_get_or_create(|ecx, next_id| { + Ok(ecx.get_or_create_id(next_id.to_u32_scalar(), lock_op)?.map(InitOnceId::from_u32)) + }) + } } impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} + +#[allow(non_snake_case)] pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - #[allow(non_snake_case)] fn AcquireSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = srwlock_get_or_create_id(this, lock_op)?; + let id = this.srwlock_get_or_create_id(lock_op)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -54,13 +79,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } - #[allow(non_snake_case)] fn TryAcquireSRWLockExclusive( &mut self, lock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = srwlock_get_or_create_id(this, lock_op)?; + let id = this.srwlock_get_or_create_id(lock_op)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -72,10 +96,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - #[allow(non_snake_case)] fn ReleaseSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = srwlock_get_or_create_id(this, lock_op)?; + let id = this.srwlock_get_or_create_id(lock_op)?; let active_thread = this.get_active_thread(); if !this.rwlock_writer_unlock(id, active_thread) { @@ -88,10 +111,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } - #[allow(non_snake_case)] fn AcquireSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = srwlock_get_or_create_id(this, lock_op)?; + let id = this.srwlock_get_or_create_id(lock_op)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -103,13 +125,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } - #[allow(non_snake_case)] fn TryAcquireSRWLockShared( &mut self, lock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = srwlock_get_or_create_id(this, lock_op)?; + let id = this.srwlock_get_or_create_id(lock_op)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -120,10 +141,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - #[allow(non_snake_case)] fn ReleaseSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = srwlock_get_or_create_id(this, lock_op)?; + let id = this.srwlock_get_or_create_id(lock_op)?; let active_thread = this.get_active_thread(); if !this.rwlock_reader_unlock(id, active_thread) { @@ -135,4 +155,112 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } + + fn InitOnceBeginInitialize( + &mut self, + init_once_op: &OpTy<'tcx, Provenance>, + flags_op: &OpTy<'tcx, Provenance>, + pending_op: &OpTy<'tcx, Provenance>, + context_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + let active_thread = this.get_active_thread(); + + let id = this.init_once_get_or_create_id(init_once_op)?; + let flags = this.read_scalar(flags_op)?.to_u32()?; + let pending_place = this.deref_operand(pending_op)?.into(); + let context = this.read_pointer(context_op)?; + + if flags != 0 { + throw_unsup_format!("unsupported `dwFlags` {flags} in `InitOnceBeginInitialize`"); + } + + if !this.ptr_is_null(context)? { + throw_unsup_format!("non-null `lpContext` in `InitOnceBeginInitialize`"); + } + + struct Callback<'tcx> { + init_once_id: InitOnceId, + pending_place: PlaceTy<'tcx, Provenance>, + } + + impl<'tcx> VisitTags for Callback<'tcx> { + fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { + let Callback { init_once_id: _, pending_place } = self; + pending_place.visit_tags(visit); + } + } + + impl<'mir, 'tcx> MachineCallback<'mir, 'tcx> for Callback<'tcx> { + fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { + let pending = match this.init_once_status(self.init_once_id) { + InitOnceStatus::Uninitialized => + unreachable!("status should have either been set to begun or complete"), + InitOnceStatus::Begun => this.eval_windows("c", "TRUE")?, + InitOnceStatus::Complete => this.eval_windows("c", "FALSE")?, + }; + + this.write_scalar(pending, &self.pending_place)?; + + Ok(()) + } + } + + match this.init_once_status(id) { + InitOnceStatus::Uninitialized => { + this.init_once_begin(id); + this.write_scalar(this.eval_windows("c", "TRUE")?, &pending_place)?; + } + InitOnceStatus::Begun => + this.init_once_enqueue_and_block( + id, + active_thread, + Box::new(Callback { init_once_id: id, pending_place }), + ), + InitOnceStatus::Complete => + this.write_scalar(this.eval_windows("c", "FALSE")?, &pending_place)?, + } + + Ok(Scalar::from_i32(1)) + } + + fn InitOnceComplete( + &mut self, + init_once_op: &OpTy<'tcx, Provenance>, + flags_op: &OpTy<'tcx, Provenance>, + context_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let id = this.init_once_get_or_create_id(init_once_op)?; + let flags = this.read_scalar(flags_op)?.to_u32()?; + let context = this.read_pointer(context_op)?; + + let success = if flags == 0 { + true + } else if flags == this.eval_windows("c", "INIT_ONCE_INIT_FAILED")?.to_u32()? { + false + } else { + throw_unsup_format!("unsupported `dwFlags` {flags} in `InitOnceBeginInitialize`"); + }; + + if !this.ptr_is_null(context)? { + throw_unsup_format!("non-null `lpContext` in `InitOnceBeginInitialize`"); + } + + if this.init_once_status(id) != InitOnceStatus::Begun { + // The docs do not say anything about this case, but it seems better to not allow it. + throw_ub_format!( + "calling InitOnceComplete on a one time initialization that has not begun or is already completed" + ); + } + + if success { + this.init_once_complete(id)?; + } else { + this.init_once_fail(id)?; + } + + Ok(Scalar::from_i32(1)) + } } From 342251cb64dcd048c35e668a54c2f5f7ff1a3ad6 Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Tue, 18 Oct 2022 00:48:40 -0700 Subject: [PATCH 0163/1126] add test for init once --- .../pass/concurrency/windows_init_once.rs | 138 ++++++++++++++++++ .../pass/concurrency/windows_init_once.stdout | 6 + 2 files changed, 144 insertions(+) create mode 100644 src/tools/miri/tests/pass/concurrency/windows_init_once.rs create mode 100644 src/tools/miri/tests/pass/concurrency/windows_init_once.stdout diff --git a/src/tools/miri/tests/pass/concurrency/windows_init_once.rs b/src/tools/miri/tests/pass/concurrency/windows_init_once.rs new file mode 100644 index 000000000000..d3c72c3d028c --- /dev/null +++ b/src/tools/miri/tests/pass/concurrency/windows_init_once.rs @@ -0,0 +1,138 @@ +//@only-target-windows: Uses win32 api functions +// We are making scheduler assumptions here. +//@compile-flags: -Zmiri-preemption-rate=0 + +use std::ffi::c_void; +use std::ptr::null_mut; +use std::thread; + +#[derive(Copy, Clone)] +struct SendPtr(*mut T); + +unsafe impl Send for SendPtr {} + +extern "system" { + fn InitOnceBeginInitialize( + init: *mut *mut c_void, + flags: u32, + pending: *mut i32, + context: *mut c_void, + ) -> i32; + + fn InitOnceComplete(init: *mut *mut c_void, flags: u32, context: *mut c_void) -> i32; +} + +const TRUE: i32 = 1; +const FALSE: i32 = 0; + +const INIT_ONCE_INIT_FAILED: u32 = 4; + +fn single_thread() { + let mut init_once = null_mut(); + let mut pending = 0; + + unsafe { + assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE); + assert_eq!(pending, TRUE); + + assert_eq!(InitOnceComplete(&mut init_once, 0, null_mut()), TRUE); + + assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE); + assert_eq!(pending, FALSE); + } + + let mut init_once = null_mut(); + + unsafe { + assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE); + assert_eq!(pending, TRUE); + + assert_eq!(InitOnceComplete(&mut init_once, INIT_ONCE_INIT_FAILED, null_mut()), TRUE); + + assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE); + assert_eq!(pending, TRUE); + } +} + +fn block_until_complete() { + let mut init_once = null_mut(); + let mut pending = 0; + + unsafe { + assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE); + assert_eq!(pending, TRUE); + } + + let init_once_ptr = SendPtr(&mut init_once); + + let waiter = move || unsafe { + let mut pending = 0; + + assert_eq!(InitOnceBeginInitialize(init_once_ptr.0, 0, &mut pending, null_mut()), TRUE); + assert_eq!(pending, FALSE); + + println!("finished waiting for initialization"); + }; + + let waiter1 = thread::spawn(waiter); + let waiter2 = thread::spawn(waiter); + + // this yield ensures `waiter1` & `waiter2` are blocked on the main thread + thread::yield_now(); + + println!("completing initialization"); + + unsafe { + assert_eq!(InitOnceComplete(init_once_ptr.0, 0, null_mut()), TRUE); + } + + waiter1.join().unwrap(); + waiter2.join().unwrap(); +} + +fn retry_on_fail() { + let mut init_once = null_mut(); + let mut pending = 0; + + unsafe { + assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE); + assert_eq!(pending, TRUE); + } + + let init_once_ptr = SendPtr(&mut init_once); + + let waiter = move || unsafe { + let mut pending = 0; + + assert_eq!(InitOnceBeginInitialize(init_once_ptr.0, 0, &mut pending, null_mut()), TRUE); + + if pending == 1 { + println!("retrying initialization"); + + assert_eq!(InitOnceComplete(init_once_ptr.0, 0, null_mut()), TRUE); + } else { + println!("finished waiting for initialization"); + } + }; + + let waiter1 = thread::spawn(waiter); + let waiter2 = thread::spawn(waiter); + + // this yield ensures `waiter1` & `waiter2` are blocked on the main thread + thread::yield_now(); + + println!("failing initialization"); + + unsafe { + assert_eq!(InitOnceComplete(init_once_ptr.0, INIT_ONCE_INIT_FAILED, null_mut()), TRUE); + } + + waiter1.join().unwrap(); + waiter2.join().unwrap(); +} + +fn main() { + single_thread(); + block_until_complete(); + retry_on_fail(); +} diff --git a/src/tools/miri/tests/pass/concurrency/windows_init_once.stdout b/src/tools/miri/tests/pass/concurrency/windows_init_once.stdout new file mode 100644 index 000000000000..f3d5aad8edce --- /dev/null +++ b/src/tools/miri/tests/pass/concurrency/windows_init_once.stdout @@ -0,0 +1,6 @@ +completing initialization +finished waiting for initialization +finished waiting for initialization +failing initialization +retrying initialization +finished waiting for initialization From 22d06f9754cfa63c1ed557dd4901034ebccb38d2 Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Tue, 18 Oct 2022 16:37:30 -0700 Subject: [PATCH 0164/1126] update rust version --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 21e9d5a05df8..f94611f0d416 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -538f118da1409759ba198acc0ff62070bc6d2dce +a24a020e6d926dffe6b472fc647978f92269504e From 7ca6b175b49410259303827c2101c0a251806830 Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Tue, 18 Oct 2022 17:23:17 -0700 Subject: [PATCH 0165/1126] use Default derive for InitOnceStatus --- src/tools/miri/src/concurrency/sync.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 189209510497..f514c30be4ad 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -166,20 +166,15 @@ impl<'mir, 'tcx> std::fmt::Debug for InitOnceWaiter<'mir, 'tcx> { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq,)] /// The current status of a one time initialization. pub enum InitOnceStatus { + #[default] Uninitialized, Begun, Complete, } -impl Default for InitOnceStatus { - fn default() -> Self { - Self::Uninitialized - } -} - /// The one time initialization state. #[derive(Default, Debug)] struct InitOnce<'mir, 'tcx> { From 7cf32a7d47778080af38bed9e40add6c7c8b1f01 Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Tue, 18 Oct 2022 18:24:16 -0700 Subject: [PATCH 0166/1126] code reuse for sync ids --- src/tools/miri/src/concurrency/sync.rs | 87 ++++++++++++++++- src/tools/miri/src/lib.rs | 2 +- src/tools/miri/src/shims/unix/sync.rs | 115 ++++------------------- src/tools/miri/src/shims/windows/sync.rs | 68 ++------------ 4 files changed, 110 insertions(+), 162 deletions(-) diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index f514c30be4ad..2ec852b413e8 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -11,6 +11,11 @@ use super::thread::MachineCallback; use super::vector_clock::VClock; use crate::*; +pub trait SyncId { + fn from_u32(id: u32) -> Self; + fn to_u32_scalar(&self) -> Scalar; +} + /// We cannot use the `newtype_index!` macro because we have to use 0 as a /// sentinel value meaning that the identifier is not assigned. This is because /// the pthreads static initializers initialize memory with zeros (see the @@ -22,11 +27,14 @@ macro_rules! declare_id { #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] pub struct $name(NonZeroU32); - impl $name { + impl SyncId for $name { // Panics if `id == 0`. - pub fn from_u32(id: u32) -> Self { + fn from_u32(id: u32) -> Self { Self(NonZeroU32::new(id).unwrap()) } + fn to_u32_scalar(&self) -> Scalar { + Scalar::from_u32(self.0.get()) + } } impl Idx for $name { @@ -166,7 +174,7 @@ impl<'mir, 'tcx> std::fmt::Debug for InitOnceWaiter<'mir, 'tcx> { } } -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq,)] +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] /// The current status of a one time initialization. pub enum InitOnceStatus { #[default] @@ -212,6 +220,37 @@ impl<'mir, 'tcx> VisitTags for SynchronizationState<'mir, 'tcx> { // Private extension trait for local helper methods impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + #[inline] + // Miri sync structures contain zero-initialized ids stored at some offset behind a pointer + fn get_or_create_id( + &mut self, + next_id: Id, + lock_op: &OpTy<'tcx, Provenance>, + offset: u64, + ) -> InterpResult<'tcx, Option> { + let this = self.eval_context_mut(); + let value_place = + this.deref_operand_and_offset(lock_op, offset, this.machine.layouts.u32)?; + + let (old, success) = this + .atomic_compare_exchange_scalar( + &value_place, + &ImmTy::from_uint(0u32, this.machine.layouts.u32), + next_id.to_u32_scalar(), + AtomicRwOrd::Relaxed, + AtomicReadOrd::Relaxed, + false, + )? + .to_scalar_pair(); + + Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") { + // Caller of the closure needs to allocate next_id + None + } else { + Some(Id::from_u32(old.to_u32().expect("layout is u32"))) + }) + } + /// Take a reader out of the queue waiting for the lock. /// Returns `true` if some thread got the rwlock. #[inline] @@ -261,6 +300,48 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // situations. impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + fn mutex_get_or_create_id( + &mut self, + lock_op: &OpTy<'tcx, Provenance>, + offset: u64, + ) -> InterpResult<'tcx, MutexId> { + let this = self.eval_context_mut(); + this.mutex_get_or_create(|ecx, next_id| Ok(ecx.get_or_create_id(next_id, lock_op, offset)?)) + } + + fn rwlock_get_or_create_id( + &mut self, + lock_op: &OpTy<'tcx, Provenance>, + offset: u64, + ) -> InterpResult<'tcx, RwLockId> { + let this = self.eval_context_mut(); + this.rwlock_get_or_create( + |ecx, next_id| Ok(ecx.get_or_create_id(next_id, lock_op, offset)?), + ) + } + + fn condvar_get_or_create_id( + &mut self, + lock_op: &OpTy<'tcx, Provenance>, + offset: u64, + ) -> InterpResult<'tcx, CondvarId> { + let this = self.eval_context_mut(); + this.condvar_get_or_create(|ecx, next_id| { + Ok(ecx.get_or_create_id(next_id, lock_op, offset)?) + }) + } + + fn init_once_get_or_create_id( + &mut self, + lock_op: &OpTy<'tcx, Provenance>, + offset: u64, + ) -> InterpResult<'tcx, InitOnceId> { + let this = self.eval_context_mut(); + this.init_once_get_or_create(|ecx, next_id| { + Ok(ecx.get_or_create_id(next_id, lock_op, offset)?) + }) + } + #[inline] /// Create state for a new mutex. fn mutex_create(&mut self) -> MutexId { diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index f7c22b76f4f6..5c4094378ac3 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -87,7 +87,7 @@ pub use crate::concurrency::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as DataRaceEvalContextExt, }, - sync::{CondvarId, EvalContextExt as SyncEvalContextExt, InitOnceId, MutexId, RwLockId}, + sync::{CondvarId, EvalContextExt as SyncEvalContextExt, InitOnceId, MutexId, RwLockId, SyncId}, thread::{ EvalContextExt as ThreadsEvalContextExt, SchedulingAction, ThreadId, ThreadManager, ThreadState, Time, diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 5aafe76ade1d..7857fa4bcc99 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -108,33 +108,6 @@ fn mutex_set_id<'mir, 'tcx: 'mir>( ) } -fn mutex_get_or_create_id<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - mutex_op: &OpTy<'tcx, Provenance>, -) -> InterpResult<'tcx, MutexId> { - let value_place = ecx.deref_operand_and_offset(mutex_op, 4, ecx.machine.layouts.u32)?; - - ecx.mutex_get_or_create(|ecx, next_id| { - let (old, success) = ecx - .atomic_compare_exchange_scalar( - &value_place, - &ImmTy::from_uint(0u32, ecx.machine.layouts.u32), - next_id.to_u32_scalar(), - AtomicRwOrd::Relaxed, - AtomicReadOrd::Relaxed, - false, - )? - .to_scalar_pair(); - - Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") { - // Caller of the closure needs to allocate next_id - None - } else { - Some(MutexId::from_u32(old.to_u32().expect("layout is u32"))) - }) - }) -} - // pthread_rwlock_t is between 32 and 56 bytes, depending on the platform. // Our chosen memory layout for the emulated rwlock (does not have to match the platform layout!): @@ -149,33 +122,6 @@ fn rwlock_get_id<'mir, 'tcx: 'mir>( ecx.read_scalar_at_offset_atomic(rwlock_op, 4, ecx.machine.layouts.u32, AtomicReadOrd::Relaxed) } -fn rwlock_get_or_create_id<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - rwlock_op: &OpTy<'tcx, Provenance>, -) -> InterpResult<'tcx, RwLockId> { - let value_place = ecx.deref_operand_and_offset(rwlock_op, 4, ecx.machine.layouts.u32)?; - - ecx.rwlock_get_or_create(|ecx, next_id| { - let (old, success) = ecx - .atomic_compare_exchange_scalar( - &value_place, - &ImmTy::from_uint(0u32, ecx.machine.layouts.u32), - next_id.to_u32_scalar(), - AtomicRwOrd::Relaxed, - AtomicReadOrd::Relaxed, - false, - )? - .to_scalar_pair(); - - Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") { - // Caller of the closure needs to allocate next_id - None - } else { - Some(RwLockId::from_u32(old.to_u32().expect("layout is u32"))) - }) - }) -} - // pthread_condattr_t // Our chosen memory layout for emulation (does not have to match the platform layout!): @@ -232,33 +178,6 @@ fn cond_set_id<'mir, 'tcx: 'mir>( ) } -fn cond_get_or_create_id<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - cond_op: &OpTy<'tcx, Provenance>, -) -> InterpResult<'tcx, CondvarId> { - let value_place = ecx.deref_operand_and_offset(cond_op, 4, ecx.machine.layouts.u32)?; - - ecx.condvar_get_or_create(|ecx, next_id| { - let (old, success) = ecx - .atomic_compare_exchange_scalar( - &value_place, - &ImmTy::from_uint(0u32, ecx.machine.layouts.u32), - next_id.to_u32_scalar(), - AtomicRwOrd::Relaxed, - AtomicReadOrd::Relaxed, - false, - )? - .to_scalar_pair(); - - Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") { - // Caller of the closure needs to allocate next_id - None - } else { - Some(CondvarId::from_u32(old.to_u32().expect("layout is u32"))) - }) - }) -} - fn cond_get_clock_id<'mir, 'tcx: 'mir>( ecx: &MiriInterpCx<'mir, 'tcx>, cond_op: &OpTy<'tcx, Provenance>, @@ -435,7 +354,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?; - let id = mutex_get_or_create_id(this, mutex_op)?; + let id = this.mutex_get_or_create_id(mutex_op, 4)?; let active_thread = this.get_active_thread(); if this.mutex_is_locked(id) { @@ -475,7 +394,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?; - let id = mutex_get_or_create_id(this, mutex_op)?; + let id = this.mutex_get_or_create_id(mutex_op, 4)?; let active_thread = this.get_active_thread(); if this.mutex_is_locked(id) { @@ -511,7 +430,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?; - let id = mutex_get_or_create_id(this, mutex_op)?; + let id = this.mutex_get_or_create_id(mutex_op, 4)?; let active_thread = this.get_active_thread(); if let Some(_old_locked_count) = this.mutex_unlock(id, active_thread) { @@ -545,7 +464,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = mutex_get_or_create_id(this, mutex_op)?; + let id = this.mutex_get_or_create_id(mutex_op, 4)?; if this.mutex_is_locked(id) { throw_ub_format!("destroyed a locked mutex"); @@ -568,7 +487,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = rwlock_get_or_create_id(this, rwlock_op)?; + let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -586,7 +505,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = rwlock_get_or_create_id(this, rwlock_op)?; + let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -603,7 +522,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = rwlock_get_or_create_id(this, rwlock_op)?; + let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -633,7 +552,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = rwlock_get_or_create_id(this, rwlock_op)?; + let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -650,7 +569,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = rwlock_get_or_create_id(this, rwlock_op)?; + let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; let active_thread = this.get_active_thread(); #[allow(clippy::if_same_then_else)] @@ -669,7 +588,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = rwlock_get_or_create_id(this, rwlock_op)?; + let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; if this.rwlock_is_locked(id) { throw_ub_format!("destroyed a locked rwlock"); @@ -772,7 +691,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = cond_get_or_create_id(this, cond_op)?; + let id = this.condvar_get_or_create_id(cond_op, 4)?; if let Some((thread, mutex)) = this.condvar_signal(id) { post_cond_signal(this, thread, mutex)?; } @@ -785,7 +704,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { cond_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = cond_get_or_create_id(this, cond_op)?; + let id = this.condvar_get_or_create_id(cond_op, 4)?; while let Some((thread, mutex)) = this.condvar_signal(id) { post_cond_signal(this, thread, mutex)?; @@ -801,8 +720,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = cond_get_or_create_id(this, cond_op)?; - let mutex_id = mutex_get_or_create_id(this, mutex_op)?; + let id = this.condvar_get_or_create_id(cond_op, 4)?; + let mutex_id = this.mutex_get_or_create_id(mutex_op, 4)?; let active_thread = this.get_active_thread(); release_cond_mutex_and_block(this, active_thread, mutex_id)?; @@ -822,8 +741,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.check_no_isolation("`pthread_cond_timedwait`")?; - let id = cond_get_or_create_id(this, cond_op)?; - let mutex_id = mutex_get_or_create_id(this, mutex_op)?; + let id = this.condvar_get_or_create_id(cond_op, 4)?; + let mutex_id = this.mutex_get_or_create_id(mutex_op, 4)?; let active_thread = this.get_active_thread(); // Extract the timeout. @@ -899,7 +818,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = cond_get_or_create_id(this, cond_op)?; + let id = this.condvar_get_or_create_id(cond_op, 4)?; if this.condvar_is_awaited(id) { throw_ub_format!("destroying an awaited conditional variable"); } diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index feed90fad163..3eca86d38604 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -2,65 +2,13 @@ use crate::concurrency::sync::InitOnceStatus; use crate::concurrency::thread::MachineCallback; use crate::*; -impl<'mir, 'tcx> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - // These synchronization structures are pointer-sized pieces of data, initialized to 0. - // We use the first 4 bytes to store the id. - fn get_or_create_id( - &mut self, - next_id: Scalar, - lock_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Option> { - let this = self.eval_context_mut(); - let value_place = this.deref_operand_and_offset(lock_op, 0, this.machine.layouts.u32)?; - - let (old, success) = this - .atomic_compare_exchange_scalar( - &value_place, - &ImmTy::from_uint(0u32, this.machine.layouts.u32), - next_id, - AtomicRwOrd::Relaxed, - AtomicReadOrd::Relaxed, - false, - )? - .to_scalar_pair(); - - Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") { - // Caller of the closure needs to allocate next_id - None - } else { - Some(old.to_u32().expect("layout is u32")) - }) - } - - fn srwlock_get_or_create_id( - &mut self, - lock_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, RwLockId> { - let this = self.eval_context_mut(); - this.rwlock_get_or_create(|ecx, next_id| { - Ok(ecx.get_or_create_id(next_id.to_u32_scalar(), lock_op)?.map(RwLockId::from_u32)) - }) - } - - fn init_once_get_or_create_id( - &mut self, - lock_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, InitOnceId> { - let this = self.eval_context_mut(); - this.init_once_get_or_create(|ecx, next_id| { - Ok(ecx.get_or_create_id(next_id.to_u32_scalar(), lock_op)?.map(InitOnceId::from_u32)) - }) - } -} - impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} #[allow(non_snake_case)] pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn AcquireSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.srwlock_get_or_create_id(lock_op)?; + let id = this.rwlock_get_or_create_id(lock_op, 0)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -84,7 +32,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { lock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = this.srwlock_get_or_create_id(lock_op)?; + let id = this.rwlock_get_or_create_id(lock_op, 0)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -98,7 +46,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn ReleaseSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.srwlock_get_or_create_id(lock_op)?; + let id = this.rwlock_get_or_create_id(lock_op, 0)?; let active_thread = this.get_active_thread(); if !this.rwlock_writer_unlock(id, active_thread) { @@ -113,7 +61,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn AcquireSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.srwlock_get_or_create_id(lock_op)?; + let id = this.rwlock_get_or_create_id(lock_op, 0)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -130,7 +78,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { lock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = this.srwlock_get_or_create_id(lock_op)?; + let id = this.rwlock_get_or_create_id(lock_op, 0)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -143,7 +91,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn ReleaseSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.srwlock_get_or_create_id(lock_op)?; + let id = this.rwlock_get_or_create_id(lock_op, 0)?; let active_thread = this.get_active_thread(); if !this.rwlock_reader_unlock(id, active_thread) { @@ -166,7 +114,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let active_thread = this.get_active_thread(); - let id = this.init_once_get_or_create_id(init_once_op)?; + let id = this.init_once_get_or_create_id(init_once_op, 0)?; let flags = this.read_scalar(flags_op)?.to_u32()?; let pending_place = this.deref_operand(pending_op)?.into(); let context = this.read_pointer(context_op)?; @@ -232,7 +180,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = this.init_once_get_or_create_id(init_once_op)?; + let id = this.init_once_get_or_create_id(init_once_op, 0)?; let flags = this.read_scalar(flags_op)?.to_u32()?; let context = this.read_pointer(context_op)?; From b649b96bc82b1691c4948ead6b43b4d058acd539 Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Tue, 18 Oct 2022 23:50:39 -0700 Subject: [PATCH 0167/1126] remove redundant Ok(...?) --- src/tools/miri/src/concurrency/sync.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 2ec852b413e8..02b6c09cd51a 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -315,9 +315,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { offset: u64, ) -> InterpResult<'tcx, RwLockId> { let this = self.eval_context_mut(); - this.rwlock_get_or_create( - |ecx, next_id| Ok(ecx.get_or_create_id(next_id, lock_op, offset)?), - ) + this.rwlock_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset)) } fn condvar_get_or_create_id( @@ -326,9 +324,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { offset: u64, ) -> InterpResult<'tcx, CondvarId> { let this = self.eval_context_mut(); - this.condvar_get_or_create(|ecx, next_id| { - Ok(ecx.get_or_create_id(next_id, lock_op, offset)?) - }) + this.condvar_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset)) } fn init_once_get_or_create_id( @@ -337,9 +333,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { offset: u64, ) -> InterpResult<'tcx, InitOnceId> { let this = self.eval_context_mut(); - this.init_once_get_or_create(|ecx, next_id| { - Ok(ecx.get_or_create_id(next_id, lock_op, offset)?) - }) + this.init_once_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset)) } #[inline] From 7e56e773de74891291eb242f89a07d9c60a80949 Mon Sep 17 00:00:00 2001 From: DrMeepster <19316085+DrMeepster@users.noreply.github.com> Date: Wed, 19 Oct 2022 23:06:01 -0700 Subject: [PATCH 0168/1126] change rust version to fix CI --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index f94611f0d416..0a3daa2d0864 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -a24a020e6d926dffe6b472fc647978f92269504e +edabf59ca4646b3fc1a961c26431215001043f6a From a39629b88f6c1f2044326d94aced180a4015541c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 20 Oct 2022 22:18:59 +0200 Subject: [PATCH 0169/1126] slight refactoring --- src/tools/miri/src/concurrency/init_once.rs | 204 ++++++++++++++++ src/tools/miri/src/concurrency/mod.rs | 2 + src/tools/miri/src/concurrency/sync.rs | 249 ++------------------ src/tools/miri/src/lib.rs | 22 +- src/tools/miri/src/shims/env.rs | 10 +- src/tools/miri/src/shims/unix/sync.rs | 38 +-- src/tools/miri/src/shims/windows/sync.rs | 90 +++---- 7 files changed, 310 insertions(+), 305 deletions(-) create mode 100644 src/tools/miri/src/concurrency/init_once.rs diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs new file mode 100644 index 000000000000..791931901e2a --- /dev/null +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -0,0 +1,204 @@ +use std::collections::VecDeque; +use std::num::NonZeroU32; + +use rustc_index::vec::Idx; + +use super::sync::EvalContextExtPriv; +use super::thread::MachineCallback; +use super::vector_clock::VClock; +use crate::*; + +declare_id!(InitOnceId); + +/// A thread waiting on an InitOnce object. +struct InitOnceWaiter<'mir, 'tcx> { + /// The thread that is waiting. + thread: ThreadId, + /// The callback that should be executed, after the thread has been woken up. + callback: Box + 'tcx>, +} + +impl<'mir, 'tcx> std::fmt::Debug for InitOnceWaiter<'mir, 'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InitOnce") + .field("thread", &self.thread) + .field("callback", &"dyn MachineCallback") + .finish() + } +} + +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] +/// The current status of a one time initialization. +pub enum InitOnceStatus { + #[default] + Uninitialized, + Begun, + Complete, +} + +/// The one time initialization state. +#[derive(Default, Debug)] +pub(super) struct InitOnce<'mir, 'tcx> { + status: InitOnceStatus, + waiters: VecDeque>, + data_race: VClock, +} + +impl<'mir, 'tcx> VisitTags for InitOnce<'mir, 'tcx> { + fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { + for waiter in self.waiters.iter() { + waiter.callback.visit_tags(visit); + } + } +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + fn init_once_get_or_create_id( + &mut self, + lock_op: &OpTy<'tcx, Provenance>, + offset: u64, + ) -> InterpResult<'tcx, InitOnceId> { + let this = self.eval_context_mut(); + this.init_once_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset)) + } + + /// Provides the closure with the next InitOnceId. Creates that InitOnce if the closure returns None, + /// otherwise returns the value from the closure. + #[inline] + fn init_once_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, InitOnceId> + where + F: FnOnce( + &mut MiriInterpCx<'mir, 'tcx>, + InitOnceId, + ) -> InterpResult<'tcx, Option>, + { + let this = self.eval_context_mut(); + let next_index = this.machine.threads.sync.init_onces.next_index(); + if let Some(old) = existing(this, next_index)? { + Ok(old) + } else { + let new_index = this.machine.threads.sync.init_onces.push(Default::default()); + assert_eq!(next_index, new_index); + Ok(new_index) + } + } + + #[inline] + fn init_once_status(&mut self, id: InitOnceId) -> InitOnceStatus { + let this = self.eval_context_ref(); + this.machine.threads.sync.init_onces[id].status + } + + /// Put the thread into the queue waiting for the initialization. + #[inline] + fn init_once_enqueue_and_block( + &mut self, + id: InitOnceId, + thread: ThreadId, + callback: Box + 'tcx>, + ) { + let this = self.eval_context_mut(); + let init_once = &mut this.machine.threads.sync.init_onces[id]; + assert_ne!(init_once.status, InitOnceStatus::Complete, "queueing on complete init once"); + init_once.waiters.push_back(InitOnceWaiter { thread, callback }); + this.block_thread(thread); + } + + /// Begin initializing this InitOnce. Must only be called after checking that it is currently + /// uninitialized. + #[inline] + fn init_once_begin(&mut self, id: InitOnceId) { + let this = self.eval_context_mut(); + let init_once = &mut this.machine.threads.sync.init_onces[id]; + assert_eq!( + init_once.status, + InitOnceStatus::Uninitialized, + "begining already begun or complete init once" + ); + init_once.status = InitOnceStatus::Begun; + } + + #[inline] + fn init_once_complete(&mut self, id: InitOnceId) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let current_thread = this.get_active_thread(); + let init_once = &mut this.machine.threads.sync.init_onces[id]; + + assert_eq!( + init_once.status, + InitOnceStatus::Begun, + "completing already complete or uninit init once" + ); + + init_once.status = InitOnceStatus::Complete; + + // Each complete happens-before the end of the wait + if let Some(data_race) = &this.machine.data_race { + data_race.validate_lock_release(&mut init_once.data_race, current_thread); + } + + // Wake up everyone. + // need to take the queue to avoid having `this` be borrowed multiple times + for waiter in std::mem::take(&mut init_once.waiters) { + // End of the wait happens-before woken-up thread. + if let Some(data_race) = &this.machine.data_race { + data_race.validate_lock_acquire( + &this.machine.threads.sync.init_onces[id].data_race, + waiter.thread, + ); + } + + this.unblock_thread(waiter.thread); + + // Call callback, with the woken-up thread as `current`. + this.set_active_thread(waiter.thread); + waiter.callback.call(this)?; + this.set_active_thread(current_thread); + } + + Ok(()) + } + + #[inline] + fn init_once_fail(&mut self, id: InitOnceId) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let current_thread = this.get_active_thread(); + let init_once = &mut this.machine.threads.sync.init_onces[id]; + assert_eq!( + init_once.status, + InitOnceStatus::Begun, + "failing already completed or uninit init once" + ); + + // Each complete happens-before the end of the wait + // FIXME: should this really induce synchronization? If we think of it as a lock, then yes, + // but the docs don't talk about such details. + if let Some(data_race) = &this.machine.data_race { + data_race.validate_lock_release(&mut init_once.data_race, current_thread); + } + + // Wake up one waiting thread, so they can go ahead and try to init this. + if let Some(waiter) = init_once.waiters.pop_front() { + // End of the wait happens-before woken-up thread. + if let Some(data_race) = &this.machine.data_race { + data_race.validate_lock_acquire( + &this.machine.threads.sync.init_onces[id].data_race, + waiter.thread, + ); + } + + this.unblock_thread(waiter.thread); + + // Call callback, with the woken-up thread as `current`. + this.set_active_thread(waiter.thread); + waiter.callback.call(this)?; + this.set_active_thread(current_thread); + } else { + // Nobody there to take this, so go back to 'uninit' + init_once.status = InitOnceStatus::Uninitialized; + } + + Ok(()) + } +} diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index 61ef3d5640e0..45903107f171 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -1,6 +1,8 @@ pub mod data_race; mod range_object_map; +#[macro_use] pub mod sync; +pub mod init_once; pub mod thread; mod vector_clock; pub mod weak_memory; diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 02b6c09cd51a..e76610e73028 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -7,13 +7,13 @@ use log::trace; use rustc_data_structures::fx::FxHashMap; use rustc_index::vec::{Idx, IndexVec}; -use super::thread::MachineCallback; +use super::init_once::InitOnce; use super::vector_clock::VClock; use crate::*; pub trait SyncId { fn from_u32(id: u32) -> Self; - fn to_u32_scalar(&self) -> Scalar; + fn to_u32(&self) -> u32; } /// We cannot use the `newtype_index!` macro because we have to use 0 as a @@ -32,8 +32,8 @@ macro_rules! declare_id { fn from_u32(id: u32) -> Self { Self(NonZeroU32::new(id).unwrap()) } - fn to_u32_scalar(&self) -> Scalar { - Scalar::from_u32(self.0.get()) + fn to_u32(&self) -> u32 { + self.0.get() } } @@ -158,47 +158,6 @@ struct FutexWaiter { bitset: u32, } -declare_id!(InitOnceId); - -struct InitOnceWaiter<'mir, 'tcx> { - thread: ThreadId, - callback: Box + 'tcx>, -} - -impl<'mir, 'tcx> std::fmt::Debug for InitOnceWaiter<'mir, 'tcx> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("InitOnce") - .field("thread", &self.thread) - .field("callback", &"dyn MachineCallback") - .finish() - } -} - -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] -/// The current status of a one time initialization. -pub enum InitOnceStatus { - #[default] - Uninitialized, - Begun, - Complete, -} - -/// The one time initialization state. -#[derive(Default, Debug)] -struct InitOnce<'mir, 'tcx> { - status: InitOnceStatus, - waiters: VecDeque>, - data_race: VClock, -} - -impl<'mir, 'tcx> VisitTags for InitOnce<'mir, 'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { - for waiter in self.waiters.iter() { - waiter.callback.visit_tags(visit); - } - } -} - /// The state of all synchronization variables. #[derive(Default, Debug)] pub(crate) struct SynchronizationState<'mir, 'tcx> { @@ -206,7 +165,7 @@ pub(crate) struct SynchronizationState<'mir, 'tcx> { rwlocks: IndexVec, condvars: IndexVec, futexes: FxHashMap, - init_onces: IndexVec>, + pub(super) init_onces: IndexVec>, } impl<'mir, 'tcx> VisitTags for SynchronizationState<'mir, 'tcx> { @@ -219,7 +178,9 @@ impl<'mir, 'tcx> VisitTags for SynchronizationState<'mir, 'tcx> { // Private extension trait for local helper methods impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>: + crate::MiriInterpCxExt<'mir, 'tcx> +{ #[inline] // Miri sync structures contain zero-initialized ids stored at some offset behind a pointer fn get_or_create_id( @@ -236,8 +197,8 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .atomic_compare_exchange_scalar( &value_place, &ImmTy::from_uint(0u32, this.machine.layouts.u32), - next_id.to_u32_scalar(), - AtomicRwOrd::Relaxed, + Scalar::from_u32(next_id.to_u32()), + AtomicRwOrd::Relaxed, // deliberately *no* synchronization AtomicReadOrd::Relaxed, false, )? @@ -306,7 +267,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { offset: u64, ) -> InterpResult<'tcx, MutexId> { let this = self.eval_context_mut(); - this.mutex_get_or_create(|ecx, next_id| Ok(ecx.get_or_create_id(next_id, lock_op, offset)?)) + this.mutex_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset)) } fn rwlock_get_or_create_id( @@ -327,22 +288,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.condvar_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset)) } - fn init_once_get_or_create_id( - &mut self, - lock_op: &OpTy<'tcx, Provenance>, - offset: u64, - ) -> InterpResult<'tcx, InitOnceId> { - let this = self.eval_context_mut(); - this.init_once_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset)) - } - - #[inline] - /// Create state for a new mutex. - fn mutex_create(&mut self) -> MutexId { - let this = self.eval_context_mut(); - this.machine.threads.sync.mutexes.push(Default::default()) - } - #[inline] /// Provides the closure with the next MutexId. Creates that mutex if the closure returns None, /// otherwise returns the value from the closure @@ -427,8 +372,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - #[inline] /// Put the thread into the queue waiting for the mutex. + #[inline] fn mutex_enqueue_and_block(&mut self, id: MutexId, thread: ThreadId) { let this = self.eval_context_mut(); assert!(this.mutex_is_locked(id), "queing on unlocked mutex"); @@ -436,16 +381,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.block_thread(thread); } - #[inline] - /// Create state for a new read write lock. - fn rwlock_create(&mut self) -> RwLockId { - let this = self.eval_context_mut(); - this.machine.threads.sync.rwlocks.push(Default::default()) - } - - #[inline] /// Provides the closure with the next RwLockId. Creates that RwLock if the closure returns None, /// otherwise returns the value from the closure + #[inline] fn rwlock_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, RwLockId> where F: FnOnce(&mut MiriInterpCx<'mir, 'tcx>, RwLockId) -> InterpResult<'tcx, Option>, @@ -475,8 +413,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { rwlock.writer.is_some() || rwlock.readers.is_empty().not() } - #[inline] /// Check if write locked. + #[inline] fn rwlock_is_write_locked(&self, id: RwLockId) -> bool { let this = self.eval_context_ref(); let rwlock = &this.machine.threads.sync.rwlocks[id]; @@ -533,8 +471,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { true } - #[inline] /// Put the reader in the queue waiting for the lock and block it. + #[inline] fn rwlock_enqueue_and_block_reader(&mut self, id: RwLockId, reader: ThreadId) { let this = self.eval_context_mut(); assert!(this.rwlock_is_write_locked(id), "read-queueing on not write locked rwlock"); @@ -542,8 +480,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.block_thread(reader); } - #[inline] /// Lock by setting the writer that owns the lock. + #[inline] fn rwlock_writer_lock(&mut self, id: RwLockId, writer: ThreadId) { let this = self.eval_context_mut(); assert!(!this.rwlock_is_locked(id), "the rwlock is already locked"); @@ -555,8 +493,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - #[inline] /// Try to unlock by removing the writer. + #[inline] fn rwlock_writer_unlock(&mut self, id: RwLockId, expected_writer: ThreadId) -> bool { let this = self.eval_context_mut(); let rwlock = &mut this.machine.threads.sync.rwlocks[id]; @@ -593,8 +531,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - #[inline] /// Put the writer in the queue waiting for the lock. + #[inline] fn rwlock_enqueue_and_block_writer(&mut self, id: RwLockId, writer: ThreadId) { let this = self.eval_context_mut(); assert!(this.rwlock_is_locked(id), "write-queueing on unlocked rwlock"); @@ -602,16 +540,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.block_thread(writer); } - #[inline] - /// Create state for a new conditional variable. - fn condvar_create(&mut self) -> CondvarId { - let this = self.eval_context_mut(); - this.machine.threads.sync.condvars.push(Default::default()) - } - - #[inline] /// Provides the closure with the next CondvarId. Creates that Condvar if the closure returns None, /// otherwise returns the value from the closure + #[inline] fn condvar_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, CondvarId> where F: FnOnce( @@ -630,8 +561,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - #[inline] /// Is the conditional variable awaited? + #[inline] fn condvar_is_awaited(&mut self, id: CondvarId) -> bool { let this = self.eval_context_mut(); !this.machine.threads.sync.condvars[id].waiters.is_empty() @@ -707,144 +638,4 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { futex.waiters.retain(|waiter| waiter.thread != thread); } } - - #[inline] - /// Create state for a new one time initialization. - fn init_once_create(&mut self) -> InitOnceId { - let this = self.eval_context_mut(); - this.machine.threads.sync.init_onces.push(Default::default()) - } - - #[inline] - /// Provides the closure with the next InitOnceId. Creates that InitOnce if the closure returns None, - /// otherwise returns the value from the closure - fn init_once_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, InitOnceId> - where - F: FnOnce( - &mut MiriInterpCx<'mir, 'tcx>, - InitOnceId, - ) -> InterpResult<'tcx, Option>, - { - let this = self.eval_context_mut(); - let next_index = this.machine.threads.sync.init_onces.next_index(); - if let Some(old) = existing(this, next_index)? { - Ok(old) - } else { - let new_index = this.machine.threads.sync.init_onces.push(Default::default()); - assert_eq!(next_index, new_index); - Ok(new_index) - } - } - - #[inline] - fn init_once_status(&mut self, id: InitOnceId) -> InitOnceStatus { - let this = self.eval_context_ref(); - this.machine.threads.sync.init_onces[id].status - } - - #[inline] - /// Put the thread into the queue waiting for the initialization. - fn init_once_enqueue_and_block( - &mut self, - id: InitOnceId, - thread: ThreadId, - callback: Box + 'tcx>, - ) { - let this = self.eval_context_mut(); - let init_once = &mut this.machine.threads.sync.init_onces[id]; - assert_ne!(init_once.status, InitOnceStatus::Complete, "queueing on complete init once"); - init_once.waiters.push_back(InitOnceWaiter { thread, callback }); - this.block_thread(thread); - } - - #[inline] - fn init_once_begin(&mut self, id: InitOnceId) { - let this = self.eval_context_mut(); - let init_once = &mut this.machine.threads.sync.init_onces[id]; - assert_eq!( - init_once.status, - InitOnceStatus::Uninitialized, - "begining already begun or complete init once" - ); - init_once.status = InitOnceStatus::Begun; - } - - #[inline] - fn init_once_complete(&mut self, id: InitOnceId) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let current_thread = this.get_active_thread(); - let init_once = &mut this.machine.threads.sync.init_onces[id]; - - assert_eq!( - init_once.status, - InitOnceStatus::Begun, - "completing already complete or uninit init once" - ); - - init_once.status = InitOnceStatus::Complete; - - // Each complete happens-before the end of the wait - if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_release(&mut init_once.data_race, current_thread); - } - - // need to take the queue to avoid having `this` be borrowed multiple times - for waiter in std::mem::take(&mut init_once.waiters) { - this.unblock_thread(waiter.thread); - - this.set_active_thread(waiter.thread); - waiter.callback.call(this)?; - this.set_active_thread(current_thread); - - if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_acquire( - &this.machine.threads.sync.init_onces[id].data_race, - waiter.thread, - ); - } - } - - Ok(()) - } - - #[inline] - fn init_once_fail(&mut self, id: InitOnceId) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let current_thread = this.get_active_thread(); - let init_once = &mut this.machine.threads.sync.init_onces[id]; - assert_eq!( - init_once.status, - InitOnceStatus::Begun, - "failing already completed or uninit init once" - ); - - // Each complete happens-before the end of the wait - if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_release(&mut init_once.data_race, current_thread); - } - - // the behavior of failing the initialization is left vague by the docs - // it had to be determined experimentally - if let Some(waiter) = init_once.waiters.pop_front() { - // try initializing again on a different thread - init_once.status = InitOnceStatus::Begun; - - this.unblock_thread(waiter.thread); - - this.set_active_thread(waiter.thread); - waiter.callback.call(this)?; - this.set_active_thread(current_thread); - - if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_acquire( - &this.machine.threads.sync.init_onces[id].data_race, - waiter.thread, - ); - } - } else { - init_once.status = InitOnceStatus::Uninitialized; - } - - Ok(()) - } } diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 5c4094378ac3..479353bb9839 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -83,34 +83,28 @@ pub use crate::shims::EvalContextExt as _; pub use crate::clock::{Clock, Instant}; pub use crate::concurrency::{ - data_race::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, - EvalContextExt as DataRaceEvalContextExt, - }, - sync::{CondvarId, EvalContextExt as SyncEvalContextExt, InitOnceId, MutexId, RwLockId, SyncId}, - thread::{ - EvalContextExt as ThreadsEvalContextExt, SchedulingAction, ThreadId, ThreadManager, - ThreadState, Time, - }, + data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _}, + init_once::{EvalContextExt as _, InitOnceId}, + sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SyncId}, + thread::{EvalContextExt as _, SchedulingAction, ThreadId, ThreadManager, ThreadState, Time}, }; pub use crate::diagnostics::{ - report_error, EvalContextExt as DiagnosticsEvalContextExt, NonHaltingDiagnostic, - TerminationInfo, + report_error, EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, }; pub use crate::eval::{ create_ecx, eval_entry, AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, RejectOpWith, }; -pub use crate::helpers::{CurrentSpan, EvalContextExt as HelpersEvalContextExt}; +pub use crate::helpers::{CurrentSpan, EvalContextExt as _}; pub use crate::intptrcast::ProvenanceMode; pub use crate::machine::{ AllocExtra, FrameData, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind, Provenance, ProvenanceExtra, PAGE_SIZE, STACK_ADDR, STACK_SIZE, }; pub use crate::mono_hash_map::MonoHashMap; -pub use crate::operator::EvalContextExt as OperatorEvalContextExt; +pub use crate::operator::EvalContextExt as _; pub use crate::range_map::RangeMap; pub use crate::stacked_borrows::{ - CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, SbTag, Stack, Stacks, + CallId, EvalContextExt as _, Item, Permission, SbTag, Stack, Stacks, }; pub use crate::tag_gc::{EvalContextExt as _, VisitTags}; diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index 036f06bde0ae..bf6c1f875629 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -274,7 +274,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; this.update_environ()?; } - Ok(Scalar::from_i32(1)) // return non-zero on success + Ok(this.eval_windows("c", "TRUE")?) } else { let value = this.read_os_str_from_wide_str(value_ptr)?; let var_ptr = alloc_env_var_as_wide_str(&name, &value, this)?; @@ -282,7 +282,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; } this.update_environ()?; - Ok(Scalar::from_i32(1)) // return non-zero on success + Ok(this.eval_windows("c", "TRUE")?) } } @@ -411,14 +411,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; - return Ok(Scalar::from_i32(0)); + return this.eval_windows("c", "FALSE"); } match env::set_current_dir(path) { - Ok(()) => Ok(Scalar::from_i32(1)), + Ok(()) => this.eval_windows("c", "TRUE"), Err(e) => { this.set_last_error_from_io_error(e.kind())?; - Ok(Scalar::from_i32(0)) + this.eval_windows("c", "FALSE") } } } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 7857fa4bcc99..3e1e34c5dbe7 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -19,6 +19,10 @@ use crate::*; /// in `pthread_mutexattr_settype` function. const PTHREAD_MUTEX_NORMAL_FLAG: i32 = 0x8000000; +const MUTEX_ID_OFFSET: u64 = 4; +const RWLOCK_ID_OFFSET: u64 = 4; +const CONDVAR_ID_OFFSET: u64 = 4; + fn is_mutex_kind_default<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, kind: Scalar, @@ -354,7 +358,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?; - let id = this.mutex_get_or_create_id(mutex_op, 4)?; + let id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?; let active_thread = this.get_active_thread(); if this.mutex_is_locked(id) { @@ -394,7 +398,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?; - let id = this.mutex_get_or_create_id(mutex_op, 4)?; + let id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?; let active_thread = this.get_active_thread(); if this.mutex_is_locked(id) { @@ -430,7 +434,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?; - let id = this.mutex_get_or_create_id(mutex_op, 4)?; + let id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?; let active_thread = this.get_active_thread(); if let Some(_old_locked_count) = this.mutex_unlock(id, active_thread) { @@ -464,7 +468,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = this.mutex_get_or_create_id(mutex_op, 4)?; + let id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?; if this.mutex_is_locked(id) { throw_ub_format!("destroyed a locked mutex"); @@ -487,7 +491,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; + let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -505,7 +509,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; + let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -522,7 +526,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; + let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -552,7 +556,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; + let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -569,7 +573,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; + let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?; let active_thread = this.get_active_thread(); #[allow(clippy::if_same_then_else)] @@ -588,7 +592,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(rwlock_op, 4)?; + let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?; if this.rwlock_is_locked(id) { throw_ub_format!("destroyed a locked rwlock"); @@ -691,7 +695,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = this.condvar_get_or_create_id(cond_op, 4)?; + let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?; if let Some((thread, mutex)) = this.condvar_signal(id) { post_cond_signal(this, thread, mutex)?; } @@ -704,7 +708,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { cond_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = this.condvar_get_or_create_id(cond_op, 4)?; + let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?; while let Some((thread, mutex)) = this.condvar_signal(id) { post_cond_signal(this, thread, mutex)?; @@ -720,8 +724,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = this.condvar_get_or_create_id(cond_op, 4)?; - let mutex_id = this.mutex_get_or_create_id(mutex_op, 4)?; + let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?; + let mutex_id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?; let active_thread = this.get_active_thread(); release_cond_mutex_and_block(this, active_thread, mutex_id)?; @@ -741,8 +745,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.check_no_isolation("`pthread_cond_timedwait`")?; - let id = this.condvar_get_or_create_id(cond_op, 4)?; - let mutex_id = this.mutex_get_or_create_id(mutex_op, 4)?; + let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?; + let mutex_id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?; let active_thread = this.get_active_thread(); // Extract the timeout. @@ -818,7 +822,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let id = this.condvar_get_or_create_id(cond_op, 4)?; + let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?; if this.condvar_is_awaited(id) { throw_ub_format!("destroying an awaited conditional variable"); } diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 3eca86d38604..8064ca566755 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -1,14 +1,17 @@ -use crate::concurrency::sync::InitOnceStatus; +use crate::concurrency::init_once::InitOnceStatus; use crate::concurrency::thread::MachineCallback; use crate::*; +const SRWLOCK_ID_OFFSET: u64 = 0; +const INIT_ONCE_ID_OFFSET: u64 = 0; + impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} #[allow(non_snake_case)] pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn AcquireSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, 0)?; + let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -32,7 +35,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { lock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, 0)?; + let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { @@ -46,7 +49,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn ReleaseSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, 0)?; + let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; let active_thread = this.get_active_thread(); if !this.rwlock_writer_unlock(id, active_thread) { @@ -61,7 +64,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn AcquireSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, 0)?; + let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -78,7 +81,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { lock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, 0)?; + let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { @@ -91,7 +94,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn ReleaseSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let id = this.rwlock_get_or_create_id(lock_op, 0)?; + let id = this.rwlock_get_or_create_id(lock_op, SRWLOCK_ID_OFFSET)?; let active_thread = this.get_active_thread(); if !this.rwlock_reader_unlock(id, active_thread) { @@ -114,7 +117,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let active_thread = this.get_active_thread(); - let id = this.init_once_get_or_create_id(init_once_op, 0)?; + let id = this.init_once_get_or_create_id(init_once_op, INIT_ONCE_ID_OFFSET)?; let flags = this.read_scalar(flags_op)?.to_u32()?; let pending_place = this.deref_operand(pending_op)?.into(); let context = this.read_pointer(context_op)?; @@ -127,49 +130,56 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("non-null `lpContext` in `InitOnceBeginInitialize`"); } - struct Callback<'tcx> { - init_once_id: InitOnceId, - pending_place: PlaceTy<'tcx, Provenance>, - } - - impl<'tcx> VisitTags for Callback<'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { - let Callback { init_once_id: _, pending_place } = self; - pending_place.visit_tags(visit); - } - } - - impl<'mir, 'tcx> MachineCallback<'mir, 'tcx> for Callback<'tcx> { - fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - let pending = match this.init_once_status(self.init_once_id) { - InitOnceStatus::Uninitialized => - unreachable!("status should have either been set to begun or complete"), - InitOnceStatus::Begun => this.eval_windows("c", "TRUE")?, - InitOnceStatus::Complete => this.eval_windows("c", "FALSE")?, - }; - - this.write_scalar(pending, &self.pending_place)?; - - Ok(()) - } - } - match this.init_once_status(id) { InitOnceStatus::Uninitialized => { this.init_once_begin(id); this.write_scalar(this.eval_windows("c", "TRUE")?, &pending_place)?; } - InitOnceStatus::Begun => + InitOnceStatus::Begun => { + // Someone else is already on it. + // Block this thread until they are done. + // When we are woken up, set the `pending` flag accordingly. + struct Callback<'tcx> { + init_once_id: InitOnceId, + pending_place: PlaceTy<'tcx, Provenance>, + } + + impl<'tcx> VisitTags for Callback<'tcx> { + fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { + let Callback { init_once_id: _, pending_place } = self; + pending_place.visit_tags(visit); + } + } + + impl<'mir, 'tcx> MachineCallback<'mir, 'tcx> for Callback<'tcx> { + fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { + let pending = match this.init_once_status(self.init_once_id) { + InitOnceStatus::Uninitialized => + unreachable!( + "status should have either been set to begun or complete" + ), + InitOnceStatus::Begun => this.eval_windows("c", "TRUE")?, + InitOnceStatus::Complete => this.eval_windows("c", "FALSE")?, + }; + + this.write_scalar(pending, &self.pending_place)?; + + Ok(()) + } + } + this.init_once_enqueue_and_block( id, active_thread, Box::new(Callback { init_once_id: id, pending_place }), - ), + ) + } InitOnceStatus::Complete => this.write_scalar(this.eval_windows("c", "FALSE")?, &pending_place)?, } - Ok(Scalar::from_i32(1)) + // This always succeeds (even if the thread is blocked, we will succeed if we ever unblock). + this.eval_windows("c", "TRUE") } fn InitOnceComplete( @@ -180,7 +190,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let id = this.init_once_get_or_create_id(init_once_op, 0)?; + let id = this.init_once_get_or_create_id(init_once_op, INIT_ONCE_ID_OFFSET)?; let flags = this.read_scalar(flags_op)?.to_u32()?; let context = this.read_pointer(context_op)?; @@ -209,6 +219,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.init_once_fail(id)?; } - Ok(Scalar::from_i32(1)) + this.eval_windows("c", "TRUE") } } From 770538ef9b9a93038983ee5d82e07754c279df06 Mon Sep 17 00:00:00 2001 From: Nicolas Barrios Date: Wed, 19 Oct 2022 22:06:50 -0400 Subject: [PATCH 0170/1126] Add fix suggestions for E0199, E0200, and E0569 --- .../src/coherence/unsafety.rs | 30 +++++++++++++++++++ .../coherence-default-trait-impl.stderr | 12 ++++++++ ...dropck-eyepatch-implies-unsafe-impl.stderr | 12 ++++++++ src/test/ui/error-codes/E0199.stderr | 6 ++++ src/test/ui/error-codes/E0200.stderr | 6 ++++ .../ui/traits/safety-trait-impl-cc.stderr | 6 ++++ src/test/ui/traits/safety-trait-impl.stderr | 12 ++++++++ 7 files changed, 84 insertions(+) diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs index e45fb5fe41c0..25b2ed76adf2 100644 --- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -26,6 +26,12 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "implementing the trait `{}` is not unsafe", trait_ref.print_only_trait_path() ) + .span_suggestion_verbose( + item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)), + "remove `unsafe` from this trait implementation", + "", + rustc_errors::Applicability::MachineApplicable, + ) .emit(); } @@ -37,6 +43,18 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "the trait `{}` requires an `unsafe impl` declaration", trait_ref.print_only_trait_path() ) + .note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ + Review the trait documentation and make sure this implementation \ + upholds those invariants before adding the `unsafe` keyword", + trait_ref.print_only_trait_path() + )) + .span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) .emit(); } @@ -48,6 +66,18 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "requires an `unsafe impl` declaration due to `#[{}]` attribute", attr_name ) + .note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ + Review the trait documentation and make sure this implementation \ + upholds those invariants before adding the `unsafe` keyword", + trait_ref.print_only_trait_path() + )) + .span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) .emit(); } diff --git a/src/test/ui/coherence/coherence-default-trait-impl.stderr b/src/test/ui/coherence/coherence-default-trait-impl.stderr index b08ccb087d91..632018782725 100644 --- a/src/test/ui/coherence/coherence-default-trait-impl.stderr +++ b/src/test/ui/coherence/coherence-default-trait-impl.stderr @@ -3,12 +3,24 @@ error[E0199]: implementing the trait `MySafeTrait` is not unsafe | LL | unsafe impl MySafeTrait for Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove `unsafe` from this trait implementation + | +LL - unsafe impl MySafeTrait for Foo {} +LL + impl MySafeTrait for Foo {} + | error[E0200]: the trait `MyUnsafeTrait` requires an `unsafe impl` declaration --> $DIR/coherence-default-trait-impl.rs:13:1 | LL | impl MyUnsafeTrait for Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the trait `MyUnsafeTrait` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword +help: add `unsafe` to this trait implementation + | +LL | unsafe impl MyUnsafeTrait for Foo {} + | ++++++ error: aborting due to 2 previous errors diff --git a/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr index 49e55be1b49e..82169ee01bed 100644 --- a/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr +++ b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr @@ -8,6 +8,12 @@ LL | | // (unsafe to access self.1 due to #[may_dangle] on A) LL | | fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); } LL | | } | |_^ + | + = note: the trait `Drop` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword +help: add `unsafe` to this trait implementation + | +LL | unsafe impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt { + | ++++++ error[E0569]: requires an `unsafe impl` declaration due to `#[may_dangle]` attribute --> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:27:1 @@ -19,6 +25,12 @@ LL | | // (unsafe to access self.1 due to #[may_dangle] on 'a) LL | | fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); } LL | | } | |_^ + | + = note: the trait `Drop` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword +help: add `unsafe` to this trait implementation + | +LL | unsafe impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> { + | ++++++ error: aborting due to 2 previous errors diff --git a/src/test/ui/error-codes/E0199.stderr b/src/test/ui/error-codes/E0199.stderr index 3632d26cd32e..99d808c0d4b1 100644 --- a/src/test/ui/error-codes/E0199.stderr +++ b/src/test/ui/error-codes/E0199.stderr @@ -3,6 +3,12 @@ error[E0199]: implementing the trait `Bar` is not unsafe | LL | unsafe impl Bar for Foo { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove `unsafe` from this trait implementation + | +LL - unsafe impl Bar for Foo { } +LL + impl Bar for Foo { } + | error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0200.stderr b/src/test/ui/error-codes/E0200.stderr index 677271aad445..1fd86aecee17 100644 --- a/src/test/ui/error-codes/E0200.stderr +++ b/src/test/ui/error-codes/E0200.stderr @@ -3,6 +3,12 @@ error[E0200]: the trait `Bar` requires an `unsafe impl` declaration | LL | impl Bar for Foo { } | ^^^^^^^^^^^^^^^^^^^^ + | + = note: the trait `Bar` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword +help: add `unsafe` to this trait implementation + | +LL | unsafe impl Bar for Foo { } + | ++++++ error: aborting due to previous error diff --git a/src/test/ui/traits/safety-trait-impl-cc.stderr b/src/test/ui/traits/safety-trait-impl-cc.stderr index 5a0f8d3b8cac..0b1fb30478ff 100644 --- a/src/test/ui/traits/safety-trait-impl-cc.stderr +++ b/src/test/ui/traits/safety-trait-impl-cc.stderr @@ -7,6 +7,12 @@ LL | | panic!(); LL | | } LL | | } | |_^ + | + = note: the trait `Foo` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword +help: add `unsafe` to this trait implementation + | +LL | unsafe impl lib::Foo for Bar { + | ++++++ error: aborting due to previous error diff --git a/src/test/ui/traits/safety-trait-impl.stderr b/src/test/ui/traits/safety-trait-impl.stderr index fc0f6c693089..721e2b48b954 100644 --- a/src/test/ui/traits/safety-trait-impl.stderr +++ b/src/test/ui/traits/safety-trait-impl.stderr @@ -3,12 +3,24 @@ error[E0200]: the trait `UnsafeTrait` requires an `unsafe impl` declaration | LL | impl UnsafeTrait for u16 { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the trait `UnsafeTrait` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword +help: add `unsafe` to this trait implementation + | +LL | unsafe impl UnsafeTrait for u16 { } + | ++++++ error[E0199]: implementing the trait `SafeTrait` is not unsafe --> $DIR/safety-trait-impl.rs:16:1 | LL | unsafe impl SafeTrait for u32 { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove `unsafe` from this trait implementation + | +LL - unsafe impl SafeTrait for u32 { } +LL + impl SafeTrait for u32 { } + | error: aborting due to 2 previous errors From 76c554eac22112968da92699c64c260700bcd663 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 21 Oct 2022 10:22:15 +0200 Subject: [PATCH 0171/1126] rustup --- src/tools/miri/bench-cargo-miri/serde2/src/main.rs | 1 + src/tools/miri/rust-version | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/bench-cargo-miri/serde2/src/main.rs b/src/tools/miri/bench-cargo-miri/serde2/src/main.rs index c81b32c13fef..83c04b9dd5f5 100644 --- a/src/tools/miri/bench-cargo-miri/serde2/src/main.rs +++ b/src/tools/miri/bench-cargo-miri/serde2/src/main.rs @@ -5,6 +5,7 @@ use serde::Deserialize; use std::thread; #[derive(Deserialize)] +#[allow(unused)] struct DeriveStruct { buffer: Vec, } diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 0a3daa2d0864..eb0301bee2ac 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -edabf59ca4646b3fc1a961c26431215001043f6a +b1ab3b738ac718da74cd4aa0bb7f362d0adbdf84 From 6a065f78c43c8ee8f0a9a0607eb33f8e23ed69f9 Mon Sep 17 00:00:00 2001 From: Kitsu Date: Fri, 21 Oct 2022 12:50:28 +0300 Subject: [PATCH 0172/1126] Fix unreachable_pub suggestion for enum with fields --- compiler/rustc_lint/src/builtin.rs | 8 ++++++-- src/test/ui/lint/issue-103317.rs | 13 +++++++++++++ src/test/ui/lint/issue-103317.stderr | 17 +++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/lint/issue-103317.rs create mode 100644 src/test/ui/lint/issue-103317.stderr diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 886e25f2d788..dfd664521899 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -40,7 +40,7 @@ use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, Gate use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; -use rustc_hir::{ForeignItemKind, GenericParamKind, HirId, PatKind, PredicateOrigin}; +use rustc_hir::{ForeignItemKind, GenericParamKind, HirId, Node, PatKind, PredicateOrigin}; use rustc_index::vec::Idx; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::{LayoutError, LayoutOf}; @@ -1430,7 +1430,11 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - let def_id = cx.tcx.hir().local_def_id(field.hir_id); + let map = cx.tcx.hir(); + let def_id = map.local_def_id(field.hir_id); + if matches!(map.get(map.get_parent_node(field.hir_id)), Node::Variant(_)) { + return; + } self.perform_lint(cx, "field", def_id, field.vis_span, false); } diff --git a/src/test/ui/lint/issue-103317.rs b/src/test/ui/lint/issue-103317.rs new file mode 100644 index 000000000000..b39b441a0cf3 --- /dev/null +++ b/src/test/ui/lint/issue-103317.rs @@ -0,0 +1,13 @@ +// check-pass + +#[warn(unreachable_pub)] +#[allow(unused)] +mod inner { + pub enum T { + //~^ WARN unreachable `pub` item + A(u8), + X { a: f32, b: () }, + } +} + +fn main() {} diff --git a/src/test/ui/lint/issue-103317.stderr b/src/test/ui/lint/issue-103317.stderr new file mode 100644 index 000000000000..9fdd6a570e6f --- /dev/null +++ b/src/test/ui/lint/issue-103317.stderr @@ -0,0 +1,17 @@ +warning: unreachable `pub` item + --> $DIR/issue-103317.rs:6:5 + | +LL | pub enum T { + | ---^^^^^^^ + | | + | help: consider restricting its visibility: `pub(crate)` + | + = help: or consider exporting it for use by other crates +note: the lint level is defined here + --> $DIR/issue-103317.rs:3:8 + | +LL | #[warn(unreachable_pub)] + | ^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + From bb911ce32aeef2d691af993d0a1696d8402998a9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 21 Oct 2022 12:19:59 +0200 Subject: [PATCH 0173/1126] add always-failing GetFileInformationByHandleEx stub --- .../miri/src/shims/windows/foreign_items.rs | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index d998bdf420f6..fa8eaaed58c8 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -343,16 +343,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // FIXME: we should set last_error, but to what? this.write_null(dest)?; } - "GetConsoleMode" => { - // Windows "isatty" (in libtest) needs this, so we fake it. - let [console, mode] = - this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - this.read_scalar(console)?.to_machine_isize(this)?; - this.deref_operand(mode)?; - // Indicate an error. - // FIXME: we should set last_error, but to what? - this.write_null(dest)?; - } "GetStdHandle" => { let [which] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; @@ -404,14 +394,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; // Just fake a HANDLE // It's fine to not use the Handle type here because its a stub - this.write_scalar(Scalar::from_machine_isize(1, this), dest)?; + this.write_int(1, dest)?; } "GetModuleHandleA" if this.frame_in_std() => { #[allow(non_snake_case)] let [_lpModuleName] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; // We need to return something non-null here to make `compat_fn!` work. - this.write_scalar(Scalar::from_machine_isize(1, this), dest)?; + this.write_int(1, dest)?; } "SetConsoleTextAttribute" if this.frame_in_std() => { #[allow(non_snake_case)] @@ -420,24 +410,39 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Pretend these does not exist / nothing happened, by returning zero. this.write_null(dest)?; } + "GetConsoleMode" if this.frame_in_std() => { + let [console, mode] = + this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; + this.read_scalar(console)?.to_machine_isize(this)?; + this.deref_operand(mode)?; + // Indicate an error. + this.write_null(dest)?; + } + "GetFileInformationByHandleEx" if this.frame_in_std() => { + #[allow(non_snake_case)] + let [_hFile, _FileInformationClass, _lpFileInformation, _dwBufferSize] = + this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; + // Just make it fail. + this.write_null(dest)?; + } "AddVectoredExceptionHandler" if this.frame_in_std() => { #[allow(non_snake_case)] let [_First, _Handler] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; // Any non zero value works for the stdlib. This is just used for stack overflows anyway. - this.write_scalar(Scalar::from_machine_usize(1, this), dest)?; + this.write_int(1, dest)?; } "SetThreadStackGuarantee" if this.frame_in_std() => { #[allow(non_snake_case)] let [_StackSizeInBytes] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; // Any non zero value works for the stdlib. This is just used for stack overflows anyway. - this.write_scalar(Scalar::from_u32(1), dest)?; + this.write_int(1, dest)?; } "GetCurrentProcessId" if this.frame_in_std() => { let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let result = this.GetCurrentProcessId()?; - this.write_scalar(Scalar::from_u32(result), dest)?; + this.write_int(result, dest)?; } // this is only callable from std because we know that std ignores the return value "SwitchToThread" if this.frame_in_std() => { From 615b7617ed3ba1ea44575a9072b17a4bdfb565b2 Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 21 Oct 2022 13:48:41 +0200 Subject: [PATCH 0174/1126] `ref_option_ref` do not lint when inner reference is mutable As it makes the `Option` Non Copy --- clippy_lints/src/ref_option_ref.rs | 3 ++- tests/ui/ref_option_ref.rs | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 42514f861be1..f21b3ea6c3b0 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -52,7 +52,8 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { GenericArg::Type(inner_ty) => Some(inner_ty), _ => None, }); - if let TyKind::Rptr(_, _) = inner_ty.kind; + if let TyKind::Rptr(_, ref inner_mut_ty) = inner_ty.kind; + if inner_mut_ty.mutbl == Mutability::Not; then { span_lint_and_sugg( diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 2df45c927d71..e487799e1522 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -45,3 +45,8 @@ impl RefOptTrait for u32 { fn main() { let x: &Option<&u32> = &None; } + +fn issue9682(arg: &Option<&mut String>) { + // Should not lint, as the inner ref is mutable making it non `Copy` + println!("{arg:?}"); +} From 9a1edc3c061021a48ffbd149340d7ce059faa97c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 21 Oct 2022 14:05:38 +0200 Subject: [PATCH 0175/1126] use is_terminal to implement isatty --- src/tools/miri/src/lib.rs | 1 + .../miri/src/shims/unix/foreign_items.rs | 2 +- src/tools/miri/src/shims/unix/fs.rs | 68 +++++++++++-------- 3 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 479353bb9839..97271c33a2e6 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -9,6 +9,7 @@ #![feature(is_some_and)] #![feature(nonzero_ops)] #![feature(local_key_cell_methods)] +#![feature(is_terminal)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index c21e0441cacf..44a433df1e9c 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -452,7 +452,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "isatty" => { let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.isatty(fd)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "pthread_atfork" => { let [prepare, parent, child] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index ed68976773d1..0610f65db113 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -4,7 +4,7 @@ use std::convert::TryInto; use std::fs::{ read_dir, remove_dir, remove_file, rename, DirBuilder, File, FileType, OpenOptions, ReadDir, }; -use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; +use std::io::{self, ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::time::SystemTime; @@ -65,6 +65,8 @@ trait FileDescriptor: std::fmt::Debug { fn dup(&mut self) -> io::Result>; + fn is_tty(&self) -> bool; + #[cfg(unix)] fn as_unix_host_fd(&self) -> Option { None @@ -143,6 +145,10 @@ impl FileDescriptor for FileHandle { use std::os::unix::io::AsRawFd; Some(self.file.as_raw_fd()) } + + fn is_tty(&self) -> bool { + self.file.is_terminal() + } } impl FileDescriptor for io::Stdin { @@ -170,6 +176,10 @@ impl FileDescriptor for io::Stdin { fn as_unix_host_fd(&self) -> Option { Some(libc::STDIN_FILENO) } + + fn is_tty(&self) -> bool { + self.is_terminal() + } } impl FileDescriptor for io::Stdout { @@ -202,6 +212,10 @@ impl FileDescriptor for io::Stdout { fn as_unix_host_fd(&self) -> Option { Some(libc::STDOUT_FILENO) } + + fn is_tty(&self) -> bool { + self.is_terminal() + } } impl FileDescriptor for io::Stderr { @@ -227,12 +241,16 @@ impl FileDescriptor for io::Stderr { fn as_unix_host_fd(&self) -> Option { Some(libc::STDERR_FILENO) } + + fn is_tty(&self) -> bool { + self.is_terminal() + } } #[derive(Debug)] -struct DummyOutput; +struct NullOutput; -impl FileDescriptor for DummyOutput { +impl FileDescriptor for NullOutput { fn name(&self) -> &'static str { "stderr and stdout" } @@ -247,7 +265,11 @@ impl FileDescriptor for DummyOutput { } fn dup(&mut self) -> io::Result> { - Ok(Box::new(DummyOutput)) + Ok(Box::new(NullOutput)) + } + + fn is_tty(&self) -> bool { + false } } @@ -267,8 +289,8 @@ impl FileHandler { let mut handles: BTreeMap<_, Box> = BTreeMap::new(); handles.insert(0i32, Box::new(io::stdin())); if mute_stdout_stderr { - handles.insert(1i32, Box::new(DummyOutput)); - handles.insert(2i32, Box::new(DummyOutput)); + handles.insert(1i32, Box::new(NullOutput)); + handles.insert(2i32, Box::new(NullOutput)); } else { handles.insert(1i32, Box::new(io::stdout())); handles.insert(2i32, Box::new(io::stderr())); @@ -1662,35 +1684,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } #[cfg_attr(not(unix), allow(unused))] - fn isatty(&mut self, miri_fd: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn isatty( + &mut self, + miri_fd: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - #[cfg(unix)] + // "returns 1 if fd is an open file descriptor referring to a terminal; + // otherwise 0 is returned, and errno is set to indicate the error" if matches!(this.machine.isolated_op, IsolatedOp::Allow) { - let miri_fd = this.read_scalar(miri_fd)?.to_i32()?; - if let Some(host_fd) = - this.machine.file_handler.handles.get(&miri_fd).and_then(|fd| fd.as_unix_host_fd()) - { - // "returns 1 if fd is an open file descriptor referring to a terminal; - // otherwise 0 is returned, and errno is set to indicate the error" - // SAFETY: isatty has no preconditions - let is_tty = unsafe { libc::isatty(host_fd) }; - if is_tty == 0 { - let errno = std::io::Error::last_os_error() - .raw_os_error() - .map(Scalar::from_i32) - .unwrap(); - this.set_last_error(errno)?; - } - return Ok(is_tty); + let fd = this.read_scalar(miri_fd)?.to_i32()?; + if this.machine.file_handler.handles.get(&fd).map(|fd| fd.is_tty()) == Some(true) { + return Ok(Scalar::from_i32(1)); } } - // We are attemping to use a Unix interface on a non-Unix platform, or we are on a Unix - // platform and the passed file descriptor is not open, or isolation is enabled - // FIXME: It should be possible to emulate this at least on Windows by using - // GetConsoleMode. + // Fallback when the FD was not found or isolation is enabled. let enotty = this.eval_libc("ENOTTY")?; this.set_last_error(enotty)?; - Ok(0) + Ok(Scalar::from_i32(0)) } fn realpath( From 7300aac798d033df973f595063c77ecd986e6d4e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 21 Oct 2022 14:12:19 +0200 Subject: [PATCH 0176/1126] split libc tests from stdlib tests --- .../pass-dep/shims/libc-fs-with-isolation.rs | 28 ++++ .../shims/libc-fs-with-isolation.stderr | 6 + .../miri/tests/pass-dep/shims/libc-fs.rs | 137 ++++++++++++++++++ .../shims/{fs.stderr => libc-fs.stderr} | 0 .../shims/{fs.stdout => libc-fs.stdout} | 0 .../tests/pass-dep/shims/libc-rsfs.stdout | 1 + .../shims/fs-with-isolation.rs} | 19 +-- .../shims/fs-with-isolation.stderr} | 4 - .../miri/tests/{pass-dep => pass}/shims/fs.rs | 101 +------------ 9 files changed, 176 insertions(+), 120 deletions(-) create mode 100644 src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs create mode 100644 src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr create mode 100644 src/tools/miri/tests/pass-dep/shims/libc-fs.rs rename src/tools/miri/tests/pass-dep/shims/{fs.stderr => libc-fs.stderr} (100%) rename src/tools/miri/tests/pass-dep/shims/{fs.stdout => libc-fs.stdout} (100%) create mode 100644 src/tools/miri/tests/pass-dep/shims/libc-rsfs.stdout rename src/tools/miri/tests/{pass-dep/shims/fs_with_isolation.rs => pass/shims/fs-with-isolation.rs} (62%) rename src/tools/miri/tests/{pass-dep/shims/fs_with_isolation.stderr => pass/shims/fs-with-isolation.stderr} (79%) rename src/tools/miri/tests/{pass-dep => pass}/shims/fs.rs (77%) diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs new file mode 100644 index 000000000000..f1838cf64f7f --- /dev/null +++ b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs @@ -0,0 +1,28 @@ +//@ignore-target-windows: no libc on Windows +//@compile-flags: -Zmiri-isolation-error=warn-nobacktrace +//@normalize-stderr-test: "(stat(x)?)" -> "$$STAT" + +use std::ffi::CString; +use std::fs; +use std::io::{Error, ErrorKind}; + +fn main() { + // test `fcntl` + unsafe { + assert_eq!(libc::fcntl(1, libc::F_DUPFD, 0), -1); + assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EPERM)); + } + + // test `readlink` + let symlink_c_str = CString::new("foo.txt").unwrap(); + let mut buf = vec![0; "foo_link.txt".len() + 1]; + unsafe { + assert_eq!(libc::readlink(symlink_c_str.as_ptr(), buf.as_mut_ptr(), buf.len()), -1); + assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES)); + } + + // test `stat` + assert_eq!(fs::metadata("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied); + // check that it is the right kind of `PermissionDenied` + assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES)); +} diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr new file mode 100644 index 000000000000..21fcb65243e2 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr @@ -0,0 +1,6 @@ +warning: `fcntl` was made to return an error due to isolation + +warning: `readlink` was made to return an error due to isolation + +warning: `$STAT` was made to return an error due to isolation + diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs.rs b/src/tools/miri/tests/pass-dep/shims/libc-fs.rs new file mode 100644 index 000000000000..acf16ecb7e06 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/shims/libc-fs.rs @@ -0,0 +1,137 @@ +//@ignore-target-windows: no libc on Windows +//@compile-flags: -Zmiri-disable-isolation + +#![feature(io_error_more)] +#![feature(io_error_uncategorized)] + +use std::convert::TryInto; +use std::ffi::CString; +use std::fs::{canonicalize, remove_file, File}; +use std::io::{Error, ErrorKind, Write}; +use std::os::unix::ffi::OsStrExt; +use std::path::PathBuf; + +fn main() { + test_dup_stdout_stderr(); + test_canonicalize_too_long(); + test_readlink(); + test_file_open_unix_allow_two_args(); + test_file_open_unix_needs_three_args(); + test_file_open_unix_extra_third_arg(); +} + +fn tmp() -> PathBuf { + std::env::var("MIRI_TEMP") + .map(|tmp| { + // MIRI_TEMP is set outside of our emulated + // program, so it may have path separators that don't + // correspond to our target platform. We normalize them here + // before constructing a `PathBuf` + + #[cfg(windows)] + return PathBuf::from(tmp.replace("/", "\\")); + + #[cfg(not(windows))] + return PathBuf::from(tmp.replace("\\", "/")); + }) + .unwrap_or_else(|_| std::env::temp_dir()) +} + +/// Prepare: compute filename and make sure the file does not exist. +fn prepare(filename: &str) -> PathBuf { + let path = tmp().join(filename); + // Clean the paths for robustness. + remove_file(&path).ok(); + path +} + +/// Prepare like above, and also write some initial content to the file. +fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf { + let path = prepare(filename); + let mut file = File::create(&path).unwrap(); + file.write(content).unwrap(); + path +} + +fn test_file_open_unix_allow_two_args() { + let path = prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]); + + let mut name = path.into_os_string(); + name.push("\0"); + let name_ptr = name.as_bytes().as_ptr().cast::(); + let _fd = unsafe { libc::open(name_ptr, libc::O_RDONLY) }; +} + +fn test_file_open_unix_needs_three_args() { + let path = prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]); + + let mut name = path.into_os_string(); + name.push("\0"); + let name_ptr = name.as_bytes().as_ptr().cast::(); + let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT, 0o666) }; +} + +fn test_file_open_unix_extra_third_arg() { + let path = prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]); + + let mut name = path.into_os_string(); + name.push("\0"); + let name_ptr = name.as_bytes().as_ptr().cast::(); + let _fd = unsafe { libc::open(name_ptr, libc::O_RDONLY, 42) }; +} + +fn test_dup_stdout_stderr() { + let bytes = b"hello dup fd\n"; + unsafe { + let new_stdout = libc::fcntl(1, libc::F_DUPFD, 0); + let new_stderr = libc::fcntl(2, libc::F_DUPFD, 0); + libc::write(new_stdout, bytes.as_ptr() as *const libc::c_void, bytes.len()); + libc::write(new_stderr, bytes.as_ptr() as *const libc::c_void, bytes.len()); + } +} + +fn test_canonicalize_too_long() { + // Make sure we get an error for long paths. + let too_long = "x/".repeat(libc::PATH_MAX.try_into().unwrap()); + assert!(canonicalize(too_long).is_err()); +} + +fn test_readlink() { + let bytes = b"Hello, World!\n"; + let path = prepare_with_content("miri_test_fs_link_target.txt", bytes); + let expected_path = path.as_os_str().as_bytes(); + + let symlink_path = prepare("miri_test_fs_symlink.txt"); + std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); + + // Test that the expected string gets written to a buffer of proper + // length, and that a trailing null byte is not written. + let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); + let symlink_c_ptr = symlink_c_str.as_ptr(); + + // Make the buf one byte larger than it needs to be, + // and check that the last byte is not overwritten. + let mut large_buf = vec![0xFF; expected_path.len() + 1]; + let res = + unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) }; + // Check that the resovled path was properly written into the buf. + assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path); + assert_eq!(large_buf.last(), Some(&0xFF)); + assert_eq!(res, large_buf.len() as isize - 1); + + // Test that the resolved path is truncated if the provided buffer + // is too small. + let mut small_buf = [0u8; 2]; + let res = + unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) }; + assert_eq!(small_buf, &expected_path[..small_buf.len()]); + assert_eq!(res, small_buf.len() as isize); + + // Test that we report a proper error for a missing path. + let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap(); + let res = unsafe { + libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len()) + }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound); +} diff --git a/src/tools/miri/tests/pass-dep/shims/fs.stderr b/src/tools/miri/tests/pass-dep/shims/libc-fs.stderr similarity index 100% rename from src/tools/miri/tests/pass-dep/shims/fs.stderr rename to src/tools/miri/tests/pass-dep/shims/libc-fs.stderr diff --git a/src/tools/miri/tests/pass-dep/shims/fs.stdout b/src/tools/miri/tests/pass-dep/shims/libc-fs.stdout similarity index 100% rename from src/tools/miri/tests/pass-dep/shims/fs.stdout rename to src/tools/miri/tests/pass-dep/shims/libc-fs.stdout diff --git a/src/tools/miri/tests/pass-dep/shims/libc-rsfs.stdout b/src/tools/miri/tests/pass-dep/shims/libc-rsfs.stdout new file mode 100644 index 000000000000..b6fa69e3d5d2 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/shims/libc-rsfs.stdout @@ -0,0 +1 @@ +hello dup fd diff --git a/src/tools/miri/tests/pass-dep/shims/fs_with_isolation.rs b/src/tools/miri/tests/pass/shims/fs-with-isolation.rs similarity index 62% rename from src/tools/miri/tests/pass-dep/shims/fs_with_isolation.rs rename to src/tools/miri/tests/pass/shims/fs-with-isolation.rs index f5420dbc5538..8fa683085b98 100644 --- a/src/tools/miri/tests/pass-dep/shims/fs_with_isolation.rs +++ b/src/tools/miri/tests/pass/shims/fs-with-isolation.rs @@ -2,21 +2,14 @@ //@compile-flags: -Zmiri-isolation-error=warn-nobacktrace //@normalize-stderr-test: "(stat(x)?)" -> "$$STAT" -use std::ffi::CString; use std::fs::{self, File}; -use std::io::{Error, ErrorKind}; +use std::io::ErrorKind; use std::os::unix; fn main() { // test `open` assert_eq!(File::create("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied); - // test `fcntl` - unsafe { - assert_eq!(libc::fcntl(1, libc::F_DUPFD, 0), -1); - assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EPERM)); - } - // test `unlink` assert_eq!(fs::remove_file("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied); @@ -26,17 +19,8 @@ fn main() { ErrorKind::PermissionDenied ); - // test `readlink` - let symlink_c_str = CString::new("foo.txt").unwrap(); - let mut buf = vec![0; "foo_link.txt".len() + 1]; - unsafe { - assert_eq!(libc::readlink(symlink_c_str.as_ptr(), buf.as_mut_ptr(), buf.len()), -1); - assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES)); - } - // test `stat` assert_eq!(fs::metadata("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied); - assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES)); // test `rename` assert_eq!(fs::rename("a.txt", "b.txt").unwrap_err().kind(), ErrorKind::PermissionDenied); @@ -49,5 +33,4 @@ fn main() { // test `opendir` assert_eq!(fs::read_dir("foo/bar").unwrap_err().kind(), ErrorKind::PermissionDenied); - assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES)); } diff --git a/src/tools/miri/tests/pass-dep/shims/fs_with_isolation.stderr b/src/tools/miri/tests/pass/shims/fs-with-isolation.stderr similarity index 79% rename from src/tools/miri/tests/pass-dep/shims/fs_with_isolation.stderr rename to src/tools/miri/tests/pass/shims/fs-with-isolation.stderr index ad75e42831b0..452c5b9b772a 100644 --- a/src/tools/miri/tests/pass-dep/shims/fs_with_isolation.stderr +++ b/src/tools/miri/tests/pass/shims/fs-with-isolation.stderr @@ -1,13 +1,9 @@ warning: `open` was made to return an error due to isolation -warning: `fcntl` was made to return an error due to isolation - warning: `unlink` was made to return an error due to isolation warning: `symlink` was made to return an error due to isolation -warning: `readlink` was made to return an error due to isolation - warning: `$STAT` was made to return an error due to isolation warning: `rename` was made to return an error due to isolation diff --git a/src/tools/miri/tests/pass-dep/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs similarity index 77% rename from src/tools/miri/tests/pass-dep/shims/fs.rs rename to src/tools/miri/tests/pass/shims/fs.rs index e573d330aa4a..1758e486ac3f 100644 --- a/src/tools/miri/tests/pass-dep/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -5,10 +5,10 @@ #![feature(io_error_uncategorized)] use std::collections::HashMap; -use std::ffi::{CString, OsString}; +use std::ffi::OsString; use std::fs::{ - create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename, File, - OpenOptions, + canonicalize, create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename, + File, OpenOptions, }; use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; @@ -26,13 +26,7 @@ fn main() { test_rename(); test_directory(); test_canonicalize(); - test_dup_stdout_stderr(); test_from_raw_os_error(); - - // These all require unix, if the test is changed to no longer `ignore-windows`, move these to a unix test - test_file_open_unix_allow_two_args(); - test_file_open_unix_needs_three_args(); - test_file_open_unix_extra_third_arg(); } fn tmp() -> PathBuf { @@ -101,39 +95,6 @@ fn test_file() { remove_file(&path).unwrap(); } -fn test_file_open_unix_allow_two_args() { - use std::os::unix::ffi::OsStrExt; - - let path = prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]); - - let mut name = path.into_os_string(); - name.push("\0"); - let name_ptr = name.as_bytes().as_ptr().cast::(); - let _fd = unsafe { libc::open(name_ptr, libc::O_RDONLY) }; -} - -fn test_file_open_unix_needs_three_args() { - use std::os::unix::ffi::OsStrExt; - - let path = prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]); - - let mut name = path.into_os_string(); - name.push("\0"); - let name_ptr = name.as_bytes().as_ptr().cast::(); - let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT, 0o666) }; -} - -fn test_file_open_unix_extra_third_arg() { - use std::os::unix::ffi::OsStrExt; - - let path = prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]); - - let mut name = path.into_os_string(); - name.push("\0"); - let name_ptr = name.as_bytes().as_ptr().cast::(); - let _fd = unsafe { libc::open(name_ptr, libc::O_RDONLY, 42) }; -} - fn test_file_clone() { let bytes = b"Hello, World!\n"; let path = prepare_with_content("miri_test_fs_file_clone.txt", bytes); @@ -279,46 +240,6 @@ fn test_symlink() { symlink_file.read_to_end(&mut contents).unwrap(); assert_eq!(bytes, contents.as_slice()); - #[cfg(unix)] - { - use std::os::unix::ffi::OsStrExt; - - let expected_path = path.as_os_str().as_bytes(); - - // Test that the expected string gets written to a buffer of proper - // length, and that a trailing null byte is not written. - let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); - let symlink_c_ptr = symlink_c_str.as_ptr(); - - // Make the buf one byte larger than it needs to be, - // and check that the last byte is not overwritten. - let mut large_buf = vec![0xFF; expected_path.len() + 1]; - let res = unsafe { - libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) - }; - // Check that the resovled path was properly written into the buf. - assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path); - assert_eq!(large_buf.last(), Some(&0xFF)); - assert_eq!(res, large_buf.len() as isize - 1); - - // Test that the resolved path is truncated if the provided buffer - // is too small. - let mut small_buf = [0u8; 2]; - let res = unsafe { - libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) - }; - assert_eq!(small_buf, &expected_path[..small_buf.len()]); - assert_eq!(res, small_buf.len() as isize); - - // Test that we report a proper error for a missing path. - let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap(); - let res = unsafe { - libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len()) - }; - assert_eq!(res, -1); - assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound); - } - // Test that metadata of a symbolic link (i.e., the file it points to) is correct. check_metadata(bytes, &symlink_path).unwrap(); // Test that the metadata of a symbolic link is correct when not following it. @@ -369,7 +290,6 @@ fn test_rename() { } fn test_canonicalize() { - use std::fs::canonicalize; let dir_path = prepare_dir("miri_test_fs_dir"); create_dir(&dir_path).unwrap(); let path = dir_path.join("test_file"); @@ -379,11 +299,6 @@ fn test_canonicalize() { assert_eq!(p.to_string_lossy().find('.'), None); remove_dir_all(&dir_path).unwrap(); - - // Make sure we get an error for long paths. - use std::convert::TryInto; - let too_long = "x/".repeat(libc::PATH_MAX.try_into().unwrap()); - assert!(canonicalize(too_long).is_err()); } fn test_directory() { @@ -440,16 +355,6 @@ fn test_directory() { remove_dir_all(&dir_path).unwrap(); } -fn test_dup_stdout_stderr() { - let bytes = b"hello dup fd\n"; - unsafe { - let new_stdout = libc::fcntl(1, libc::F_DUPFD, 0); - let new_stderr = libc::fcntl(2, libc::F_DUPFD, 0); - libc::write(new_stdout, bytes.as_ptr() as *const libc::c_void, bytes.len()); - libc::write(new_stderr, bytes.as_ptr() as *const libc::c_void, bytes.len()); - } -} - fn test_from_raw_os_error() { let code = 6; // not a code that std or Miri know let error = Error::from_raw_os_error(code); From 1c9f3682f5d95ccb5bd7d9f3e49dd49df5e9ee4e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 21 Oct 2022 14:16:12 +0200 Subject: [PATCH 0177/1126] test is_terminal --- src/tools/miri/tests/pass/shims/fs.rs | 5 ++++- src/tools/miri/tests/pass/shims/io.rs | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/tools/miri/tests/pass/shims/io.rs diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 1758e486ac3f..65cf6fe66ba5 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -3,6 +3,7 @@ #![feature(io_error_more)] #![feature(io_error_uncategorized)] +#![feature(is_terminal)] use std::collections::HashMap; use std::ffi::OsString; @@ -10,7 +11,7 @@ use std::fs::{ canonicalize, create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename, File, OpenOptions, }; -use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom, Write}; +use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; fn main() { @@ -91,6 +92,8 @@ fn test_file() { file.read_to_end(&mut contents).unwrap(); assert_eq!(bytes, contents.as_slice()); + assert!(!file.is_terminal()); + // Removing file should succeed. remove_file(&path).unwrap(); } diff --git a/src/tools/miri/tests/pass/shims/io.rs b/src/tools/miri/tests/pass/shims/io.rs new file mode 100644 index 000000000000..4d43549a930b --- /dev/null +++ b/src/tools/miri/tests/pass/shims/io.rs @@ -0,0 +1,9 @@ +#![feature(is_terminal)] + +use std::io::IsTerminal; + +fn main() { + // We can't really assume that this is truly a terminal, and anyway on Windows Miri will always + // return `false` here, but we can check that the call succeeds. + std::io::stdout().is_terminal(); +} From f554ebbc77b850a83ecd479a0409080e27507dd0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 21 Oct 2022 14:28:12 +0200 Subject: [PATCH 0178/1126] add test for #1909 --- .../miri/tests/pass/issues/issue-miri-1909.rs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/tools/miri/tests/pass/issues/issue-miri-1909.rs diff --git a/src/tools/miri/tests/pass/issues/issue-miri-1909.rs b/src/tools/miri/tests/pass/issues/issue-miri-1909.rs new file mode 100644 index 000000000000..ce2114e760a3 --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-miri-1909.rs @@ -0,0 +1,57 @@ +//@compile-flags: -Zmiri-permissive-provenance +#![deny(unsafe_op_in_unsafe_fn)] +//! This does some tricky ptr-int-casting. + +use core::alloc::{GlobalAlloc, Layout}; +use std::alloc::System; + +/// # Safety +/// `ptr` must be valid for writes of `len` bytes +unsafe fn volatile_write_zeroize_mem(ptr: *mut u8, len: usize) { + for i in 0..len { + // ptr as usize + i can't overlow because `ptr` is valid for writes of `len` + let ptr_new: *mut u8 = ((ptr as usize) + i) as *mut u8; + // SAFETY: `ptr` is valid for writes of `len` bytes, so `ptr_new` is valid for a + // byte write + unsafe { + core::ptr::write_volatile(ptr_new, 0u8); + } + } +} + +pub struct ZeroizeAlloc; + +unsafe impl GlobalAlloc for ZeroizeAlloc { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // SAFETY: uphold by caller + unsafe { System.alloc(layout) } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // securely wipe the deallocated memory + // SAFETY: `ptr` is valid for writes of `layout.size()` bytes since it was + // previously successfully allocated (by the safety assumption on this function) + // and not yet deallocated + unsafe { + volatile_write_zeroize_mem(ptr, layout.size()); + } + // SAFETY: uphold by caller + unsafe { System.dealloc(ptr, layout) } + } + + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // SAFETY: uphold by caller + unsafe { System.alloc_zeroed(layout) } + } +} + +#[global_allocator] +static GLOBAL: ZeroizeAlloc = ZeroizeAlloc; + +fn main() { + let layout = Layout::new::<[u8; 16]>(); + let ptr = unsafe { std::alloc::alloc_zeroed(layout) }; + unsafe { + std::alloc::dealloc(ptr, layout); + } +} From 487c6fc9adc02e835fde35451f016296450b1c33 Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 21 Oct 2022 14:51:13 +0200 Subject: [PATCH 0179/1126] [`collapsible_match`] specify field name when destructuring structs --- clippy_lints/src/matches/collapsible_match.rs | 23 ++++++++++--- tests/ui/collapsible_match.rs | 21 ++++++++++++ tests/ui/collapsible_match.stderr | 34 ++++++++++++++++++- 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index fd14d868df34..33a052c41a38 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, @@ -63,7 +64,8 @@ fn check_arm<'tcx>( if !pat_contains_or(inner_then_pat); // the binding must come from the pattern of the containing match arm // .... => match { .. } - if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); + if let (Some(binding_span), is_innermost_parent_pat_struct) + = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id); // the "else" branches must be equal if match (outer_else_body, inner_else_body) { (None, None) => true, @@ -88,6 +90,13 @@ fn check_arm<'tcx>( if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" }, if outer_is_match { "match" } else { "if let" }, ); + // collapsing patterns need an explicit field name in struct pattern matching + // ex: Struct {x: Some(1)} + let replace_msg = if is_innermost_parent_pat_struct { + format!(", prefixed by {}:", snippet(cx, binding_span, "their field name")) + } else { + String::new() + }; span_lint_and_then( cx, COLLAPSIBLE_MATCH, @@ -96,7 +105,7 @@ fn check_arm<'tcx>( |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"); - help_span.push_span_label(inner_then_pat.span, "with this pattern"); + help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); @@ -117,8 +126,9 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } } -fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { +fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option, bool) { let mut span = None; + let mut is_innermost_parent_pat_struct = false; pat.walk_short(|p| match &p.kind { // ignore OR patterns PatKind::Or(_) => false, @@ -129,9 +139,12 @@ fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { } !found }, - _ => true, + _ => { + is_innermost_parent_pat_struct = matches!(p.kind, PatKind::Struct(..)); + true + }, }); - span + (span, is_innermost_parent_pat_struct) } fn pat_contains_or(pat: &Pat<'_>) -> bool { diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 7d53e08345d3..1d7a72846419 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -253,6 +253,27 @@ fn negative_cases(res_opt: Result, String>, res_res: Result>, b: () }, + B, +} + +pub fn test_1(x: Issue9647) { + if let Issue9647::A { a, .. } = x { + if let Some(u) = a { + println!("{u:?}") + } + } +} + +pub fn test_2(x: Issue9647) { + if let Issue9647::A { a: Some(a), .. } = x { + if let Some(u) = a { + println!("{u}") + } + } +} + fn make() -> T { unimplemented!() } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 2580bef58091..0294be60b43f 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -175,5 +175,37 @@ LL | Some(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: aborting due to 10 previous errors +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:263:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u:?}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:262:27 + | +LL | if let Issue9647::A { a, .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern, prefixed by a: + +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:271:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:270:35 + | +LL | if let Issue9647::A { a: Some(a), .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern + +error: aborting due to 12 previous errors From 717bf35366e2baf44e0c9a6594041d58d35a0f13 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Fri, 21 Oct 2022 14:38:44 +0100 Subject: [PATCH 0180/1126] Different suggestions for when associated functions are referred to --- .../rustc_resolve/src/late/diagnostics.rs | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 4fd5bc1d60a4..6de63282df4e 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -38,8 +38,8 @@ type Res = def::Res; /// A field or associated item from self type suggested in case of resolution failure. enum AssocSuggestion { Field, - MethodWithSelf, - AssocFn, + MethodWithSelf { called: bool }, + AssocFn { called: bool }, AssocType, AssocConst, } @@ -48,8 +48,14 @@ impl AssocSuggestion { fn action(&self) -> &'static str { match self { AssocSuggestion::Field => "use the available field", - AssocSuggestion::MethodWithSelf => "call the method with the fully-qualified path", - AssocSuggestion::AssocFn => "call the associated function", + AssocSuggestion::MethodWithSelf { called: true } => { + "call the method with the fully-qualified path" + } + AssocSuggestion::MethodWithSelf { called: false } => { + "refer to the method with the fully-qualified path" + } + AssocSuggestion::AssocFn { called: true } => "call the associated function", + AssocSuggestion::AssocFn { called: false } => "refer to the associated function", AssocSuggestion::AssocConst => "use the associated `const`", AssocSuggestion::AssocType => "use the associated type", } @@ -498,7 +504,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { // Try Levenshtein algorithm. let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected); if path.len() == 1 && self.self_type_is_available() { - if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) { + if let Some(candidate) = + self.lookup_assoc_candidate(ident, ns, is_expected, source.is_call()) + { let self_is_available = self.self_value_is_available(path[0].ident.span); match candidate { AssocSuggestion::Field => { @@ -513,16 +521,21 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_label(span, "a field by this name exists in `Self`"); } } - AssocSuggestion::MethodWithSelf if self_is_available => { + AssocSuggestion::MethodWithSelf { called } if self_is_available => { + let msg = if called { + "you might have meant to call the method" + } else { + "you might have meant to refer to the method" + }; err.span_suggestion( span, - "you might have meant to call the method", + msg, format!("self.{path_str}"), Applicability::MachineApplicable, ); } - AssocSuggestion::MethodWithSelf - | AssocSuggestion::AssocFn + AssocSuggestion::MethodWithSelf { .. } + | AssocSuggestion::AssocFn { .. } | AssocSuggestion::AssocConst | AssocSuggestion::AssocType => { err.span_suggestion( @@ -1494,6 +1507,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ident: Ident, ns: Namespace, filter_fn: FilterFn, + called: bool, ) -> Option where FilterFn: Fn(Res) -> bool, @@ -1535,9 +1549,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { return Some(match &assoc_item.kind { ast::AssocItemKind::Const(..) => AssocSuggestion::AssocConst, ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) if sig.decl.has_self() => { - AssocSuggestion::MethodWithSelf + AssocSuggestion::MethodWithSelf { called } } - ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn, + ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn { called }, ast::AssocItemKind::Type(..) => AssocSuggestion::AssocType, ast::AssocItemKind::MacCall(_) => continue, }); @@ -1556,10 +1570,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let res = binding.res(); if filter_fn(res) { if self.r.has_self.contains(&res.def_id()) { - return Some(AssocSuggestion::MethodWithSelf); + return Some(AssocSuggestion::MethodWithSelf { called }); } else { match res { - Res::Def(DefKind::AssocFn, _) => return Some(AssocSuggestion::AssocFn), + Res::Def(DefKind::AssocFn, _) => { + return Some(AssocSuggestion::AssocFn { called }); + } Res::Def(DefKind::AssocConst, _) => { return Some(AssocSuggestion::AssocConst); } From 3f1e99936a821976fec469dd503ad1bdcad423e7 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Fri, 21 Oct 2022 14:43:58 +0100 Subject: [PATCH 0181/1126] Update UI tests --- src/test/ui/resolve/issue-14254.stderr | 49 ++++++++++++++++--- .../resolve/resolve-assoc-suggestions.stderr | 2 +- ...e-with-name-similar-to-struct-field.stderr | 2 +- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/test/ui/resolve/issue-14254.stderr b/src/test/ui/resolve/issue-14254.stderr index c848014ad8f0..690a40f7edd7 100644 --- a/src/test/ui/resolve/issue-14254.stderr +++ b/src/test/ui/resolve/issue-14254.stderr @@ -26,7 +26,12 @@ error[E0425]: cannot find value `bah` in this scope --> $DIR/issue-14254.rs:36:9 | LL | bah; - | ^^^ help: you might have meant to call the associated function: `Self::bah` + | ^^^ + | +help: you might have meant to refer to the associated function + | +LL | Self::bah; + | ~~~~~~~~~ error[E0425]: cannot find value `b` in this scope --> $DIR/issue-14254.rs:38:9 @@ -56,7 +61,12 @@ error[E0425]: cannot find value `bah` in this scope --> $DIR/issue-14254.rs:53:9 | LL | bah; - | ^^^ help: you might have meant to call the associated function: `Self::bah` + | ^^^ + | +help: you might have meant to refer to the associated function + | +LL | Self::bah; + | ~~~~~~~~~ error[E0425]: cannot find value `b` in this scope --> $DIR/issue-14254.rs:55:9 @@ -68,31 +78,56 @@ error[E0425]: cannot find value `bah` in this scope --> $DIR/issue-14254.rs:64:9 | LL | bah; - | ^^^ help: you might have meant to call the associated function: `Self::bah` + | ^^^ + | +help: you might have meant to refer to the associated function + | +LL | Self::bah; + | ~~~~~~~~~ error[E0425]: cannot find value `bah` in this scope --> $DIR/issue-14254.rs:73:9 | LL | bah; - | ^^^ help: you might have meant to call the associated function: `Self::bah` + | ^^^ + | +help: you might have meant to refer to the associated function + | +LL | Self::bah; + | ~~~~~~~~~ error[E0425]: cannot find value `bah` in this scope --> $DIR/issue-14254.rs:82:9 | LL | bah; - | ^^^ help: you might have meant to call the associated function: `Self::bah` + | ^^^ + | +help: you might have meant to refer to the associated function + | +LL | Self::bah; + | ~~~~~~~~~ error[E0425]: cannot find value `bah` in this scope --> $DIR/issue-14254.rs:91:9 | LL | bah; - | ^^^ help: you might have meant to call the associated function: `Self::bah` + | ^^^ + | +help: you might have meant to refer to the associated function + | +LL | Self::bah; + | ~~~~~~~~~ error[E0425]: cannot find value `bah` in this scope --> $DIR/issue-14254.rs:100:9 | LL | bah; - | ^^^ help: you might have meant to call the associated function: `Self::bah` + | ^^^ + | +help: you might have meant to refer to the associated function + | +LL | Self::bah; + | ~~~~~~~~~ error[E0425]: cannot find function `baz` in this scope --> $DIR/issue-14254.rs:19:9 diff --git a/src/test/ui/resolve/resolve-assoc-suggestions.stderr b/src/test/ui/resolve/resolve-assoc-suggestions.stderr index b6acaeb8cc23..8def9aa20253 100644 --- a/src/test/ui/resolve/resolve-assoc-suggestions.stderr +++ b/src/test/ui/resolve/resolve-assoc-suggestions.stderr @@ -50,7 +50,7 @@ error[E0425]: cannot find value `method` in this scope --> $DIR/resolve-assoc-suggestions.rs:34:9 | LL | method; - | ^^^^^^ help: you might have meant to call the method: `self.method` + | ^^^^^^ help: you might have meant to refer to the method: `self.method` error: aborting due to 9 previous errors diff --git a/src/test/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr b/src/test/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr index 2764e1f81328..f32e0404e46c 100644 --- a/src/test/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr +++ b/src/test/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr @@ -40,7 +40,7 @@ LL | bah; LL | fn ba() {} | ------- similarly named function `ba` defined here | -help: you might have meant to call the associated function +help: you might have meant to refer to the associated function | LL | Self::bah; | ~~~~~~~~~ From 1cb46079e493af318a2dc6f97c93c2e0ac7ecd0e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 21 Oct 2022 16:00:43 +0200 Subject: [PATCH 0182/1126] internal: Properly handle commands in the VSCode client when the server is stopped --- editors/code/src/ctx.ts | 80 +++++++++++++++----- editors/code/src/main.ts | 159 ++++++++++++++++++--------------------- 2 files changed, 136 insertions(+), 103 deletions(-) diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 75c6d4698c17..044a9470aa94 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -18,6 +18,11 @@ export type Workspace = files: vscode.TextDocument[]; }; +export type CommandFactory = { + enabled: (ctx: Ctx) => Cmd; + disabled?: (ctx: Ctx) => Cmd; +}; + export class Ctx { readonly statusBar: vscode.StatusBarItem; readonly config: Config; @@ -26,31 +31,40 @@ export class Ctx { private _serverPath: string | undefined; private traceOutputChannel: vscode.OutputChannel | undefined; private outputChannel: vscode.OutputChannel | undefined; + private clientSubscriptions: Disposable[]; private state: PersistentState; + private commandFactories: Record; + private commandDisposables: Disposable[]; workspace: Workspace; - constructor(readonly extCtx: vscode.ExtensionContext, workspace: Workspace) { - this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); - extCtx.subscriptions.push(this.statusBar); - extCtx.subscriptions.push({ - dispose() { - this.dispose(); - }, - }); + constructor( + readonly extCtx: vscode.ExtensionContext, + workspace: Workspace, + commandFactories: Record + ) { extCtx.subscriptions.push(this); + this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); this.statusBar.text = "rust-analyzer"; this.statusBar.tooltip = "ready"; this.statusBar.command = "rust-analyzer.analyzerStatus"; this.statusBar.show(); this.workspace = workspace; + this.clientSubscriptions = []; + this.commandDisposables = []; + this.commandFactories = commandFactories; this.state = new PersistentState(extCtx.globalState); this.config = new Config(extCtx); + + this.updateCommands(); } dispose() { this.config.dispose(); + this.statusBar.dispose(); + void this.disposeClient(); + this.commandDisposables.forEach((disposable) => disposable.dispose()); } clientFetcher() { @@ -63,7 +77,6 @@ export class Ctx { } async getClient() { - // if server path changes -> dispose if (!this.traceOutputChannel) { this.traceOutputChannel = vscode.window.createOutputChannel( "Rust Analyzer Language Server Trace" @@ -118,7 +131,11 @@ export class Ctx { initializationOptions, serverOptions ); - this.client.onNotification(ra.serverStatus, (params) => this.setServerStatus(params)); + this.pushClientCleanup( + this.client.onNotification(ra.serverStatus, (params) => + this.setServerStatus(params) + ) + ); } return this.client; } @@ -127,16 +144,25 @@ export class Ctx { log.info("Activating language client"); const client = await this.getClient(); await client.start(); + this.updateCommands(); return client; } async deactivate() { log.info("Deactivating language client"); await this.client?.stop(); + this.updateCommands(); } - async disposeClient() { - log.info("Deactivating language client"); + async stop() { + log.info("Stopping language client"); + await this.disposeClient(); + this.updateCommands(); + } + + private async disposeClient() { + this.clientSubscriptions?.forEach((disposable) => disposable.dispose()); + this.clientSubscriptions = []; await this.client?.dispose(); this._serverPath = undefined; this.client = undefined; @@ -159,6 +185,25 @@ export class Ctx { return this._serverPath; } + private updateCommands() { + this.commandDisposables.forEach((disposable) => disposable.dispose()); + this.commandDisposables = []; + const fetchFactory = (factory: CommandFactory, fullName: string) => { + return this.client && this.client.isRunning() + ? factory.enabled + : factory.disabled || + ((_) => () => + vscode.window.showErrorMessage( + `command ${fullName} failed: rust-analyzer server is not running` + )); + }; + for (const [name, factory] of Object.entries(this.commandFactories)) { + const fullName = `rust-analyzer.${name}`; + const callback = fetchFactory(factory, fullName)(this); + this.commandDisposables.push(vscode.commands.registerCommand(fullName, callback)); + } + } + setServerStatus(status: ServerStatusParams) { let icon = ""; const statusBar = this.statusBar; @@ -194,16 +239,13 @@ export class Ctx { statusBar.text = `${icon}rust-analyzer`; } - registerCommand(name: string, factory: (ctx: Ctx) => Cmd) { - const fullName = `rust-analyzer.${name}`; - const cmd = factory(this); - const d = vscode.commands.registerCommand(fullName, cmd); - this.pushExtCleanup(d); - } - pushExtCleanup(d: Disposable) { this.extCtx.subscriptions.push(d); } + + private pushClientCleanup(d: Disposable) { + this.clientSubscriptions.push(d); + } } export interface Disposable { diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index fa7dc6fe304e..8c3a676ffb05 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -2,7 +2,7 @@ import * as vscode from "vscode"; import * as lc from "vscode-languageclient/node"; import * as commands from "./commands"; -import { Ctx, Workspace } from "./ctx"; +import { CommandFactory, Ctx, Workspace } from "./ctx"; import { isRustDocument } from "./util"; import { activateTaskProvider } from "./tasks"; import { setContextValue } from "./util"; @@ -57,7 +57,7 @@ export async function activate( } : { kind: "Workspace Folder" }; - const ctx = new Ctx(context, workspace); + const ctx = new Ctx(context, workspace, createCommands()); // VS Code doesn't show a notification when an extension fails to activate // so we do it ourselves. const api = await activateServer(ctx).catch((err) => { @@ -75,8 +75,6 @@ async function activateServer(ctx: Ctx): Promise { ctx.pushExtCleanup(activateTaskProvider(ctx.config)); } - await initCommonContext(ctx); - vscode.workspace.onDidChangeConfiguration( async (_) => { await ctx @@ -91,85 +89,78 @@ async function activateServer(ctx: Ctx): Promise { return ctx.clientFetcher(); } -async function initCommonContext(ctx: Ctx) { - // Register a "dumb" onEnter command for the case where server fails to - // start. - // - // FIXME: refactor command registration code such that commands are - // **always** registered, even if the server does not start. Use API like - // this perhaps? - // - // ```TypeScript - // registerCommand( - // factory: (Ctx) => ((Ctx) => any), - // fallback: () => any = () => vscode.window.showErrorMessage( - // "rust-analyzer is not available" - // ), - // ) - const defaultOnEnter = vscode.commands.registerCommand("rust-analyzer.onEnter", () => - vscode.commands.executeCommand("default:type", { text: "\n" }) - ); - ctx.pushExtCleanup(defaultOnEnter); +function createCommands(): Record { + return { + onEnter: { + enabled: commands.onEnter, + disabled: (_) => () => vscode.commands.executeCommand("default:type", { text: "\n" }), + }, + reload: { + enabled: (ctx) => async () => { + void vscode.window.showInformationMessage("Reloading rust-analyzer..."); + // FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed + await ctx.stop(); + await ctx.activate(); + }, + disabled: (ctx) => async () => { + void vscode.window.showInformationMessage("Reloading rust-analyzer..."); + await ctx.activate(); + }, + }, + startServer: { + enabled: (ctx) => async () => { + await ctx.activate(); + }, + disabled: (ctx) => async () => { + await ctx.activate(); + }, + }, + stopServer: { + enabled: (ctx) => async () => { + // FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed + await ctx.stop(); + ctx.setServerStatus({ + health: "ok", + quiescent: true, + message: "server is not running", + }); + }, + }, - // Commands which invokes manually via command palette, shortcut, etc. - ctx.registerCommand("reload", (_) => async () => { - void vscode.window.showInformationMessage("Reloading rust-analyzer..."); - // FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed - await ctx.disposeClient(); - await ctx.activate(); - }); - - ctx.registerCommand("startServer", (_) => async () => { - await ctx.activate(); - }); - ctx.registerCommand("stopServer", (_) => async () => { - // FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed - await ctx.disposeClient(); - ctx.setServerStatus({ - health: "ok", - quiescent: true, - message: "server is not running", - }); - }); - ctx.registerCommand("analyzerStatus", commands.analyzerStatus); - ctx.registerCommand("memoryUsage", commands.memoryUsage); - ctx.registerCommand("shuffleCrateGraph", commands.shuffleCrateGraph); - ctx.registerCommand("reloadWorkspace", commands.reloadWorkspace); - ctx.registerCommand("matchingBrace", commands.matchingBrace); - ctx.registerCommand("joinLines", commands.joinLines); - ctx.registerCommand("parentModule", commands.parentModule); - ctx.registerCommand("syntaxTree", commands.syntaxTree); - ctx.registerCommand("viewHir", commands.viewHir); - ctx.registerCommand("viewFileText", commands.viewFileText); - ctx.registerCommand("viewItemTree", commands.viewItemTree); - ctx.registerCommand("viewCrateGraph", commands.viewCrateGraph); - ctx.registerCommand("viewFullCrateGraph", commands.viewFullCrateGraph); - ctx.registerCommand("expandMacro", commands.expandMacro); - ctx.registerCommand("run", commands.run); - ctx.registerCommand("copyRunCommandLine", commands.copyRunCommandLine); - ctx.registerCommand("debug", commands.debug); - ctx.registerCommand("newDebugConfig", commands.newDebugConfig); - ctx.registerCommand("openDocs", commands.openDocs); - ctx.registerCommand("openCargoToml", commands.openCargoToml); - ctx.registerCommand("peekTests", commands.peekTests); - ctx.registerCommand("moveItemUp", commands.moveItemUp); - ctx.registerCommand("moveItemDown", commands.moveItemDown); - ctx.registerCommand("cancelFlycheck", commands.cancelFlycheck); - - ctx.registerCommand("ssr", commands.ssr); - ctx.registerCommand("serverVersion", commands.serverVersion); - - // Internal commands which are invoked by the server. - ctx.registerCommand("runSingle", commands.runSingle); - ctx.registerCommand("debugSingle", commands.debugSingle); - ctx.registerCommand("showReferences", commands.showReferences); - ctx.registerCommand("applySnippetWorkspaceEdit", commands.applySnippetWorkspaceEditCommand); - ctx.registerCommand("resolveCodeAction", commands.resolveCodeAction); - ctx.registerCommand("applyActionGroup", commands.applyActionGroup); - ctx.registerCommand("gotoLocation", commands.gotoLocation); - - ctx.registerCommand("linkToCommand", commands.linkToCommand); - - defaultOnEnter.dispose(); - ctx.registerCommand("onEnter", commands.onEnter); + analyzerStatus: { enabled: commands.analyzerStatus }, + memoryUsage: { enabled: commands.memoryUsage }, + shuffleCrateGraph: { enabled: commands.shuffleCrateGraph }, + reloadWorkspace: { enabled: commands.reloadWorkspace }, + matchingBrace: { enabled: commands.matchingBrace }, + joinLines: { enabled: commands.joinLines }, + parentModule: { enabled: commands.parentModule }, + syntaxTree: { enabled: commands.syntaxTree }, + viewHir: { enabled: commands.viewHir }, + viewFileText: { enabled: commands.viewFileText }, + viewItemTree: { enabled: commands.viewItemTree }, + viewCrateGraph: { enabled: commands.viewCrateGraph }, + viewFullCrateGraph: { enabled: commands.viewFullCrateGraph }, + expandMacro: { enabled: commands.expandMacro }, + run: { enabled: commands.run }, + copyRunCommandLine: { enabled: commands.copyRunCommandLine }, + debug: { enabled: commands.debug }, + newDebugConfig: { enabled: commands.newDebugConfig }, + openDocs: { enabled: commands.openDocs }, + openCargoToml: { enabled: commands.openCargoToml }, + peekTests: { enabled: commands.peekTests }, + moveItemUp: { enabled: commands.moveItemUp }, + moveItemDown: { enabled: commands.moveItemDown }, + cancelFlycheck: { enabled: commands.cancelFlycheck }, + ssr: { enabled: commands.ssr }, + serverVersion: { enabled: commands.serverVersion }, + // Internal commands which are invoked by the server. + applyActionGroup: { enabled: commands.applyActionGroup }, + applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand }, + debugSingle: { enabled: commands.debugSingle }, + gotoLocation: { enabled: commands.gotoLocation }, + linkToCommand: { enabled: commands.linkToCommand }, + resolveCodeAction: { enabled: commands.resolveCodeAction }, + runSingle: { enabled: commands.runSingle }, + showReferences: { enabled: commands.showReferences }, + }; } From b42721ce45eaaee7ad473a4ea1da535a19fb2e17 Mon Sep 17 00:00:00 2001 From: Patrick Mooney Date: Thu, 20 Oct 2022 11:48:13 -0500 Subject: [PATCH 0183/1126] Update libffi-sys to 2.0.1 Prior version of libffi could not be cross-compiled to illumos due to host-triple complications. This should fix rustup builds of miri for the illumos platform. --- src/tools/miri/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 143e8f8274a3..343cf0eaba25 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -320,9 +320,9 @@ dependencies = [ [[package]] name = "libffi-sys" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4106b7f09d7b87d021334d5618fac1dfcfb824d4c5fe111ff0074dfd242e15" +checksum = "84e78d02e5a8eae9c24c38ce6e6026f80e16dff76adcdae4bc5c6c52c2de4a60" dependencies = [ "cc", ] From e4ef0e5df9a3aeee2b6af8b4c2a567e7d84cc4b1 Mon Sep 17 00:00:00 2001 From: Justin Mott Date: Fri, 21 Oct 2022 13:28:59 -0400 Subject: [PATCH 0184/1126] addressed https://github.com/rust-lang/rust-analyzer/issues/12536 --- .../ide-assists/src/handlers/inline_call.rs | 86 ++++++++++++++++--- 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 9f51cdaf8b1e..c546ee45d964 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeSet; + use ast::make; use either::Either; use hir::{db::HirDatabase, PathResolution, Semantics, TypeInfo}; @@ -373,8 +375,44 @@ fn inline( }) } } + + let mut func_let_vars: BTreeSet = BTreeSet::new(); + + // grab all of the local variable declarations in the function + for stmt in fn_body.statements() { + if let Some(let_stmt) = ast::LetStmt::cast(stmt.syntax().to_owned()) { + for has_token in let_stmt.syntax().children_with_tokens() { + if let Some(node) = has_token.as_node() { + if let Some(ident_pat) = ast::IdentPat::cast(node.to_owned()) { + func_let_vars.insert(ident_pat.syntax().text().to_string()); + } + } + } + } + } + // Inline parameter expressions or generate `let` statements depending on whether inlining works or not. for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() { + // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors + let usages: &[ast::PathExpr] = &*usages; + let expr: &ast::Expr = expr; + + let insert_let_stmt = || { + let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone()); + if let Some(stmt_list) = body.stmt_list() { + stmt_list.push_front( + make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(), + ) + } + }; + + // check if there is a local var in the function that conflicts with parameter + // if it does then emit a let statement and continue + if func_let_vars.contains(&expr.syntax().text().to_string()) { + insert_let_stmt(); + continue; + } + let inline_direct = |usage, replacement: &ast::Expr| { if let Some(field) = path_expr_as_record_field(usage) { cov_mark::hit!(inline_call_inline_direct_field); @@ -383,9 +421,7 @@ fn inline( ted::replace(usage.syntax(), &replacement.syntax().clone_for_update()); } }; - // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors - let usages: &[ast::PathExpr] = &*usages; - let expr: &ast::Expr = expr; + match usages { // inline single use closure arguments [usage] @@ -408,18 +444,11 @@ fn inline( } // can't inline, emit a let statement _ => { - let ty = - sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone()); - if let Some(stmt_list) = body.stmt_list() { - stmt_list.push_front( - make::let_stmt(pat.clone(), ty, Some(expr.clone())) - .clone_for_update() - .into(), - ) - } + insert_let_stmt(); } } } + if let Some(generic_arg_list) = generic_arg_list.clone() { if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax())) { @@ -1256,4 +1285,37 @@ impl A { "#, ) } + + #[test] + fn local_variable_shadowing_callers_argument() { + check_assist( + inline_call, + r#" +fn foo(bar: u32, baz: u32) -> u32 { + let a = 1; + bar * baz * a * 6 +} +fn main() { + let a = 7; + let b = 1; + let res = foo$0(a, b); +} +"#, + r#" +fn foo(bar: u32, baz: u32) -> u32 { + let a = 1; + bar * baz * a * 6 +} +fn main() { + let a = 7; + let b = 1; + let res = { + let bar = a; + let a = 1; + bar * b * a * 6 + }; +} +"#, + ); + } } From 590834b0328c2975ebfbfe3c71010c916ad6cdda Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Fri, 21 Oct 2022 18:24:46 +0100 Subject: [PATCH 0185/1126] add GetFileType stub --- src/tools/miri/src/shims/windows/foreign_items.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index fa8eaaed58c8..184ba997fc86 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -425,6 +425,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Just make it fail. this.write_null(dest)?; } + "GetFileType" if this.frame_in_std() => { + #[allow(non_snake_case)] + let [_hFile] = + this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; + // Return unknown file type. + this.write_null(dest)?; + } "AddVectoredExceptionHandler" if this.frame_in_std() => { #[allow(non_snake_case)] let [_First, _Handler] = From 815876d93f75c4d20c52cdd32f1a521ce306be63 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 21 Oct 2022 21:35:39 +0000 Subject: [PATCH 0186/1126] Move MSRV tests into the lint specific test files --- book/src/development/adding_lints.md | 23 +- tests/ui/cast_abs_to_unsigned.fixed | 18 +- tests/ui/cast_abs_to_unsigned.rs | 18 +- tests/ui/cast_abs_to_unsigned.stderr | 42 +-- tests/ui/cast_lossless_bool.fixed | 13 + tests/ui/cast_lossless_bool.rs | 13 + tests/ui/cast_lossless_bool.stderr | 34 ++- tests/ui/cfg_attr_rustfmt.fixed | 16 +- tests/ui/cfg_attr_rustfmt.rs | 16 +- tests/ui/cfg_attr_rustfmt.stderr | 8 +- tests/ui/checked_conversions.fixed | 16 ++ tests/ui/checked_conversions.rs | 16 ++ tests/ui/checked_conversions.stderr | 40 +-- tests/ui/cloned_instead_of_copied.fixed | 24 ++ tests/ui/cloned_instead_of_copied.rs | 24 ++ tests/ui/cloned_instead_of_copied.stderr | 30 ++- tests/ui/err_expect.fixed | 17 ++ tests/ui/err_expect.rs | 17 ++ tests/ui/err_expect.stderr | 10 +- tests/ui/filter_map_next_fixable.fixed | 16 ++ tests/ui/filter_map_next_fixable.rs | 16 ++ tests/ui/filter_map_next_fixable.stderr | 10 +- tests/ui/from_over_into.fixed | 25 ++ tests/ui/from_over_into.rs | 25 ++ tests/ui/from_over_into.stderr | 23 +- tests/ui/manual_clamp.rs | 27 ++ tests/ui/manual_clamp.stderr | 85 ++++--- tests/ui/manual_rem_euclid.fixed | 30 +++ tests/ui/manual_rem_euclid.rs | 30 +++ tests/ui/manual_rem_euclid.stderr | 30 ++- tests/ui/manual_strip.rs | 19 ++ tests/ui/manual_strip.stderr | 47 ++-- tests/ui/map_unwrap_or.rs | 20 +- tests/ui/map_unwrap_or.stderr | 30 ++- tests/ui/match_expr_like_matches_macro.fixed | 16 ++ tests/ui/match_expr_like_matches_macro.rs | 19 ++ tests/ui/match_expr_like_matches_macro.stderr | 38 ++- tests/ui/mem_replace.fixed | 18 +- tests/ui/mem_replace.rs | 18 +- tests/ui/mem_replace.stderr | 46 ++-- tests/ui/min_rust_version_attr.rs | 239 +----------------- tests/ui/min_rust_version_attr.stderr | 50 ++-- tests/ui/min_rust_version_invalid_attr.rs | 14 + tests/ui/min_rust_version_invalid_attr.stderr | 44 +++- .../min_rust_version_multiple_inner_attr.rs | 11 - ...in_rust_version_multiple_inner_attr.stderr | 38 --- tests/ui/min_rust_version_no_patch.rs | 14 - tests/ui/min_rust_version_outer_attr.rs | 4 - tests/ui/min_rust_version_outer_attr.stderr | 8 - .../ui/missing_const_for_fn/could_be_const.rs | 12 + .../could_be_const.stderr | 12 +- tests/ui/option_as_ref_deref.fixed | 17 +- tests/ui/option_as_ref_deref.rs | 17 +- tests/ui/option_as_ref_deref.stderr | 42 +-- tests/ui/range_contains.fixed | 26 +- tests/ui/range_contains.rs | 26 +- tests/ui/range_contains.stderr | 48 ++-- tests/ui/redundant_field_names.fixed | 16 ++ tests/ui/redundant_field_names.rs | 16 ++ tests/ui/redundant_field_names.stderr | 22 +- tests/ui/redundant_static_lifetimes.fixed | 13 + tests/ui/redundant_static_lifetimes.rs | 13 + tests/ui/redundant_static_lifetimes.stderr | 40 +-- tests/ui/unnested_or_patterns.fixed | 16 +- tests/ui/unnested_or_patterns.rs | 16 +- tests/ui/unnested_or_patterns.stderr | 13 +- tests/ui/use_self.fixed | 33 +++ tests/ui/use_self.rs | 33 +++ tests/ui/use_self.stderr | 90 ++++--- 69 files changed, 1292 insertions(+), 634 deletions(-) delete mode 100644 tests/ui/min_rust_version_multiple_inner_attr.rs delete mode 100644 tests/ui/min_rust_version_multiple_inner_attr.stderr delete mode 100644 tests/ui/min_rust_version_no_patch.rs delete mode 100644 tests/ui/min_rust_version_outer_attr.rs delete mode 100644 tests/ui/min_rust_version_outer_attr.stderr diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index b1e843bc7f4c..3c3f368a529b 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -478,8 +478,27 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { ``` Once the `msrv` is added to the lint, a relevant test case should be added to -`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted -if the project's MSRV is lower. +the lint's test file, `tests/ui/manual_strip.rs` in this example. It should +have a case for the version below the MSRV and one with the same contents but +for the MSRV version itself. + +```rust +#![feature(custom_inner_attributes)] + +... + +fn msrv_1_44() { + #![clippy::msrv = "1.44"] + + /* something that would trigger the lint */ +} + +fn msrv_1_45() { + #![clippy::msrv = "1.45"] + + /* something that would trigger the lint */ +} +``` As a last step, the lint should be added to the lint documentation. This is done in `clippy_lints/src/utils/conf.rs`: diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index a37f3fec20f1..e6bf944c7a5e 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,6 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +32,17 @@ fn main() { let _ = (x as i64 - y as i64).unsigned_abs() as u32; } + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + assert_eq!(10u32, x.unsigned_abs()); +} diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 5706930af5a0..c87320b5209d 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,6 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +32,17 @@ fn main() { let _ = (x as i64 - y as i64).abs() as u32; } + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index 7cea11c183d2..1b39c554b038 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -1,5 +1,5 @@ error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:7:18 + --> $DIR/cast_abs_to_unsigned.rs:9:18 | LL | let y: u32 = x.abs() as u32; | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` @@ -7,100 +7,106 @@ LL | let y: u32 = x.abs() as u32; = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:11:20 + --> $DIR/cast_abs_to_unsigned.rs:13:20 | LL | let _: usize = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:12:20 + --> $DIR/cast_abs_to_unsigned.rs:14:20 | LL | let _: usize = a.abs() as _; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:13:13 + --> $DIR/cast_abs_to_unsigned.rs:15:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:16:13 + --> $DIR/cast_abs_to_unsigned.rs:18:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:17:13 + --> $DIR/cast_abs_to_unsigned.rs:19:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:18:13 + --> $DIR/cast_abs_to_unsigned.rs:20:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:19:13 + --> $DIR/cast_abs_to_unsigned.rs:21:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:20:13 + --> $DIR/cast_abs_to_unsigned.rs:22:13 | LL | let _ = a.abs() as u64; | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:21:13 + --> $DIR/cast_abs_to_unsigned.rs:23:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:24:13 + --> $DIR/cast_abs_to_unsigned.rs:26:13 | LL | let _ = a.abs() as usize; | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:25:13 + --> $DIR/cast_abs_to_unsigned.rs:27:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:26:13 + --> $DIR/cast_abs_to_unsigned.rs:28:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:27:13 + --> $DIR/cast_abs_to_unsigned.rs:29:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:28:13 + --> $DIR/cast_abs_to_unsigned.rs:30:13 | LL | let _ = a.abs() as u64; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:29:13 + --> $DIR/cast_abs_to_unsigned.rs:31:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:31:13 + --> $DIR/cast_abs_to_unsigned.rs:33:13 | LL | let _ = (x as i64 - y as i64).abs() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()` -error: aborting due to 17 previous errors +error: casting the result of `i32::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:47:23 + | +LL | assert_eq!(10u32, x.abs() as u32); + | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/cast_lossless_bool.fixed b/tests/ui/cast_lossless_bool.fixed index 9e2da45c3785..af13b755e310 100644 --- a/tests/ui/cast_lossless_bool.fixed +++ b/tests/ui/cast_lossless_bool.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -40,3 +41,15 @@ mod cast_lossless_in_impl { } } } + +fn msrv_1_27() { + #![clippy::msrv = "1.27"] + + let _ = true as u8; +} + +fn msrv_1_28() { + #![clippy::msrv = "1.28"] + + let _ = u8::from(true); +} diff --git a/tests/ui/cast_lossless_bool.rs b/tests/ui/cast_lossless_bool.rs index b6f6c59a01f9..3b06af899c60 100644 --- a/tests/ui/cast_lossless_bool.rs +++ b/tests/ui/cast_lossless_bool.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -40,3 +41,15 @@ mod cast_lossless_in_impl { } } } + +fn msrv_1_27() { + #![clippy::msrv = "1.27"] + + let _ = true as u8; +} + +fn msrv_1_28() { + #![clippy::msrv = "1.28"] + + let _ = true as u8; +} diff --git a/tests/ui/cast_lossless_bool.stderr b/tests/ui/cast_lossless_bool.stderr index 6b148336011d..768b033d10a2 100644 --- a/tests/ui/cast_lossless_bool.stderr +++ b/tests/ui/cast_lossless_bool.stderr @@ -1,5 +1,5 @@ error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> $DIR/cast_lossless_bool.rs:8:13 + --> $DIR/cast_lossless_bool.rs:9:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` @@ -7,76 +7,82 @@ LL | let _ = true as u8; = note: `-D clippy::cast-lossless` implied by `-D warnings` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:9:13 + --> $DIR/cast_lossless_bool.rs:10:13 | LL | let _ = true as u16; | ^^^^^^^^^^^ help: try: `u16::from(true)` error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)` - --> $DIR/cast_lossless_bool.rs:10:13 + --> $DIR/cast_lossless_bool.rs:11:13 | LL | let _ = true as u32; | ^^^^^^^^^^^ help: try: `u32::from(true)` error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)` - --> $DIR/cast_lossless_bool.rs:11:13 + --> $DIR/cast_lossless_bool.rs:12:13 | LL | let _ = true as u64; | ^^^^^^^^^^^ help: try: `u64::from(true)` error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)` - --> $DIR/cast_lossless_bool.rs:12:13 + --> $DIR/cast_lossless_bool.rs:13:13 | LL | let _ = true as u128; | ^^^^^^^^^^^^ help: try: `u128::from(true)` error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)` - --> $DIR/cast_lossless_bool.rs:13:13 + --> $DIR/cast_lossless_bool.rs:14:13 | LL | let _ = true as usize; | ^^^^^^^^^^^^^ help: try: `usize::from(true)` error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)` - --> $DIR/cast_lossless_bool.rs:15:13 + --> $DIR/cast_lossless_bool.rs:16:13 | LL | let _ = true as i8; | ^^^^^^^^^^ help: try: `i8::from(true)` error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)` - --> $DIR/cast_lossless_bool.rs:16:13 + --> $DIR/cast_lossless_bool.rs:17:13 | LL | let _ = true as i16; | ^^^^^^^^^^^ help: try: `i16::from(true)` error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)` - --> $DIR/cast_lossless_bool.rs:17:13 + --> $DIR/cast_lossless_bool.rs:18:13 | LL | let _ = true as i32; | ^^^^^^^^^^^ help: try: `i32::from(true)` error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)` - --> $DIR/cast_lossless_bool.rs:18:13 + --> $DIR/cast_lossless_bool.rs:19:13 | LL | let _ = true as i64; | ^^^^^^^^^^^ help: try: `i64::from(true)` error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)` - --> $DIR/cast_lossless_bool.rs:19:13 + --> $DIR/cast_lossless_bool.rs:20:13 | LL | let _ = true as i128; | ^^^^^^^^^^^^ help: try: `i128::from(true)` error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)` - --> $DIR/cast_lossless_bool.rs:20:13 + --> $DIR/cast_lossless_bool.rs:21:13 | LL | let _ = true as isize; | ^^^^^^^^^^^^^ help: try: `isize::from(true)` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:23:13 + --> $DIR/cast_lossless_bool.rs:24:13 | LL | let _ = (true | false) as u16; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)` -error: aborting due to 13 previous errors +error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` + --> $DIR/cast_lossless_bool.rs:54:13 + | +LL | let _ = true as u8; + | ^^^^^^^^^^ help: try: `u8::from(true)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/cfg_attr_rustfmt.fixed b/tests/ui/cfg_attr_rustfmt.fixed index 061a4ab9b2ef..8a5645b22ed1 100644 --- a/tests/ui/cfg_attr_rustfmt.fixed +++ b/tests/ui/cfg_attr_rustfmt.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, custom_inner_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -29,3 +29,17 @@ mod foo { pub fn f() {} } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + #[rustfmt::skip] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.rs b/tests/ui/cfg_attr_rustfmt.rs index 035169fab85b..2fb140efae76 100644 --- a/tests/ui/cfg_attr_rustfmt.rs +++ b/tests/ui/cfg_attr_rustfmt.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, custom_inner_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -29,3 +29,17 @@ mod foo { pub fn f() {} } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.stderr b/tests/ui/cfg_attr_rustfmt.stderr index c1efd47db90b..08df7b2b39a0 100644 --- a/tests/ui/cfg_attr_rustfmt.stderr +++ b/tests/ui/cfg_attr_rustfmt.stderr @@ -12,5 +12,11 @@ error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes LL | #[cfg_attr(rustfmt, rustfmt_skip)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` -error: aborting due to 2 previous errors +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes + --> $DIR/cfg_attr_rustfmt.rs:43:5 + | +LL | #[cfg_attr(rustfmt, rustfmt::skip)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` + +error: aborting due to 3 previous errors diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index cb7100bc9efa..f936957cb40c 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -1,7 +1,9 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +78,18 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +fn msrv_1_33() { + #![clippy::msrv = "1.33"] + + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let value: i64 = 34; + let _ = u32::try_from(value).is_ok(); +} + fn main() {} diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index ed4e0692388a..77aec713ff31 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -1,7 +1,9 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +78,18 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +fn msrv_1_33() { + #![clippy::msrv = "1.33"] + + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let value: i64 = 34; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + fn main() {} diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 2e518040561c..b2bf7af8daf8 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> $DIR/checked_conversions.rs:15:13 + --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -7,94 +7,100 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = note: `-D clippy::checked-conversions` implied by `-D warnings` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:16:13 + --> $DIR/checked_conversions.rs:18:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:20:13 + --> $DIR/checked_conversions.rs:22:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:21:13 + --> $DIR/checked_conversions.rs:23:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:25:13 + --> $DIR/checked_conversions.rs:27:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:26:13 + --> $DIR/checked_conversions.rs:28:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:32:13 + --> $DIR/checked_conversions.rs:34:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:33:13 + --> $DIR/checked_conversions.rs:35:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:37:13 + --> $DIR/checked_conversions.rs:39:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:38:13 + --> $DIR/checked_conversions.rs:40:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:44:13 + --> $DIR/checked_conversions.rs:46:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:45:13 + --> $DIR/checked_conversions.rs:47:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:49:13 + --> $DIR/checked_conversions.rs:51:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:50:13 + --> $DIR/checked_conversions.rs:52:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:54:13 + --> $DIR/checked_conversions.rs:56:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:55:13 + --> $DIR/checked_conversions.rs:57:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: aborting due to 16 previous errors +error: checked cast can be simplified + --> $DIR/checked_conversions.rs:92:13 + | +LL | let _ = value <= (u32::MAX as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` + +error: aborting due to 17 previous errors diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed index 4eb999e18e64..42ed232d1001 100644 --- a/tests/ui/cloned_instead_of_copied.fixed +++ b/tests/ui/cloned_instead_of_copied.fixed @@ -1,5 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +16,24 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).copied(); // Option::copied needs 1.35 +} + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + let _ = [1].iter().copied(); // Iterator::copied needs 1.36 + let _ = Some(&1).copied(); +} diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs index 894496c0ebbb..471bd9654cc1 100644 --- a/tests/ui/cloned_instead_of_copied.rs +++ b/tests/ui/cloned_instead_of_copied.rs @@ -1,5 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +16,24 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); // Option::copied needs 1.35 +} + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + let _ = Some(&1).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr index e0707d321468..914c9a91e830 100644 --- a/tests/ui/cloned_instead_of_copied.stderr +++ b/tests/ui/cloned_instead_of_copied.stderr @@ -1,5 +1,5 @@ error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:6:24 + --> $DIR/cloned_instead_of_copied.rs:9:24 | LL | let _ = [1].iter().cloned(); | ^^^^^^ help: try: `copied` @@ -7,28 +7,46 @@ LL | let _ = [1].iter().cloned(); = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:7:31 + --> $DIR/cloned_instead_of_copied.rs:10:31 | LL | let _ = vec!["hi"].iter().cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:8:22 + --> $DIR/cloned_instead_of_copied.rs:11:22 | LL | let _ = Some(&1).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:9:34 + --> $DIR/cloned_instead_of_copied.rs:12:34 | LL | let _ = Box::new([1].iter()).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:10:32 + --> $DIR/cloned_instead_of_copied.rs:13:32 | LL | let _ = Box::new(Some(&1)).cloned(); | ^^^^^^ help: try: `copied` -error: aborting due to 5 previous errors +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:31:22 + | +LL | let _ = Some(&1).cloned(); // Option::copied needs 1.35 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:37:24 + | +LL | let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:38:22 + | +LL | let _ = Some(&1).cloned(); + | ^^^^^^ help: try: `copied` + +error: aborting due to 8 previous errors diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed index 7e18d70bae40..3bac738acd65 100644 --- a/tests/ui/err_expect.fixed +++ b/tests/ui/err_expect.fixed @@ -1,5 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +15,17 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let x: Result = Ok(16); + x.err().expect("16"); +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let x: Result = Ok(17); + x.expect_err("17"); +} diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs index bf8c3c9fb8c9..6e7c47d9ad3c 100644 --- a/tests/ui/err_expect.rs +++ b/tests/ui/err_expect.rs @@ -1,5 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +15,17 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let x: Result = Ok(16); + x.err().expect("16"); +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let x: Result = Ok(17); + x.err().expect("17"); +} diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr index ffd97e00a5c0..91a6cf8de65f 100644 --- a/tests/ui/err_expect.stderr +++ b/tests/ui/err_expect.stderr @@ -1,10 +1,16 @@ error: called `.err().expect()` on a `Result` value - --> $DIR/err_expect.rs:10:16 + --> $DIR/err_expect.rs:13: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 +error: called `.err().expect()` on a `Result` value + --> $DIR/err_expect.rs:30:7 + | +LL | x.err().expect("17"); + | ^^^^^^^^^^^^ help: try: `expect_err` + +error: aborting due to 2 previous errors diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed index c3992d7e92cf..41828ddd7acd 100644 --- a/tests/ui/filter_map_next_fixable.fixed +++ b/tests/ui/filter_map_next_fixable.fixed @@ -1,6 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +10,17 @@ fn main() { let element: Option = a.iter().find_map(|s| s.parse().ok()); assert_eq!(element, Some(1)); } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().find_map(|s| s.parse().ok()); +} diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs index 447219a96839..be492a81b45e 100644 --- a/tests/ui/filter_map_next_fixable.rs +++ b/tests/ui/filter_map_next_fixable.rs @@ -1,6 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +10,17 @@ fn main() { let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); assert_eq!(element, Some(1)); } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr index 3bb062ffd7a3..e789efeabd55 100644 --- a/tests/ui/filter_map_next_fixable.stderr +++ b/tests/ui/filter_map_next_fixable.stderr @@ -1,10 +1,16 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> $DIR/filter_map_next_fixable.rs:8:32 + --> $DIR/filter_map_next_fixable.rs:10:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` | = note: `-D clippy::filter-map-next` implied by `-D warnings` -error: aborting due to previous error +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead + --> $DIR/filter_map_next_fixable.rs:25:26 + | +LL | let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` + +error: aborting due to 2 previous errors diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed index e66dc43b0473..1cf49ca45f49 100644 --- a/tests/ui/from_over_into.fixed +++ b/tests/ui/from_over_into.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -59,4 +60,28 @@ impl From for A { } } +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + struct FromOverInto(Vec); + + impl From> for FromOverInto { + fn from(val: Vec) -> Self { + FromOverInto(val) + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 74c7be6af79e..d30f3c3fc925 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -59,4 +60,28 @@ impl From for A { } } +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 6cf83e258071..9c2a7c04c364 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,5 +1,5 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:9:1 + --> $DIR/from_over_into.rs:10:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL ~ StringWrapper(val) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:17:1 + --> $DIR/from_over_into.rs:18:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL ~ SelfType(String::new()) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:32:1 + --> $DIR/from_over_into.rs:33:1 | LL | impl Into for X { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL ~ let _: X = val; | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:44:1 + --> $DIR/from_over_into.rs:45:1 | LL | impl core::convert::Into for crate::ExplicitPaths { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,5 +58,18 @@ LL ~ val.0 = false; LL ~ val.0 | -error: aborting due to 4 previous errors +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:80:5 + | +LL | impl Into> for Vec { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From>` + | +LL ~ impl From> for FromOverInto { +LL ~ fn from(val: Vec) -> Self { +LL ~ FromOverInto(val) + | + +error: aborting due to 5 previous errors diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs index 54fd888af99f..331fd29b74e8 100644 --- a/tests/ui/manual_clamp.rs +++ b/tests/ui/manual_clamp.rs @@ -1,3 +1,4 @@ +#![feature(custom_inner_attributes)] #![warn(clippy::manual_clamp)] #![allow( unused, @@ -302,3 +303,29 @@ fn dont_tell_me_what_to_do() { fn cmp_min_max(input: i32) -> i32 { input * 3 } + +fn msrv_1_49() { + #![clippy::msrv = "1.49"] + + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr index 0604f8606c3f..70abe28091c9 100644 --- a/tests/ui/manual_clamp.stderr +++ b/tests/ui/manual_clamp.stderr @@ -1,5 +1,5 @@ error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:76:5 + --> $DIR/manual_clamp.rs:77:5 | LL | / if x9 < min { LL | | x9 = min; @@ -13,7 +13,7 @@ LL | | } = note: `-D clippy::manual-clamp` implied by `-D warnings` error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:91:5 + --> $DIR/manual_clamp.rs:92:5 | LL | / if x11 > max { LL | | x11 = max; @@ -26,7 +26,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:99:5 + --> $DIR/manual_clamp.rs:100:5 | LL | / if min > x12 { LL | | x12 = min; @@ -39,7 +39,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:107:5 + --> $DIR/manual_clamp.rs:108:5 | LL | / if max < x13 { LL | | x13 = max; @@ -52,7 +52,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:161:5 + --> $DIR/manual_clamp.rs:162:5 | LL | / if max < x33 { LL | | x33 = max; @@ -65,7 +65,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:21:14 + --> $DIR/manual_clamp.rs:22:14 | LL | let x0 = if max < input { | ______________^ @@ -80,7 +80,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:29:14 + --> $DIR/manual_clamp.rs:30:14 | LL | let x1 = if input > max { | ______________^ @@ -95,7 +95,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:37:14 + --> $DIR/manual_clamp.rs:38:14 | LL | let x2 = if input < min { | ______________^ @@ -110,7 +110,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:45:14 + --> $DIR/manual_clamp.rs:46:14 | LL | let x3 = if min > input { | ______________^ @@ -125,7 +125,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:53:14 + --> $DIR/manual_clamp.rs:54:14 | LL | let x4 = input.max(min).min(max); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -133,7 +133,7 @@ LL | let x4 = input.max(min).min(max); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:55:14 + --> $DIR/manual_clamp.rs:56:14 | LL | let x5 = input.min(max).max(min); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -141,7 +141,7 @@ LL | let x5 = input.min(max).max(min); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:57:14 + --> $DIR/manual_clamp.rs:58:14 | LL | let x6 = match input { | ______________^ @@ -154,7 +154,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:63:14 + --> $DIR/manual_clamp.rs:64:14 | LL | let x7 = match input { | ______________^ @@ -167,7 +167,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:69:14 + --> $DIR/manual_clamp.rs:70:14 | LL | let x8 = match input { | ______________^ @@ -180,7 +180,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:83:15 + --> $DIR/manual_clamp.rs:84:15 | LL | let x10 = match input { | _______________^ @@ -193,7 +193,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:114:15 + --> $DIR/manual_clamp.rs:115:15 | LL | let x14 = if input > CONST_MAX { | _______________^ @@ -208,7 +208,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:123:19 + --> $DIR/manual_clamp.rs:124:19 | LL | let x15 = if input > max { | ___________________^ @@ -224,7 +224,7 @@ LL | | }; = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:134:19 + --> $DIR/manual_clamp.rs:135:19 | LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -232,7 +232,7 @@ LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:135:19 + --> $DIR/manual_clamp.rs:136:19 | LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -240,7 +240,7 @@ LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:136:19 + --> $DIR/manual_clamp.rs:137:19 | LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -248,7 +248,7 @@ LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:137:19 + --> $DIR/manual_clamp.rs:138:19 | LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -256,7 +256,7 @@ LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:138:19 + --> $DIR/manual_clamp.rs:139:19 | LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -264,7 +264,7 @@ LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:139:19 + --> $DIR/manual_clamp.rs:140:19 | LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -272,7 +272,7 @@ LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:140:19 + --> $DIR/manual_clamp.rs:141:19 | LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -280,7 +280,7 @@ LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:141:19 + --> $DIR/manual_clamp.rs:142:19 | LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -288,7 +288,7 @@ LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:143:19 + --> $DIR/manual_clamp.rs:144:19 | LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -297,7 +297,7 @@ LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:144:19 + --> $DIR/manual_clamp.rs:145:19 | LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -306,7 +306,7 @@ LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:145:19 + --> $DIR/manual_clamp.rs:146:19 | LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -315,7 +315,7 @@ LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:146:19 + --> $DIR/manual_clamp.rs:147:19 | LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -324,7 +324,7 @@ LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:147:19 + --> $DIR/manual_clamp.rs:148:19 | LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -333,7 +333,7 @@ LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:148:19 + --> $DIR/manual_clamp.rs:149:19 | LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -342,7 +342,7 @@ LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:149:19 + --> $DIR/manual_clamp.rs:150:19 | LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -351,7 +351,7 @@ LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:150:19 + --> $DIR/manual_clamp.rs:151:19 | LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -360,7 +360,7 @@ LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:153:5 + --> $DIR/manual_clamp.rs:154:5 | LL | / if x32 < min { LL | | x32 = min; @@ -371,5 +371,20 @@ LL | | } | = note: clamp will panic if max < min -error: aborting due to 34 previous errors +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:324:13 + | +LL | let _ = if input < min { + | _____________^ +LL | | min +LL | | } else if input > max { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: aborting due to 35 previous errors diff --git a/tests/ui/manual_rem_euclid.fixed b/tests/ui/manual_rem_euclid.fixed index 5601c96c10b2..b942fbfe9305 100644 --- a/tests/ui/manual_rem_euclid.fixed +++ b/tests/ui/manual_rem_euclid.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -53,3 +54,32 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { num.rem_euclid(4) } + +pub fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub fn msrv_1_38() { + #![clippy::msrv = "1.38"] + + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} + +// For const fns: +pub const fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub const fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} diff --git a/tests/ui/manual_rem_euclid.rs b/tests/ui/manual_rem_euclid.rs index 52135be26b73..7462d532169f 100644 --- a/tests/ui/manual_rem_euclid.rs +++ b/tests/ui/manual_rem_euclid.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -53,3 +54,32 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { ((num % 4) + 4) % 4 } + +pub fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub fn msrv_1_38() { + #![clippy::msrv = "1.38"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +// For const fns: +pub const fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub const fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} diff --git a/tests/ui/manual_rem_euclid.stderr b/tests/ui/manual_rem_euclid.stderr index a237fd0213c1..d51bac03b565 100644 --- a/tests/ui/manual_rem_euclid.stderr +++ b/tests/ui/manual_rem_euclid.stderr @@ -1,5 +1,5 @@ error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:19:18 + --> $DIR/manual_rem_euclid.rs:20:18 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -7,31 +7,31 @@ LL | let _: i32 = ((value % 4) + 4) % 4; = note: `-D clippy::manual-rem-euclid` implied by `-D warnings` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:20:18 + --> $DIR/manual_rem_euclid.rs:21:18 | LL | let _: i32 = (4 + (value % 4)) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:21:18 + --> $DIR/manual_rem_euclid.rs:22:18 | LL | let _: i32 = (value % 4 + 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:22:18 + --> $DIR/manual_rem_euclid.rs:23:18 | LL | let _: i32 = (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:23:22 + --> $DIR/manual_rem_euclid.rs:24:22 | LL | let _: i32 = 1 + (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:12:22 + --> $DIR/manual_rem_euclid.rs:13:22 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -42,16 +42,28 @@ LL | internal_rem_euclid!(); = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info) error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:49:5 + --> $DIR/manual_rem_euclid.rs:50:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:54:5 + --> $DIR/manual_rem_euclid.rs:55:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` -error: aborting due to 8 previous errors +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:69:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:84:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: aborting due to 10 previous errors diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index cbb84eb5c7e3..85009d78558b 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -1,3 +1,4 @@ +#![feature(custom_inner_attributes)] #![warn(clippy::manual_strip)] fn main() { @@ -64,3 +65,21 @@ fn main() { s4[2..].to_string(); } } + +fn msrv_1_44() { + #![clippy::msrv = "1.44"] + + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} + +fn msrv_1_45() { + #![clippy::msrv = "1.45"] + + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index 2191ccb85dd5..ad2a362f3e76 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -1,11 +1,11 @@ error: stripping a prefix manually - --> $DIR/manual_strip.rs:7:24 + --> $DIR/manual_strip.rs:8:24 | LL | str::to_string(&s["ab".len()..]); | ^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:6:5 + --> $DIR/manual_strip.rs:7:5 | LL | if s.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL ~ .to_string(); | error: stripping a suffix manually - --> $DIR/manual_strip.rs:15:24 + --> $DIR/manual_strip.rs:16:24 | LL | str::to_string(&s[..s.len() - "bc".len()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the suffix was tested here - --> $DIR/manual_strip.rs:14:5 + --> $DIR/manual_strip.rs:15:5 | LL | if s.ends_with("bc") { | ^^^^^^^^^^^^^^^^^^^^^ @@ -42,13 +42,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:24:24 + --> $DIR/manual_strip.rs:25:24 | LL | str::to_string(&s[1..]); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:23:5 + --> $DIR/manual_strip.rs:24:5 | LL | if s.starts_with('a') { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -60,13 +60,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:31:24 + --> $DIR/manual_strip.rs:32:24 | LL | str::to_string(&s[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:30:5 + --> $DIR/manual_strip.rs:31:5 | LL | if s.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,13 +77,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:37:24 + --> $DIR/manual_strip.rs:38:24 | LL | str::to_string(&s[PREFIX.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:36:5 + --> $DIR/manual_strip.rs:37:5 | LL | if s.starts_with(PREFIX) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,13 +95,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:44:24 + --> $DIR/manual_strip.rs:45:24 | LL | str::to_string(&TARGET[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:43:5 + --> $DIR/manual_strip.rs:44:5 | LL | if TARGET.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,13 +112,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:50:9 + --> $DIR/manual_strip.rs:51:9 | LL | s1[2..].to_uppercase(); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:49:5 + --> $DIR/manual_strip.rs:50:5 | LL | if s1.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,5 +128,22 @@ LL ~ if let Some() = s1.strip_prefix("ab") { LL ~ .to_uppercase(); | -error: aborting due to 7 previous errors +error: stripping a prefix manually + --> $DIR/manual_strip.rs:83:9 + | +LL | s[1..].to_string(); + | ^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:82:5 + | +LL | if s.starts_with('a') { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL ~ if let Some() = s.strip_prefix('a') { +LL ~ .to_string(); + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 5429fb4e454e..396b22a9abb3 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,6 +1,8 @@ // aux-build:option_helpers.rs + +#![feature(custom_inner_attributes)] #![warn(clippy::map_unwrap_or)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)] #[macro_use] extern crate option_helpers; @@ -79,3 +81,19 @@ fn main() { option_methods(); result_methods(); } + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index abc9c1ece327..d17d24a403ea 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,5 +1,5 @@ error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:16:13 + --> $DIR/map_unwrap_or.rs:18:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -15,7 +15,7 @@ LL + let _ = opt.map_or(0, |x| x + 1); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:20:13 + --> $DIR/map_unwrap_or.rs:22:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -33,7 +33,7 @@ LL ~ ); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:24:13 + --> $DIR/map_unwrap_or.rs:26:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -50,7 +50,7 @@ LL ~ }, |x| x + 1); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:29:13 + --> $DIR/map_unwrap_or.rs:31:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + let _ = opt.and_then(|x| Some(x + 1)); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:31:13 + --> $DIR/map_unwrap_or.rs:33:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -80,7 +80,7 @@ LL ~ ); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:35:13 + --> $DIR/map_unwrap_or.rs:37:13 | LL | let _ = opt | _____________^ @@ -95,7 +95,7 @@ LL + .and_then(|x| Some(x + 1)); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:46:13 + --> $DIR/map_unwrap_or.rs:48:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL + let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:50:13 + --> $DIR/map_unwrap_or.rs:52:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -117,7 +117,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:54:13 + --> $DIR/map_unwrap_or.rs:56:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -127,7 +127,7 @@ LL | | ); | |_________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:66:13 + --> $DIR/map_unwrap_or.rs:68:13 | LL | let _ = res.map(|x| { | _____________^ @@ -137,7 +137,7 @@ LL | | ).unwrap_or_else(|_e| 0); | |____________________________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:70:13 + --> $DIR/map_unwrap_or.rs:72:13 | LL | let _ = res.map(|x| x + 1) | _____________^ @@ -146,5 +146,11 @@ LL | | 0 LL | | }); | |__________^ -error: aborting due to 11 previous errors +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:98:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: aborting due to 12 previous errors diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 95ca571d07bf..2498007694c5 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] @@ -193,3 +194,18 @@ fn main() { _ => false, }; } + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn msrv_1_42() { + #![clippy::msrv = "1.42"] + + let _y = matches!(Some(5), Some(0)); +} diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 3b9c8cadadcc..b4e48499bd0f 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] @@ -234,3 +235,21 @@ fn main() { _ => false, }; } + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn msrv_1_42() { + #![clippy::msrv = "1.42"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index e94555e27448..f1d1c23aeb0d 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:10:14 + --> $DIR/match_expr_like_matches_macro.rs:11:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:16:14 + --> $DIR/match_expr_like_matches_macro.rs:17:14 | LL | let _w = match x { | ______________^ @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: try this: `matches!(x, Some(_))` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:22:14 + --> $DIR/match_expr_like_matches_macro.rs:23:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:28:15 + --> $DIR/match_expr_like_matches_macro.rs:29:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:34:16 + --> $DIR/match_expr_like_matches_macro.rs:35:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:58:20 + --> $DIR/match_expr_like_matches_macro.rs:59:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:68:20 + --> $DIR/match_expr_like_matches_macro.rs:69:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:78:20 + --> $DIR/match_expr_like_matches_macro.rs:79:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:138:18 + --> $DIR/match_expr_like_matches_macro.rs:139:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try this: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:147:18 + --> $DIR/match_expr_like_matches_macro.rs:148:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try this: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:164:21 + --> $DIR/match_expr_like_matches_macro.rs:165:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try this: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:178:20 + --> $DIR/match_expr_like_matches_macro.rs:179:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:190:20 + --> $DIR/match_expr_like_matches_macro.rs:191:20 | LL | let _res = match &val { | ____________________^ @@ -133,5 +133,15 @@ LL | | _ => false, LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` -error: aborting due to 13 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:251:14 + | +LL | let _y = match Some(5) { + | ______________^ +LL | | Some(0) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(Some(5), Some(0))` + +error: aborting due to 14 previous errors diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index b609ba659467..ae237395b95f 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -1,5 +1,7 @@ // run-rustfix -#![allow(unused_imports)] + +#![feature(custom_inner_attributes)] +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +79,17 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let mut s = String::from("foo"); + let _ = std::mem::take(&mut s); +} diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 93f6dcdec83b..3202e99e0be9 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -1,5 +1,7 @@ // run-rustfix -#![allow(unused_imports)] + +#![feature(custom_inner_attributes)] +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +79,17 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index 90dc6c95f858..dd8a50dab900 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:15:13 + --> $DIR/mem_replace.rs:17:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -7,13 +7,13 @@ LL | let _ = mem::replace(&mut an_option, None); = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:17:13 + --> $DIR/mem_replace.rs:19:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:22:13 + --> $DIR/mem_replace.rs:24:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,100 +21,106 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:25:13 + --> $DIR/mem_replace.rs:27:13 | LL | let _ = std::mem::replace(s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:26:13 + --> $DIR/mem_replace.rs:28:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:29:13 + --> $DIR/mem_replace.rs:31:13 | LL | let _ = std::mem::replace(&mut v, Vec::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:30:13 + --> $DIR/mem_replace.rs:32:13 | LL | let _ = std::mem::replace(&mut v, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:31:13 + --> $DIR/mem_replace.rs:33:13 | LL | let _ = std::mem::replace(&mut v, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:32:13 + --> $DIR/mem_replace.rs:34:13 | LL | let _ = std::mem::replace(&mut v, vec![]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:35:13 + --> $DIR/mem_replace.rs:37:13 | LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:38:13 + --> $DIR/mem_replace.rs:40:13 | LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:41:13 + --> $DIR/mem_replace.rs:43:13 | LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:44:13 + --> $DIR/mem_replace.rs:46:13 | LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:47:13 + --> $DIR/mem_replace.rs:49:13 | LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:50:13 + --> $DIR/mem_replace.rs:52:13 | LL | let _ = std::mem::replace(&mut list, LinkedList::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:53:13 + --> $DIR/mem_replace.rs:55:13 | LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:56:13 + --> $DIR/mem_replace.rs:58:13 | LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:59:13 + --> $DIR/mem_replace.rs:61:13 | LL | let _ = std::mem::replace(&mut refstr, ""); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:62:13 + --> $DIR/mem_replace.rs:64:13 | LL | let _ = std::mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` -error: aborting due to 19 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:94:13 + | +LL | let _ = std::mem::replace(&mut s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` + +error: aborting due to 20 previous errors diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index c4c6391bb4c1..cd148063bf06 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -1,240 +1,29 @@ #![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] -#![clippy::msrv = "1.0.0"] -use std::ops::{Deref, RangeFrom}; +fn main() {} -fn approx_const() { +fn just_under_msrv() { + #![clippy::msrv = "1.42.0"] let log2_10 = 3.321928094887362; - let log10_2 = 0.301029995663981; } -fn cloned_instead_of_copied() { - let _ = [1].iter().cloned(); +fn meets_msrv() { + #![clippy::msrv = "1.43.0"] + let log2_10 = 3.321928094887362; } -fn option_as_ref_deref() { - let mut opt = Some(String::from("123")); - - let _ = opt.as_ref().map(String::as_str); - let _ = opt.as_ref().map(|x| x.as_str()); - let _ = opt.as_mut().map(String::as_mut_str); - let _ = opt.as_mut().map(|x| x.as_mut_str()); -} - -fn match_like_matches() { - let _y = match Some(5) { - Some(0) => true, - _ => false, - }; -} - -fn match_same_arms() { - match (1, 2, 3) { - (1, .., 3) => 42, - (.., 3) => 42, //~ ERROR match arms have same body - _ => 0, - }; -} - -fn match_same_arms2() { - let _ = match Some(42) { - Some(_) => 24, - None => 24, //~ ERROR match arms have same body - }; -} - -pub fn manual_strip_msrv() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } -} - -pub fn redundant_fieldnames() { - let start = 0; - let _ = RangeFrom { start: start }; -} - -pub fn redundant_static_lifetime() { - const VAR_ONE: &'static str = "Test constant #1"; -} - -pub fn checked_conversion() { - let value: i64 = 42; - let _ = value <= (u32::max_value() as i64) && value >= 0; - let _ = value <= (u32::MAX as i64) && value >= 0; -} - -pub struct FromOverInto(String); - -impl Into for String { - fn into(self) -> FromOverInto { - FromOverInto(self) - } -} - -pub fn filter_map_next() { - let a = ["1", "lol", "3", "NaN", "5"]; - - #[rustfmt::skip] - let _: Option = vec![1, 2, 3, 4, 5, 6] - .into_iter() - .filter_map(|x| { - if x == 2 { - Some(x * 2) - } else { - None - } - }) - .next(); -} - -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] -pub fn manual_range_contains() { - let x = 5; - x >= 8 && x < 12; -} - -pub fn use_self() { - struct Foo; - - impl Foo { - fn new() -> Foo { - Foo {} - } - fn test() -> Foo { - Foo::new() - } - } -} - -fn replace_with_default() { - let mut s = String::from("foo"); - let _ = std::mem::replace(&mut s, String::default()); -} - -fn map_unwrap_or() { - let opt = Some(1); - - // Check for `option.map(_).unwrap_or(_)` use. - // Single line case. - let _ = opt - .map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or(0); -} - -// Could be const -fn missing_const_for_fn() -> i32 { - 1 -} - -fn unnest_or_patterns() { - struct TS(u8, u8); - if let TS(0, x) | TS(1, x) = TS(0, 0) {} -} - -#[cfg_attr(rustfmt, rustfmt_skip)] -fn deprecated_cfg_attr() {} - -#[warn(clippy::cast_lossless)] -fn int_from_bool() -> u8 { - true as u8 -} - -fn err_expect() { - let x: Result = Ok(10); - x.err().expect("Testing expect_err"); -} - -fn cast_abs_to_unsigned() { - let x: i32 = 10; - assert_eq!(10u32, x.abs() as u32); -} - -fn manual_rem_euclid() { - let x: i32 = 10; - let _: i32 = ((x % 4) + 4) % 4; -} - -fn manual_clamp() { - let (input, min, max) = (0, -1, 2); - let _ = if input < min { - min - } else if input > max { - max - } else { - input - }; -} - -fn main() { - filter_map_next(); - checked_conversion(); - redundant_fieldnames(); - redundant_static_lifetime(); - option_as_ref_deref(); - match_like_matches(); - match_same_arms(); - match_same_arms2(); - manual_strip_msrv(); - manual_range_contains(); - use_self(); - replace_with_default(); - map_unwrap_or(); - missing_const_for_fn(); - unnest_or_patterns(); - int_from_bool(); - err_expect(); - cast_abs_to_unsigned(); - manual_rem_euclid(); - manual_clamp(); -} - -mod just_under_msrv { - #![feature(custom_inner_attributes)] +fn just_above_msrv() { #![clippy::msrv = "1.44.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } + let log2_10 = 3.321928094887362; } -mod meets_msrv { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.45.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } +fn no_patch_under() { + #![clippy::msrv = "1.42"] + let log2_10 = 3.321928094887362; } -mod just_above_msrv { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.46.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } -} - -mod const_rem_euclid { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.50.0"] - - pub const fn const_rem_euclid_4(num: i32) -> i32 { - ((num % 4) + 4) % 4 - } +fn no_patch_meets() { + #![clippy::msrv = "1.43"] + let log2_10 = 3.321928094887362; } diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index d1cffc26a831..68aa58748190 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,37 +1,27 @@ -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:216:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:13:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:215:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `-D clippy::manual-strip` implied by `-D warnings` -help: try using the `strip_prefix` method - | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly + = note: `#[deny(clippy::approx_constant)]` on by default -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:228:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:18:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:227:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method - | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly -error: aborting due to 2 previous errors +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:28:19 + | +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly + +error: aborting due to 3 previous errors diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs index f20841891a74..02892f329af6 100644 --- a/tests/ui/min_rust_version_invalid_attr.rs +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -2,3 +2,17 @@ #![clippy::msrv = "invalid.version"] fn main() {} + +#[clippy::msrv = "invalid.version"] +fn outer_attr() {} + +mod multiple { + #![clippy::msrv = "1.40"] + #![clippy::msrv = "=1.35.0"] + #![clippy::msrv = "1.10.1"] + + mod foo { + #![clippy::msrv = "1"] + #![clippy::msrv = "1.0.0"] + } +} diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr index 6ff88ca56f8b..93370a0fa9c9 100644 --- a/tests/ui/min_rust_version_invalid_attr.stderr +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -4,5 +4,47 @@ error: `invalid.version` is not a valid Rust version LL | #![clippy::msrv = "invalid.version"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: `msrv` cannot be an outer attribute + --> $DIR/min_rust_version_invalid_attr.rs:6:1 + | +LL | #[clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:11:5 + | +LL | #![clippy::msrv = "=1.35.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:12:5 + | +LL | #![clippy::msrv = "1.10.1"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:16:9 + | +LL | #![clippy::msrv = "1.0.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:15:9 + | +LL | #![clippy::msrv = "1"] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/min_rust_version_multiple_inner_attr.rs b/tests/ui/min_rust_version_multiple_inner_attr.rs deleted file mode 100644 index e882d5ccf91a..000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(custom_inner_attributes)] -#![clippy::msrv = "1.40"] -#![clippy::msrv = "=1.35.0"] -#![clippy::msrv = "1.10.1"] - -mod foo { - #![clippy::msrv = "1"] - #![clippy::msrv = "1.0.0"] -} - -fn main() {} diff --git a/tests/ui/min_rust_version_multiple_inner_attr.stderr b/tests/ui/min_rust_version_multiple_inner_attr.stderr deleted file mode 100644 index e3ff6605cde8..000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:3:1 - | -LL | #![clippy::msrv = "=1.35.0"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 - | -LL | #![clippy::msrv = "1.40"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:4:1 - | -LL | #![clippy::msrv = "1.10.1"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 - | -LL | #![clippy::msrv = "1.40"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:8:5 - | -LL | #![clippy::msrv = "1.0.0"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:7:5 - | -LL | #![clippy::msrv = "1"] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs deleted file mode 100644 index 98fffe1e3512..000000000000 --- a/tests/ui/min_rust_version_no_patch.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![allow(clippy::redundant_clone)] -#![feature(custom_inner_attributes)] -#![clippy::msrv = "1.0"] - -fn manual_strip_msrv() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } -} - -fn main() { - manual_strip_msrv() -} diff --git a/tests/ui/min_rust_version_outer_attr.rs b/tests/ui/min_rust_version_outer_attr.rs deleted file mode 100644 index 551948bd72ef..000000000000 --- a/tests/ui/min_rust_version_outer_attr.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![feature(custom_inner_attributes)] - -#[clippy::msrv = "invalid.version"] -fn main() {} diff --git a/tests/ui/min_rust_version_outer_attr.stderr b/tests/ui/min_rust_version_outer_attr.stderr deleted file mode 100644 index 579ee7a87d23..000000000000 --- a/tests/ui/min_rust_version_outer_attr.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: `msrv` cannot be an outer attribute - --> $DIR/min_rust_version_outer_attr.rs:3:1 - | -LL | #[clippy::msrv = "invalid.version"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 88f6935d224a..b85e88784918 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -77,5 +77,17 @@ mod const_fn_stabilized_before_msrv { } } +fn msrv_1_45() -> i32 { + #![clippy::msrv = "1.45"] + + 45 +} + +fn msrv_1_46() -> i32 { + #![clippy::msrv = "1.46"] + + 46 +} + // Should not be const fn main() {} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 3eb52b682747..f8e221c82f1a 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -81,5 +81,15 @@ LL | | byte.is_ascii_digit(); LL | | } | |_____^ -error: aborting due to 10 previous errors +error: this could be a `const fn` + --> $DIR/could_be_const.rs:86:1 + | +LL | / fn msrv_1_46() -> i32 { +LL | | #![clippy::msrv = "1.46"] +LL | | +LL | | 46 +LL | | } + | |_^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index 07d7f0b45b0c..bc376d0d7fb3 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -42,3 +43,17 @@ fn main() { // Issue #5927 let _ = opt.as_deref(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let opt = Some(String::from("123")); + let _ = opt.as_deref(); +} diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index 6ae059c9425d..ba3a2eedc225 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -45,3 +46,17 @@ fn main() { // Issue #5927 let _ = opt.as_ref().map(std::ops::Deref::deref); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index 62f282324752..7de8b3b6ba43 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -1,5 +1,5 @@ error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:13:13 + --> $DIR/option_as_ref_deref.rs:14:13 | LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()` @@ -7,7 +7,7 @@ LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); = note: `-D clippy::option-as-ref-deref` implied by `-D warnings` error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:16:13 + --> $DIR/option_as_ref_deref.rs:17:13 | LL | let _ = opt.clone() | _____________^ @@ -17,94 +17,100 @@ LL | | ) | |_________^ help: try using as_deref instead: `opt.clone().as_deref()` error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:22:13 + --> $DIR/option_as_ref_deref.rs:23:13 | LL | let _ = opt.as_mut().map(DerefMut::deref_mut); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:24:13 + --> $DIR/option_as_ref_deref.rs:25:13 | LL | let _ = opt.as_ref().map(String::as_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:25:13 + --> $DIR/option_as_ref_deref.rs:26:13 | LL | let _ = opt.as_ref().map(|x| x.as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:26:13 + --> $DIR/option_as_ref_deref.rs:27:13 | LL | let _ = opt.as_mut().map(String::as_mut_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:27:13 + --> $DIR/option_as_ref_deref.rs:28:13 | LL | let _ = opt.as_mut().map(|x| x.as_mut_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:28:13 + --> $DIR/option_as_ref_deref.rs:29:13 | LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()` error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:29:13 + --> $DIR/option_as_ref_deref.rs:30:13 | LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()` error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:30:13 + --> $DIR/option_as_ref_deref.rs:31:13 | LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()` error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:31:13 + --> $DIR/option_as_ref_deref.rs:32:13 | LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()` error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:32:13 + --> $DIR/option_as_ref_deref.rs:33:13 | LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()` error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:34:13 + --> $DIR/option_as_ref_deref.rs:35:13 | LL | let _ = opt.as_ref().map(|x| x.deref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:35:13 + --> $DIR/option_as_ref_deref.rs:36:13 | LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()` error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:42:13 + --> $DIR/option_as_ref_deref.rs:43:13 | LL | let _ = opt.as_ref().map(|x| &**x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:43:13 + --> $DIR/option_as_ref_deref.rs:44:13 | LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:46:13 + --> $DIR/option_as_ref_deref.rs:47:13 | LL | let _ = opt.as_ref().map(std::ops::Deref::deref); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` -error: aborting due to 17 previous errors +error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:61:13 + | +LL | let _ = opt.as_ref().map(String::as_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 85d021b2f25e..824f00cb99e8 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -1,10 +1,12 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_range_contains)] +#![allow(unused)] +#![allow(clippy::no_effect)] +#![allow(clippy::short_circuit_statement)] +#![allow(clippy::unnecessary_operation)] + fn main() { let x = 9_i32; @@ -62,3 +64,17 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let x = 5; + x >= 8 && x < 34; +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let x = 5; + (8..35).contains(&x); +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 9a7a75dc1325..df925eeadfe5 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -1,10 +1,12 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_range_contains)] +#![allow(unused)] +#![allow(clippy::no_effect)] +#![allow(clippy::short_circuit_statement)] +#![allow(clippy::unnecessary_operation)] + fn main() { let x = 9_i32; @@ -62,3 +64,17 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let x = 5; + x >= 8 && x < 34; +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let x = 5; + x >= 8 && x < 35; +} diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 936859db5a12..9689e665b05c 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -1,5 +1,5 @@ error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:12:5 + --> $DIR/range_contains.rs:14:5 | LL | x >= 8 && x < 12; | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` @@ -7,118 +7,124 @@ LL | x >= 8 && x < 12; = note: `-D clippy::manual-range-contains` implied by `-D warnings` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:13:5 + --> $DIR/range_contains.rs:15:5 | LL | x < 42 && x >= 21; | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:14:5 + --> $DIR/range_contains.rs:16:5 | LL | 100 > x && 1 <= x; | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:17:5 + --> $DIR/range_contains.rs:19:5 | LL | x >= 9 && x <= 99; | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:18:5 + --> $DIR/range_contains.rs:20:5 | LL | x <= 33 && x >= 1; | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:19:5 + --> $DIR/range_contains.rs:21:5 | LL | 999 >= x && 1 <= x; | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:22:5 + --> $DIR/range_contains.rs:24:5 | LL | x < 8 || x >= 12; | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:23:5 + --> $DIR/range_contains.rs:25:5 | LL | x >= 42 || x < 21; | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:24:5 + --> $DIR/range_contains.rs:26:5 | LL | 100 <= x || 1 > x; | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:27:5 + --> $DIR/range_contains.rs:29:5 | LL | x < 9 || x > 99; | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:28:5 + --> $DIR/range_contains.rs:30:5 | LL | x > 33 || x < 1; | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:29:5 + --> $DIR/range_contains.rs:31:5 | LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:44:5 + --> $DIR/range_contains.rs:46:5 | LL | y >= 0. && y < 1.; | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:45:5 + --> $DIR/range_contains.rs:47:5 | LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:48:5 + --> $DIR/range_contains.rs:50:5 | LL | x >= -10 && x <= 10; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:50:5 + --> $DIR/range_contains.rs:52:5 | LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:30 + --> $DIR/range_contains.rs:57:30 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:5 + --> $DIR/range_contains.rs:57:5 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:29 + --> $DIR/range_contains.rs:58:29 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:5 + --> $DIR/range_contains.rs:58:5 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` -error: aborting due to 20 previous errors +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:79:5 + | +LL | x >= 8 && x < 35; + | ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)` + +error: aborting due to 21 previous errors diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index 5b4b8eeedd46..34ab552cb1d8 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,4 +1,6 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -69,3 +71,17 @@ fn issue_3476() { S { foo: foo:: }; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let start = 0; + let _ = RangeFrom { start: start }; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let start = 0; + let _ = RangeFrom { start }; +} diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 3f97b80c5682..a051b1f96f0f 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,4 +1,6 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -69,3 +71,17 @@ fn issue_3476() { S { foo: foo:: }; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let start = 0; + let _ = RangeFrom { start: start }; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let start = 0; + let _ = RangeFrom { start: start }; +} diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 7976292df224..8b82e062b93a 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -1,5 +1,5 @@ error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:34:9 + --> $DIR/redundant_field_names.rs:36:9 | LL | gender: gender, | ^^^^^^^^^^^^^^ help: replace it with: `gender` @@ -7,40 +7,46 @@ LL | gender: gender, = note: `-D clippy::redundant-field-names` implied by `-D warnings` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:35:9 + --> $DIR/redundant_field_names.rs:37:9 | LL | age: age, | ^^^^^^^^ help: replace it with: `age` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:56:25 + --> $DIR/redundant_field_names.rs:58:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:57:23 + --> $DIR/redundant_field_names.rs:59:23 | LL | let _ = RangeTo { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:21 + --> $DIR/redundant_field_names.rs:60:21 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:35 + --> $DIR/redundant_field_names.rs:60:35 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:60:32 + --> $DIR/redundant_field_names.rs:62:32 | LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` -error: aborting due to 7 previous errors +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:86:25 + | +LL | let _ = RangeFrom { start: start }; + | ^^^^^^^^^^^^ help: replace it with: `start` + +error: aborting due to 8 previous errors diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index acc8f1e25b6e..42110dbe81e8 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -54,3 +55,15 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + static V: &'static u8 = &16; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + static V: &u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index f2f0f78659c9..bc5200bc8625 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -54,3 +55,15 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + static V: &'static u8 = &16; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + static V: &'static u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 649831f9c069..735113460d28 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -1,5 +1,5 @@ error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:8:17 + --> $DIR/redundant_static_lifetimes.rs:9:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` @@ -7,94 +7,100 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:12:21 + --> $DIR/redundant_static_lifetimes.rs:13:21 | LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:14:32 + --> $DIR/redundant_static_lifetimes.rs:15:32 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:14:47 + --> $DIR/redundant_static_lifetimes.rs:15:47 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:16:17 + --> $DIR/redundant_static_lifetimes.rs:17:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:18:20 + --> $DIR/redundant_static_lifetimes.rs:19:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:20:19 + --> $DIR/redundant_static_lifetimes.rs:21:19 | LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:22:19 + --> $DIR/redundant_static_lifetimes.rs:23:19 | LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:24:19 + --> $DIR/redundant_static_lifetimes.rs:25:19 | LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:26:25 + --> $DIR/redundant_static_lifetimes.rs:27:25 | LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:30:29 + --> $DIR/redundant_static_lifetimes.rs:31:29 | LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:32:25 + --> $DIR/redundant_static_lifetimes.rs:33:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:34:28 + --> $DIR/redundant_static_lifetimes.rs:35:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:36:27 + --> $DIR/redundant_static_lifetimes.rs:37:27 | LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:38:27 + --> $DIR/redundant_static_lifetimes.rs:39:27 | LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:40:27 + --> $DIR/redundant_static_lifetimes.rs:41:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` -error: aborting due to 16 previous errors +error: statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:68:16 + | +LL | static V: &'static u8 = &17; + | -^^^^^^^--- help: consider removing `'static`: `&u8` + +error: aborting due to 17 previous errors diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index c223b5bc711b..9786c7b12128 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -1,9 +1,9 @@ // run-rustfix -#![feature(box_patterns)] +#![feature(box_patterns, custom_inner_attributes)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,15 @@ fn main() { if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + if let [1] | [52] = [0] {} +} + +fn msrv_1_53() { + #![clippy::msrv = "1.53"] + + if let [1 | 53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 04cd11036e4e..f57322396d4a 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -1,9 +1,9 @@ // run-rustfix -#![feature(box_patterns)] +#![feature(box_patterns, custom_inner_attributes)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,15 @@ fn main() { if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + if let [1] | [52] = [0] {} +} + +fn msrv_1_53() { + #![clippy::msrv = "1.53"] + + if let [1] | [53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index 453c66cbba8f..fbc12fff0b0e 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -175,5 +175,16 @@ help: nest the patterns LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} | ~~~~~~~~~~~~~~~~~ -error: aborting due to 16 previous errors +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:46:12 + | +LL | if let [1] | [53] = [0] {} + | ^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let [1 | 53] = [0] {} + | ~~~~~~~~ + +error: aborting due to 17 previous errors diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 37986187da17..3b54fe9d5ff3 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -617,3 +618,35 @@ mod issue6902 { Bar = 1, } } + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + Self::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 1b2b3337c92e..bf87633cd2d8 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -617,3 +618,35 @@ mod issue6902 { Bar = 1, } } + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index f06bb959b3bd..16fb0609242c 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,5 +1,5 @@ error: unnecessary structure name repetition - --> $DIR/use_self.rs:22:21 + --> $DIR/use_self.rs:23:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -7,244 +7,250 @@ LL | fn new() -> Foo { = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:23:13 + --> $DIR/use_self.rs:24:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:25:22 + --> $DIR/use_self.rs:26:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:26:13 + --> $DIR/use_self.rs:27:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:31:25 + --> $DIR/use_self.rs:32:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:32:13 + --> $DIR/use_self.rs:33:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:97:24 + --> $DIR/use_self.rs:98:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:97:55 + --> $DIR/use_self.rs:98:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:112:13 + --> $DIR/use_self.rs:113:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:147:29 + --> $DIR/use_self.rs:148:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:21 + --> $DIR/use_self.rs:149:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:159:21 + --> $DIR/use_self.rs:160:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:160:13 + --> $DIR/use_self.rs:161:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:177:21 + --> $DIR/use_self.rs:178:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:178:21 + --> $DIR/use_self.rs:179:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:179:21 + --> $DIR/use_self.rs:180:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:221:13 + --> $DIR/use_self.rs:222:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:222:13 + --> $DIR/use_self.rs:223:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:224:13 + --> $DIR/use_self.rs:225:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:243:13 + --> $DIR/use_self.rs:244:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:257:25 + --> $DIR/use_self.rs:258:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:13 + --> $DIR/use_self.rs:259:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:262:16 + --> $DIR/use_self.rs:263:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:262:22 + --> $DIR/use_self.rs:263:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:285:29 + --> $DIR/use_self.rs:286:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:286:13 + --> $DIR/use_self.rs:287:13 | LL | Foo:: { value } | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:458:13 + --> $DIR/use_self.rs:459:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:495:13 + --> $DIR/use_self.rs:496:13 | LL | S2::new() | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:532:17 + --> $DIR/use_self.rs:533:17 | LL | Foo::Bar => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:533:17 + --> $DIR/use_self.rs:534:17 | LL | Foo::Baz => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:539:20 + --> $DIR/use_self.rs:540:20 | LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:563:17 + --> $DIR/use_self.rs:564:17 | LL | Something::Num(n) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:564:17 + --> $DIR/use_self.rs:565:17 | LL | Something::TupleNums(n, _m) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:565:17 + --> $DIR/use_self.rs:566:17 | LL | Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:571:17 + --> $DIR/use_self.rs:572:17 | LL | crate::issue8845::Something::Num(n) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:572:17 + --> $DIR/use_self.rs:573:17 | LL | crate::issue8845::Something::TupleNums(n, _m) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:573:17 + --> $DIR/use_self.rs:574:17 | LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:589:17 + --> $DIR/use_self.rs:590:17 | LL | let Foo(x) = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:594:17 + --> $DIR/use_self.rs:595:17 | LL | let crate::issue8845::Foo(x) = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:601:17 + --> $DIR/use_self.rs:602:17 | LL | let Bar { x, .. } = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:606:17 + --> $DIR/use_self.rs:607:17 | LL | let crate::issue8845::Bar { x, .. } = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` -error: aborting due to 41 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:648:17 + | +LL | E::A => {}, + | ^ help: use the applicable keyword: `Self` + +error: aborting due to 42 previous errors From 7280f3d28aa139cec0c75072a3e66294b7f99b59 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 21 Oct 2022 17:44:35 -0700 Subject: [PATCH 0187/1126] Truncate thread names on Linux and Apple targets These targets have system limits on the thread names, 16 and 64 bytes respectively, and `pthread_setname_np` returns an error if the name is longer. However, we're not in a context that can propagate errors when we call this, and we used to implicitly truncate on Linux with `prctl`, so now we manually truncate these names ahead of time. --- library/std/src/sys/unix/thread.rs | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 42ac6fcd8bf3..6a533854fad3 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -132,8 +132,11 @@ impl Thread { #[cfg(target_os = "linux")] pub fn set_name(name: &CStr) { + const TASK_COMM_LEN: usize = 16; + unsafe { // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20. + let name = truncate_cstr(name, TASK_COMM_LEN); libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); } } @@ -148,6 +151,7 @@ impl Thread { #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] pub fn set_name(name: &CStr) { unsafe { + let name = truncate_cstr(name, libc::MAXTHREADNAMESIZE); libc::pthread_setname_np(name.as_ptr()); } } @@ -276,6 +280,20 @@ impl Drop for Thread { } } +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))] +fn truncate_cstr(cstr: &CStr, max_with_nul: usize) -> crate::borrow::Cow<'_, CStr> { + use crate::{borrow::Cow, ffi::CString}; + + if cstr.to_bytes_with_nul().len() > max_with_nul { + let bytes = cstr.to_bytes()[..max_with_nul - 1].to_vec(); + // SAFETY: the non-nul bytes came straight from a CStr. + // (CString will add the terminating nul.) + Cow::Owned(unsafe { CString::from_vec_unchecked(bytes) }) + } else { + Cow::Borrowed(cstr) + } +} + pub fn available_parallelism() -> io::Result { cfg_if::cfg_if! { if #[cfg(any( @@ -902,3 +920,28 @@ fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { 2048 // just a guess } + +#[test] +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))] +fn test_named_thread_truncation() { + use crate::thread::{self, Builder}; + + let long_name = crate::iter::once("test_named_thread_truncation") + .chain(crate::iter::repeat(" yada").take(100)) + .collect::(); + + let result = Builder::new().name(long_name.clone()).spawn(move || { + // Rust remembers the full thread name itself. + assert_eq!(thread::current().name(), Some(long_name.as_str())); + + // But the kernel is limited -- make sure we successfully set a truncation. + let mut buf = vec![0u8; long_name.len() + 1]; + unsafe { + libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()); + } + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + assert!(cstr.to_bytes().len() > 0); + assert!(long_name.as_bytes().starts_with(cstr.to_bytes())); + }); + result.unwrap().join().unwrap(); +} From 12e45846ebbc32bd6a56f2de2658d3d9ad459032 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 21 Oct 2022 18:13:22 -0700 Subject: [PATCH 0188/1126] Move truncation next to other thread tests for tidy --- library/std/src/sys/unix/thread.rs | 25 ------------------------- library/std/src/thread/tests.rs | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 6a533854fad3..69cd2b500a1f 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -920,28 +920,3 @@ fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { 2048 // just a guess } - -#[test] -#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))] -fn test_named_thread_truncation() { - use crate::thread::{self, Builder}; - - let long_name = crate::iter::once("test_named_thread_truncation") - .chain(crate::iter::repeat(" yada").take(100)) - .collect::(); - - let result = Builder::new().name(long_name.clone()).spawn(move || { - // Rust remembers the full thread name itself. - assert_eq!(thread::current().name(), Some(long_name.as_str())); - - // But the kernel is limited -- make sure we successfully set a truncation. - let mut buf = vec![0u8; long_name.len() + 1]; - unsafe { - libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()); - } - let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); - assert!(cstr.to_bytes().len() > 0); - assert!(long_name.as_bytes().starts_with(cstr.to_bytes())); - }); - result.unwrap().join().unwrap(); -} diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index dfb8765ab4ee..71eb41cd564d 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -37,6 +37,31 @@ fn test_named_thread() { .unwrap(); } +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[test] +fn test_named_thread_truncation() { + use crate::ffi::CStr; + + let long_name = crate::iter::once("test_named_thread_truncation") + .chain(crate::iter::repeat(" yada").take(100)) + .collect::(); + + let result = Builder::new().name(long_name.clone()).spawn(move || { + // Rust remembers the full thread name itself. + assert_eq!(thread::current().name(), Some(long_name.as_str())); + + // But the system is limited -- make sure we successfully set a truncation. + let mut buf = vec![0u8; long_name.len() + 1]; + unsafe { + libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()); + } + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + assert!(cstr.to_bytes().len() > 0); + assert!(long_name.as_bytes().starts_with(cstr.to_bytes())); + }); + result.unwrap().join().unwrap(); +} + #[test] #[should_panic] fn test_invalid_named_thread() { From 10dad22b6669defeee296a91b2e2c92f8fd46e43 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Wed, 16 Feb 2022 14:48:39 -0500 Subject: [PATCH 0189/1126] Only apply `ProceduralMasquerade` hack to older versions of `rental` The latest version of `rental` (v0.5.6) contains a fix that allows it to compile without relying on the pretty-print back-compat hack. Hopefully, there are no longer any crates relying on the affected versions of the (much less popular) `procedural-masquerade` crate. This should allow us to target the pretty-print back-compat hack specifically to older versions of `rental`, and specifically mention upgrading to `rental` v0.5.6 in the lint message. --- compiler/rustc_expand/src/base.rs | 46 +++-- .../issue-73933-procedural-masquerade.rs | 9 +- .../issue-73933-procedural-masquerade.stderr | 91 --------- .../issue-73933-procedural-masquerade.stdout | 11 +- .../ui/proc-macro/pretty-print-hack-hide.rs | 12 ++ .../proc-macro/pretty-print-hack-hide.stdout | 21 ++ .../ui/proc-macro/pretty-print-hack-show.rs | 17 ++ .../proc-macro/pretty-print-hack-show.stderr | 179 ++++++++++++++++++ .../proc-macro/pretty-print-hack-show.stdout | 44 +++++ .../allsorts-rental-0.5.6/src/lib.rs | 14 ++ .../pretty-print-hack/rental-0.5.5/src/lib.rs | 14 ++ .../pretty-print-hack/rental-0.5.6/src/lib.rs | 14 ++ 12 files changed, 356 insertions(+), 116 deletions(-) delete mode 100644 src/test/ui/proc-macro/issue-73933-procedural-masquerade.stderr create mode 100644 src/test/ui/proc-macro/pretty-print-hack-hide.rs create mode 100644 src/test/ui/proc-macro/pretty-print-hack-hide.stdout create mode 100644 src/test/ui/proc-macro/pretty-print-hack-show.rs create mode 100644 src/test/ui/proc-macro/pretty-print-hack-show.stderr create mode 100644 src/test/ui/proc-macro/pretty-print-hack-show.stdout create mode 100644 src/test/ui/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs create mode 100644 src/test/ui/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs create mode 100644 src/test/ui/proc-macro/pretty-print-hack/rental-0.5.6/src/lib.rs diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 052ca229f012..c8de60ccb89b 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -22,7 +22,7 @@ use rustc_span::edition::Edition; use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, FileName, Span, DUMMY_SP}; +use rustc_span::{BytePos, FileName, RealFileName, Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::default::Default; @@ -1423,16 +1423,40 @@ fn pretty_printing_compatibility_hack(item: &Item, sess: &ParseSess) -> bool { if let ast::ItemKind::Enum(enum_def, _) = &item.kind { if let [variant] = &*enum_def.variants { if variant.ident.name == sym::Input { - sess.buffer_lint_with_diagnostic( - &PROC_MACRO_BACK_COMPAT, - item.ident.span, - ast::CRATE_NODE_ID, - "using `procedural-masquerade` crate", - BuiltinLintDiagnostics::ProcMacroBackCompat( - "The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. \ - Versions of this crate below 0.1.7 will eventually stop compiling.".to_string()) - ); - return true; + let filename = sess.source_map().span_to_filename(item.ident.span); + if let FileName::Real(RealFileName::LocalPath(path)) = filename { + if let Some(c) = path + .components() + .flat_map(|c| c.as_os_str().to_str()) + .find(|c| c.starts_with("rental") || c.starts_with("allsorts-rental")) + { + let crate_matches = if c.starts_with("allsorts-rental") { + true + } else { + let mut version = c.trim_start_matches("rental-").split("."); + version.next() == Some("0") + && version.next() == Some("5") + && version + .next() + .and_then(|c| c.parse::().ok()) + .map_or(false, |v| v < 6) + }; + + if crate_matches { + sess.buffer_lint_with_diagnostic( + &PROC_MACRO_BACK_COMPAT, + item.ident.span, + ast::CRATE_NODE_ID, + "using an old version of `rental`", + BuiltinLintDiagnostics::ProcMacroBackCompat( + "older versions of the `rental` crate will stop compiling in future versions of Rust; \ + please update to `rental` v0.5.6, or switch to one of the `rental` alternatives".to_string() + ) + ); + return true; + } + } + } } } } diff --git a/src/test/ui/proc-macro/issue-73933-procedural-masquerade.rs b/src/test/ui/proc-macro/issue-73933-procedural-masquerade.rs index 0c1c51c01a88..a573c6e1c0b8 100644 --- a/src/test/ui/proc-macro/issue-73933-procedural-masquerade.rs +++ b/src/test/ui/proc-macro/issue-73933-procedural-masquerade.rs @@ -1,18 +1,11 @@ // aux-build:test-macros.rs +// check-pass #[macro_use] extern crate test_macros; #[derive(Print)] enum ProceduralMasqueradeDummyType { -//~^ ERROR using -//~| WARN this was previously -//~| ERROR using -//~| WARN this was previously -//~| ERROR using -//~| WARN this was previously -//~| ERROR using -//~| WARN this was previously Input } diff --git a/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stderr b/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stderr deleted file mode 100644 index ebb8e825e6a3..000000000000 --- a/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stderr +++ /dev/null @@ -1,91 +0,0 @@ -error: using `procedural-masquerade` crate - --> $DIR/issue-73933-procedural-masquerade.rs:7:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #83125 - = note: The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. Versions of this crate below 0.1.7 will eventually stop compiling. - = note: `#[deny(proc_macro_back_compat)]` on by default - -error: using `procedural-masquerade` crate - --> $DIR/issue-73933-procedural-masquerade.rs:7:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #83125 - = note: The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. Versions of this crate below 0.1.7 will eventually stop compiling. - -error: using `procedural-masquerade` crate - --> $DIR/issue-73933-procedural-masquerade.rs:7:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #83125 - = note: The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. Versions of this crate below 0.1.7 will eventually stop compiling. - -error: using `procedural-masquerade` crate - --> $DIR/issue-73933-procedural-masquerade.rs:7:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #83125 - = note: The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. Versions of this crate below 0.1.7 will eventually stop compiling. - -error: aborting due to 4 previous errors - -Future incompatibility report: Future breakage diagnostic: -error: using `procedural-masquerade` crate - --> $DIR/issue-73933-procedural-masquerade.rs:7:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #83125 - = note: The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. Versions of this crate below 0.1.7 will eventually stop compiling. - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using `procedural-masquerade` crate - --> $DIR/issue-73933-procedural-masquerade.rs:7:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #83125 - = note: The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. Versions of this crate below 0.1.7 will eventually stop compiling. - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using `procedural-masquerade` crate - --> $DIR/issue-73933-procedural-masquerade.rs:7:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #83125 - = note: The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. Versions of this crate below 0.1.7 will eventually stop compiling. - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using `procedural-masquerade` crate - --> $DIR/issue-73933-procedural-masquerade.rs:7:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #83125 - = note: The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. Versions of this crate below 0.1.7 will eventually stop compiling. - = note: `#[deny(proc_macro_back_compat)]` on by default - diff --git a/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stdout b/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stdout index 50334589d0bb..8cd981e03f11 100644 --- a/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stdout +++ b/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stdout @@ -1,22 +1,21 @@ -PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, } -PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input } +PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", - span: #0 bytes(86..90), + span: #0 bytes(100..104), }, Ident { ident: "ProceduralMasqueradeDummyType", - span: #0 bytes(91..120), + span: #0 bytes(105..134), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "Input", - span: #0 bytes(315..320), + span: #0 bytes(141..146), }, ], - span: #0 bytes(121..322), + span: #0 bytes(135..148), }, ] diff --git a/src/test/ui/proc-macro/pretty-print-hack-hide.rs b/src/test/ui/proc-macro/pretty-print-hack-hide.rs new file mode 100644 index 000000000000..f53e8fe8252f --- /dev/null +++ b/src/test/ui/proc-macro/pretty-print-hack-hide.rs @@ -0,0 +1,12 @@ +// aux-build:test-macros.rs +// compile-flags: -Z span-debug +// check-pass + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] extern crate test_macros; + +include!("pretty-print-hack/rental-0.5.6/src/lib.rs"); + +fn main() {} diff --git a/src/test/ui/proc-macro/pretty-print-hack-hide.stdout b/src/test/ui/proc-macro/pretty-print-hack-hide.stdout new file mode 100644 index 000000000000..ea796bb26976 --- /dev/null +++ b/src/test/ui/proc-macro/pretty-print-hack-hide.stdout @@ -0,0 +1,21 @@ +PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input } +PRINT-DERIVE INPUT (DEBUG): TokenStream [ + Ident { + ident: "enum", + span: $DIR/pretty-print-hack/rental-0.5.6/src/lib.rs:4:1: 4:5 (#0), + }, + Ident { + ident: "ProceduralMasqueradeDummyType", + span: $DIR/pretty-print-hack/rental-0.5.6/src/lib.rs:4:6: 4:35 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "Input", + span: $DIR/pretty-print-hack/rental-0.5.6/src/lib.rs:13:5: 13:10 (#0), + }, + ], + span: $DIR/pretty-print-hack/rental-0.5.6/src/lib.rs:4:36: 14:2 (#0), + }, +] diff --git a/src/test/ui/proc-macro/pretty-print-hack-show.rs b/src/test/ui/proc-macro/pretty-print-hack-show.rs new file mode 100644 index 000000000000..9b1899e49220 --- /dev/null +++ b/src/test/ui/proc-macro/pretty-print-hack-show.rs @@ -0,0 +1,17 @@ +// aux-build:test-macros.rs +// compile-flags: -Z span-debug + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] extern crate test_macros; + +mod first { + include!("pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs"); +} + +mod second { + include!("pretty-print-hack/rental-0.5.5/src/lib.rs"); +} + +fn main() {} diff --git a/src/test/ui/proc-macro/pretty-print-hack-show.stderr b/src/test/ui/proc-macro/pretty-print-hack-show.stderr new file mode 100644 index 000000000000..873054927c96 --- /dev/null +++ b/src/test/ui/proc-macro/pretty-print-hack-show.stderr @@ -0,0 +1,179 @@ +error: using an old version of `rental` + --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + = note: `#[deny(proc_macro_back_compat)]` on by default + +error: using an old version of `rental` + --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + +error: using an old version of `rental` + --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + +error: using an old version of `rental` + --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + +error: using an old version of `rental` + --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + +error: using an old version of `rental` + --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + +error: using an old version of `rental` + --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + +error: using an old version of `rental` + --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + +error: aborting due to 8 previous errors + +Future incompatibility report: Future breakage diagnostic: +error: using an old version of `rental` + --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + = note: `#[deny(proc_macro_back_compat)]` on by default + +Future breakage diagnostic: +error: using an old version of `rental` + --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + = note: `#[deny(proc_macro_back_compat)]` on by default + +Future breakage diagnostic: +error: using an old version of `rental` + --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + = note: `#[deny(proc_macro_back_compat)]` on by default + +Future breakage diagnostic: +error: using an old version of `rental` + --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + = note: `#[deny(proc_macro_back_compat)]` on by default + +Future breakage diagnostic: +error: using an old version of `rental` + --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + = note: `#[deny(proc_macro_back_compat)]` on by default + +Future breakage diagnostic: +error: using an old version of `rental` + --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + = note: `#[deny(proc_macro_back_compat)]` on by default + +Future breakage diagnostic: +error: using an old version of `rental` + --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + = note: `#[deny(proc_macro_back_compat)]` on by default + +Future breakage diagnostic: +error: using an old version of `rental` + --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 + = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives + = note: `#[deny(proc_macro_back_compat)]` on by default + diff --git a/src/test/ui/proc-macro/pretty-print-hack-show.stdout b/src/test/ui/proc-macro/pretty-print-hack-show.stdout new file mode 100644 index 000000000000..3d793d2a0145 --- /dev/null +++ b/src/test/ui/proc-macro/pretty-print-hack-show.stdout @@ -0,0 +1,44 @@ +PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, } +PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input } +PRINT-DERIVE INPUT (DEBUG): TokenStream [ + Ident { + ident: "enum", + span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:1: 4:5 (#0), + }, + Ident { + ident: "ProceduralMasqueradeDummyType", + span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6: 4:35 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "Input", + span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:13:5: 13:10 (#0), + }, + ], + span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:36: 14:2 (#0), + }, +] +PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, } +PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input } +PRINT-DERIVE INPUT (DEBUG): TokenStream [ + Ident { + ident: "enum", + span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:1: 4:5 (#0), + }, + Ident { + ident: "ProceduralMasqueradeDummyType", + span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6: 4:35 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "Input", + span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:13:5: 13:10 (#0), + }, + ], + span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:36: 14:2 (#0), + }, +] diff --git a/src/test/ui/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs b/src/test/ui/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs new file mode 100644 index 000000000000..9501980fa55d --- /dev/null +++ b/src/test/ui/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs @@ -0,0 +1,14 @@ +// ignore-test + +#[derive(Print)] +enum ProceduralMasqueradeDummyType { +//~^ ERROR using +//~| WARN this was previously +//~| ERROR using +//~| WARN this was previously +//~| ERROR using +//~| WARN this was previously +//~| ERROR using +//~| WARN this was previously + Input +} diff --git a/src/test/ui/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs b/src/test/ui/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs new file mode 100644 index 000000000000..9501980fa55d --- /dev/null +++ b/src/test/ui/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs @@ -0,0 +1,14 @@ +// ignore-test + +#[derive(Print)] +enum ProceduralMasqueradeDummyType { +//~^ ERROR using +//~| WARN this was previously +//~| ERROR using +//~| WARN this was previously +//~| ERROR using +//~| WARN this was previously +//~| ERROR using +//~| WARN this was previously + Input +} diff --git a/src/test/ui/proc-macro/pretty-print-hack/rental-0.5.6/src/lib.rs b/src/test/ui/proc-macro/pretty-print-hack/rental-0.5.6/src/lib.rs new file mode 100644 index 000000000000..9501980fa55d --- /dev/null +++ b/src/test/ui/proc-macro/pretty-print-hack/rental-0.5.6/src/lib.rs @@ -0,0 +1,14 @@ +// ignore-test + +#[derive(Print)] +enum ProceduralMasqueradeDummyType { +//~^ ERROR using +//~| WARN this was previously +//~| ERROR using +//~| WARN this was previously +//~| ERROR using +//~| WARN this was previously +//~| ERROR using +//~| WARN this was previously + Input +} From e025306fa050ac593bff366eb1c9bff7bfd00b1d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 22 Oct 2022 02:48:36 +0000 Subject: [PATCH 0190/1126] Don't ICE on regions from anonymous_lifetime_in_impl_trait --- .../src/diagnostics/region_name.rs | 100 ++++++++++++++++-- .../ui/borrowck/anonymous-region-in-apit.rs | 12 +++ .../borrowck/anonymous-region-in-apit.stderr | 16 +++ 3 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 src/test/ui/borrowck/anonymous-region-in-apit.rs create mode 100644 src/test/ui/borrowck/anonymous-region-in-apit.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 4d251cf7ac75..c044dbaba47e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -251,7 +251,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { .or_else(|| self.give_name_if_anonymous_region_appears_in_upvars(fr)) .or_else(|| self.give_name_if_anonymous_region_appears_in_output(fr)) .or_else(|| self.give_name_if_anonymous_region_appears_in_yield_ty(fr)) - .or_else(|| self.give_name_if_anonymous_region_appears_in_impl_signature(fr)); + .or_else(|| self.give_name_if_anonymous_region_appears_in_impl_signature(fr)) + .or_else(|| self.give_name_if_anonymous_region_appears_in_arg_position_impl_trait(fr)); if let Some(ref value) = value { self.region_names.try_borrow_mut().unwrap().insert(fr, value.clone()); @@ -869,13 +870,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { return None; } - let mut found = false; - tcx.fold_regions(tcx.type_of(region_parent), |r: ty::Region<'tcx>, _| { - if *r == ty::ReEarlyBound(region) { - found = true; - } - r - }); + let found = tcx + .any_free_region_meets(&tcx.type_of(region_parent), |r| *r == ty::ReEarlyBound(region)); Some(RegionName { name: self.synthesize_region_name(), @@ -888,4 +884,92 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ), }) } + + fn give_name_if_anonymous_region_appears_in_arg_position_impl_trait( + &self, + fr: RegionVid, + ) -> Option { + let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else { + return None; + }; + if region.has_name() { + return None; + }; + + let predicates = self + .infcx + .tcx + .predicates_of(self.body.source.def_id()) + .instantiate_identity(self.infcx.tcx) + .predicates; + + if let Some(upvar_index) = self + .regioncx + .universal_regions() + .defining_ty + .upvar_tys() + .position(|ty| self.any_param_predicate_mentions(&predicates, ty, region)) + { + let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region( + self.infcx.tcx, + &self.upvars, + upvar_index, + ); + let region_name = self.synthesize_region_name(); + + Some(RegionName { + name: region_name, + source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), + }) + } else if let Some(arg_index) = self + .regioncx + .universal_regions() + .unnormalized_input_tys + .iter() + .position(|ty| self.any_param_predicate_mentions(&predicates, *ty, region)) + { + let (arg_name, arg_span) = self.regioncx.get_argument_name_and_span_for_region( + self.body, + &self.local_names, + arg_index, + ); + let region_name = self.synthesize_region_name(); + + Some(RegionName { + name: region_name, + source: RegionNameSource::AnonRegionFromArgument( + RegionNameHighlight::CannotMatchHirTy(arg_span, arg_name?.to_string()), + ), + }) + } else { + None + } + } + + fn any_param_predicate_mentions( + &self, + predicates: &[ty::Predicate<'tcx>], + ty: Ty<'tcx>, + region: ty::EarlyBoundRegion, + ) -> bool { + let tcx = self.infcx.tcx; + ty.walk().any(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Param(_) = ty.kind() + { + predicates.iter().any(|pred| { + match pred.kind().skip_binder() { + ty::PredicateKind::Trait(data) if data.self_ty() == ty => {} + ty::PredicateKind::Projection(data) if data.projection_ty.self_ty() == ty => {} + _ => return false, + } + tcx.any_free_region_meets(pred, |r| { + *r == ty::ReEarlyBound(region) + }) + }) + } else { + false + } + }) + } } diff --git a/src/test/ui/borrowck/anonymous-region-in-apit.rs b/src/test/ui/borrowck/anonymous-region-in-apit.rs new file mode 100644 index 000000000000..7799a7cb151d --- /dev/null +++ b/src/test/ui/borrowck/anonymous-region-in-apit.rs @@ -0,0 +1,12 @@ +#![feature(anonymous_lifetime_in_impl_trait)] + +trait Foo { + fn bar(self, baz: T); +} + +fn qux(foo: impl Foo<&str>) { + |baz: &str| foo.bar(baz); + //~^ ERROR borrowed data escapes outside of closure +} + +fn main() {} diff --git a/src/test/ui/borrowck/anonymous-region-in-apit.stderr b/src/test/ui/borrowck/anonymous-region-in-apit.stderr new file mode 100644 index 000000000000..9e100f8ac3c5 --- /dev/null +++ b/src/test/ui/borrowck/anonymous-region-in-apit.stderr @@ -0,0 +1,16 @@ +error[E0521]: borrowed data escapes outside of closure + --> $DIR/anonymous-region-in-apit.rs:8:17 + | +LL | fn qux(foo: impl Foo<&str>) { + | --- lifetime `'2` appears in the type of `foo` +LL | |baz: &str| foo.bar(baz); + | --- - ^^^^^^^^^^^^ + | | | | + | | | `baz` escapes the closure body here + | | | argument requires that `'1` must outlive `'2` + | | let's call the lifetime of this reference `'1` + | `baz` is a reference that is only valid in the closure body + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0521`. From 4accf838f6d847c0c93f4c25540446dad0309519 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 22 Oct 2022 03:09:49 +0000 Subject: [PATCH 0191/1126] Note scope of TAIT more accurately --- .../rustc_error_messages/locales/en-US/hir_analysis.ftl | 2 +- compiler/rustc_hir_analysis/src/collect/type_of.rs | 6 ++++++ compiler/rustc_hir_analysis/src/errors.rs | 1 + src/test/ui/generic-associated-types/issue-87258_a.stderr | 2 +- src/test/ui/lint/inline-trait-and-foreign-items.stderr | 2 +- src/test/ui/lint/no-coverage.stderr | 2 +- src/test/ui/save-analysis/issue-68621.stderr | 2 +- 7 files changed, 12 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl index 357c6900a70e..a5bbd1f7fc24 100644 --- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl +++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl @@ -93,7 +93,7 @@ hir_analysis_expected_default_return_type = expected `()` because of default ret hir_analysis_expected_return_type = expected `{$expected}` because of return type hir_analysis_unconstrained_opaque_type = unconstrained opaque type - .note = `{$name}` must be used in combination with a concrete type within the same module + .note = `{$name}` must be used in combination with a concrete type within the same {$what} hir_analysis_missing_type_params = the type {$parameterCount -> diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 32f359a81581..05ada7879224 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -693,6 +693,12 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T tcx.sess.emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), + what: match tcx.hir().get(scope) { + _ if scope == hir::CRATE_HIR_ID => "module", + Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module", + Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => "impl", + _ => "item", + }, }); return tcx.ty_error(); }; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index aaebbe6398a1..5b41a64dcd8e 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -143,6 +143,7 @@ pub struct UnconstrainedOpaqueType { #[primary_span] pub span: Span, pub name: Symbol, + pub what: &'static str, } pub struct MissingTypeParams { diff --git a/src/test/ui/generic-associated-types/issue-87258_a.stderr b/src/test/ui/generic-associated-types/issue-87258_a.stderr index fa0748a280b6..eae9bd9b16f2 100644 --- a/src/test/ui/generic-associated-types/issue-87258_a.stderr +++ b/src/test/ui/generic-associated-types/issue-87258_a.stderr @@ -4,7 +4,7 @@ error: unconstrained opaque type LL | type FooFuture<'a> = impl Trait1; | ^^^^^^^^^^^ | - = note: `FooFuture` must be used in combination with a concrete type within the same module + = note: `FooFuture` must be used in combination with a concrete type within the same impl error: aborting due to previous error diff --git a/src/test/ui/lint/inline-trait-and-foreign-items.stderr b/src/test/ui/lint/inline-trait-and-foreign-items.stderr index 27399746bed6..2f1fb4c46c08 100644 --- a/src/test/ui/lint/inline-trait-and-foreign-items.stderr +++ b/src/test/ui/lint/inline-trait-and-foreign-items.stderr @@ -67,7 +67,7 @@ error: unconstrained opaque type LL | type U = impl Trait; | ^^^^^^^^^^ | - = note: `U` must be used in combination with a concrete type within the same module + = note: `U` must be used in combination with a concrete type within the same impl error: aborting due to 6 previous errors; 2 warnings emitted diff --git a/src/test/ui/lint/no-coverage.stderr b/src/test/ui/lint/no-coverage.stderr index 8452ccc7a03c..404efbeac1e1 100644 --- a/src/test/ui/lint/no-coverage.stderr +++ b/src/test/ui/lint/no-coverage.stderr @@ -94,7 +94,7 @@ error: unconstrained opaque type LL | type U = impl Trait; | ^^^^^^^^^^ | - = note: `U` must be used in combination with a concrete type within the same module + = note: `U` must be used in combination with a concrete type within the same impl error: aborting due to 7 previous errors; 6 warnings emitted diff --git a/src/test/ui/save-analysis/issue-68621.stderr b/src/test/ui/save-analysis/issue-68621.stderr index 4a4bf9a6996b..4452ee7915ba 100644 --- a/src/test/ui/save-analysis/issue-68621.stderr +++ b/src/test/ui/save-analysis/issue-68621.stderr @@ -4,7 +4,7 @@ error: unconstrained opaque type LL | type Future = impl Trait; | ^^^^^^^^^^ | - = note: `Future` must be used in combination with a concrete type within the same module + = note: `Future` must be used in combination with a concrete type within the same impl error: aborting due to previous error From 988e75bb65f688d76f4f80e0fb72ef21e831d0b0 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 6 Apr 2022 11:21:10 +1000 Subject: [PATCH 0192/1126] Stabilize arbitrary_enum_discriminant, take 2 --- compiler/rustc_ast_passes/src/feature_gate.rs | 62 +------------------ .../src/error_codes/E0732.md | 4 -- compiler/rustc_feature/src/accepted.rs | 2 + compiler/rustc_feature/src/active.rs | 2 - .../rustc_hir_analysis/src/check/check.rs | 2 +- .../arbitrary-enum-discriminant.md | 37 ----------- src/test/ui/cast/issue-88621.rs | 2 - .../arbitrary_enum_discriminant-no-repr.rs | 1 - .../arbitrary_enum_discriminant.rs | 2 +- .../enum-discriminant/discriminant_value.rs | 2 +- ...eature-gate-arbitrary_enum_discriminant.rs | 10 --- ...re-gate-arbitrary_enum_discriminant.stderr | 36 ----------- .../issue-70453-generics-in-discr-ice-2.rs | 2 +- .../issue-70453-polymorphic-ctfe.rs | 2 +- .../issue-70509-partial_eq.rs | 2 +- .../issue-70509-partial_eq.stderr | 2 +- .../intrinsics/panic-uninitialized-zeroed.rs | 2 +- src/test/ui/macros/macros-nonfatal-errors.rs | 2 +- src/test/ui/parser/issues/issue-17383.rs | 7 --- src/test/ui/parser/issues/issue-17383.stderr | 15 ----- .../ui/parser/tag-variant-disr-non-nullary.rs | 12 ---- .../tag-variant-disr-non-nullary.stderr | 25 -------- 22 files changed, 12 insertions(+), 221 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md delete mode 100644 src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.rs delete mode 100644 src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr delete mode 100644 src/test/ui/parser/issues/issue-17383.rs delete mode 100644 src/test/ui/parser/issues/issue-17383.stderr delete mode 100644 src/test/ui/parser/tag-variant-disr-non-nullary.rs delete mode 100644 src/test/ui/parser/tag-variant-disr-non-nullary.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 0f11c1766528..546010135a72 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,7 +1,7 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{AssocConstraint, AssocConstraintKind, NodeId}; -use rustc_ast::{PatKind, RangeEnd, VariantData}; +use rustc_ast::{PatKind, RangeEnd}; use rustc_errors::{struct_span_err, Applicability, StashKey}; use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP}; use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; @@ -116,46 +116,6 @@ impl<'a> PostExpansionVisitor<'a> { } } - fn maybe_report_invalid_custom_discriminants(&self, variants: &[ast::Variant]) { - let has_fields = variants.iter().any(|variant| match variant.data { - VariantData::Tuple(..) | VariantData::Struct(..) => true, - VariantData::Unit(..) => false, - }); - - let discriminant_spans = variants - .iter() - .filter(|variant| match variant.data { - VariantData::Tuple(..) | VariantData::Struct(..) => false, - VariantData::Unit(..) => true, - }) - .filter_map(|variant| variant.disr_expr.as_ref().map(|c| c.value.span)) - .collect::>(); - - if !discriminant_spans.is_empty() && has_fields { - let mut err = feature_err( - &self.sess.parse_sess, - sym::arbitrary_enum_discriminant, - discriminant_spans.clone(), - "custom discriminant values are not allowed in enums with tuple or struct variants", - ); - for sp in discriminant_spans { - err.span_label(sp, "disallowed custom discriminant"); - } - for variant in variants.iter() { - match &variant.data { - VariantData::Struct(..) => { - err.span_label(variant.span, "struct variant defined here"); - } - VariantData::Tuple(..) => { - err.span_label(variant.span, "tuple variant defined here"); - } - VariantData::Unit(..) => {} - } - } - err.emit(); - } - } - /// Feature gate `impl Trait` inside `type Alias = $type_expr;`. fn check_impl_trait(&self, ty: &ast::Ty) { struct ImplTraitVisitor<'a> { @@ -273,26 +233,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - ast::ItemKind::Enum(ast::EnumDef { ref variants, .. }, ..) => { - for variant in variants { - match (&variant.data, &variant.disr_expr) { - (ast::VariantData::Unit(..), _) => {} - (_, Some(disr_expr)) => gate_feature_post!( - &self, - arbitrary_enum_discriminant, - disr_expr.value.span, - "discriminants on non-unit variants are experimental" - ), - _ => {} - } - } - - let has_feature = self.features.arbitrary_enum_discriminant; - if !has_feature && !i.span.allows_unstable(sym::arbitrary_enum_discriminant) { - self.maybe_report_invalid_custom_discriminants(&variants); - } - } - ast::ItemKind::Impl(box ast::Impl { polarity, defaultness, ref of_trait, .. }) => { if let ast::ImplPolarity::Negative(span) = polarity { gate_feature_post!( diff --git a/compiler/rustc_error_codes/src/error_codes/E0732.md b/compiler/rustc_error_codes/src/error_codes/E0732.md index 7347e6654c5b..9536fdbf0df8 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0732.md +++ b/compiler/rustc_error_codes/src/error_codes/E0732.md @@ -3,8 +3,6 @@ An `enum` with a discriminant must specify a `#[repr(inttype)]`. Erroneous code example: ```compile_fail,E0732 -#![feature(arbitrary_enum_discriminant)] - enum Enum { // error! Unit = 1, Tuple() = 2, @@ -20,8 +18,6 @@ is a well-defined way to extract a variant's discriminant from a value; for instance: ``` -#![feature(arbitrary_enum_discriminant)] - #[repr(u8)] enum Enum { Unit = 3, diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 54bf5a2c34b8..db289a64046a 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -53,6 +53,8 @@ declare_features! ( (accepted, abi_sysv64, "1.24.0", Some(36167), None), /// Allows using ADX intrinsics from `core::arch::{x86, x86_64}`. (accepted, adx_target_feature, "1.61.0", Some(44839), None), + /// Allows explicit discriminants on non-unit enum variants. + (accepted, arbitrary_enum_discriminant, "CURRENT_RUSTC_VERSION", Some(60553), None), /// Allows using `sym` operands in inline assembly. (accepted, asm_sym, "CURRENT_RUSTC_VERSION", Some(93333), None), /// Allows the definition of associated constants in `trait` or `impl` blocks. diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 1b8d683b1336..4facb6140a30 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -292,8 +292,6 @@ declare_features! ( (incomplete, adt_const_params, "1.56.0", Some(95174), None), /// Allows defining an `#[alloc_error_handler]`. (active, alloc_error_handler, "1.29.0", Some(51540), None), - /// Allows explicit discriminants on non-unit enum variants. - (active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None), /// Allows trait methods with arbitrary self types. (active, arbitrary_self_types, "1.23.0", Some(44874), None), /// Allows using `const` operands in inline assembly. diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index a1faf8025195..b302cee4816f 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1180,7 +1180,7 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L } } - if tcx.adt_def(def_id).repr().int.is_none() && tcx.features().arbitrary_enum_discriminant { + if tcx.adt_def(def_id).repr().int.is_none() { let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..)); let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some(); diff --git a/src/doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md b/src/doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md deleted file mode 100644 index e0bb782270e2..000000000000 --- a/src/doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md +++ /dev/null @@ -1,37 +0,0 @@ -# `arbitrary_enum_discriminant` - -The tracking issue for this feature is: [#60553] - -[#60553]: https://github.com/rust-lang/rust/issues/60553 - ------------------------- - -The `arbitrary_enum_discriminant` feature permits tuple-like and -struct-like enum variants with `#[repr()]` to have explicit discriminants. - -## Examples - -```rust -#![feature(arbitrary_enum_discriminant)] - -#[allow(dead_code)] -#[repr(u8)] -enum Enum { - Unit = 3, - Tuple(u16) = 2, - Struct { - a: u8, - b: u16, - } = 1, -} - -impl Enum { - fn tag(&self) -> u8 { - unsafe { *(self as *const Self as *const u8) } - } -} - -assert_eq!(3, Enum::Unit.tag()); -assert_eq!(2, Enum::Tuple(5).tag()); -assert_eq!(1, Enum::Struct{a: 7, b: 11}.tag()); -``` diff --git a/src/test/ui/cast/issue-88621.rs b/src/test/ui/cast/issue-88621.rs index 9242b80e2293..1679793ee683 100644 --- a/src/test/ui/cast/issue-88621.rs +++ b/src/test/ui/cast/issue-88621.rs @@ -1,5 +1,3 @@ -#![feature(arbitrary_enum_discriminant)] - #[repr(u8)] enum Kind2 { Foo() = 1, diff --git a/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs index 4da7b5ab24b2..a6e5f70fdefa 100644 --- a/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs +++ b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs @@ -1,5 +1,4 @@ #![crate_type="lib"] -#![feature(arbitrary_enum_discriminant)] enum Enum { //~^ ERROR `#[repr(inttype)]` must be specified diff --git a/src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs index ccc423e4a194..83e74a6e685a 100644 --- a/src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs +++ b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs @@ -1,5 +1,5 @@ // run-pass -#![feature(arbitrary_enum_discriminant, test)] +#![feature(test)] extern crate test; diff --git a/src/test/ui/enum-discriminant/discriminant_value.rs b/src/test/ui/enum-discriminant/discriminant_value.rs index 65ab411dbcbe..f3dfac298ad7 100644 --- a/src/test/ui/enum-discriminant/discriminant_value.rs +++ b/src/test/ui/enum-discriminant/discriminant_value.rs @@ -1,6 +1,6 @@ // run-pass #![allow(stable_features)] -#![feature(arbitrary_enum_discriminant, core, core_intrinsics)] +#![feature(core, core_intrinsics)] extern crate core; use core::intrinsics::discriminant_value; diff --git a/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.rs b/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.rs deleted file mode 100644 index 3e90af4d36af..000000000000 --- a/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![crate_type="lib"] - -enum Enum { - Unit = 1, - //~^ ERROR custom discriminant values are not allowed in enums with tuple or struct variants - Tuple() = 2, - //~^ ERROR discriminants on non-unit variants are experimental - Struct{} = 3, - //~^ ERROR discriminants on non-unit variants are experimental -} diff --git a/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr b/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr deleted file mode 100644 index b5f61e6e991d..000000000000 --- a/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr +++ /dev/null @@ -1,36 +0,0 @@ -error[E0658]: discriminants on non-unit variants are experimental - --> $DIR/feature-gate-arbitrary_enum_discriminant.rs:6:13 - | -LL | Tuple() = 2, - | ^ - | - = note: see issue #60553 for more information - = help: add `#![feature(arbitrary_enum_discriminant)]` to the crate attributes to enable - -error[E0658]: discriminants on non-unit variants are experimental - --> $DIR/feature-gate-arbitrary_enum_discriminant.rs:8:14 - | -LL | Struct{} = 3, - | ^ - | - = note: see issue #60553 for more information - = help: add `#![feature(arbitrary_enum_discriminant)]` to the crate attributes to enable - -error[E0658]: custom discriminant values are not allowed in enums with tuple or struct variants - --> $DIR/feature-gate-arbitrary_enum_discriminant.rs:4:10 - | -LL | Unit = 1, - | ^ disallowed custom discriminant -LL | -LL | Tuple() = 2, - | ----------- tuple variant defined here -LL | -LL | Struct{} = 3, - | ------------ struct variant defined here - | - = note: see issue #60553 for more information - = help: add `#![feature(arbitrary_enum_discriminant)]` to the crate attributes to enable - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs index f927dd189038..ad9fcc25b412 100644 --- a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs +++ b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice-2.rs @@ -1,4 +1,4 @@ -#![feature(arbitrary_enum_discriminant, core_intrinsics)] +#![feature(core_intrinsics)] extern crate core; use core::intrinsics::discriminant_value; diff --git a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs index e62582fb5161..42a062239d34 100644 --- a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs +++ b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs @@ -1,4 +1,4 @@ -#![feature(arbitrary_enum_discriminant, core_intrinsics)] +#![feature(core_intrinsics)] extern crate core; use core::intrinsics::discriminant_value; diff --git a/src/test/ui/enum-discriminant/issue-70509-partial_eq.rs b/src/test/ui/enum-discriminant/issue-70509-partial_eq.rs index ae389e114664..3adac7b72621 100644 --- a/src/test/ui/enum-discriminant/issue-70509-partial_eq.rs +++ b/src/test/ui/enum-discriminant/issue-70509-partial_eq.rs @@ -1,5 +1,5 @@ // run-pass -#![feature(repr128, arbitrary_enum_discriminant)] +#![feature(repr128)] //~^ WARN the feature `repr128` is incomplete #[derive(PartialEq, Debug)] diff --git a/src/test/ui/enum-discriminant/issue-70509-partial_eq.stderr b/src/test/ui/enum-discriminant/issue-70509-partial_eq.stderr index ac93badf2156..2eef930c3943 100644 --- a/src/test/ui/enum-discriminant/issue-70509-partial_eq.stderr +++ b/src/test/ui/enum-discriminant/issue-70509-partial_eq.stderr @@ -1,7 +1,7 @@ warning: the feature `repr128` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-70509-partial_eq.rs:2:12 | -LL | #![feature(repr128, arbitrary_enum_discriminant)] +LL | #![feature(repr128)] | ^^^^^^^ | = note: see issue #56071 for more information diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs index ae44ffd29bd5..ec3860a322fc 100644 --- a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -7,7 +7,7 @@ // This test checks panic emitted from `mem::{uninitialized,zeroed}`. -#![feature(never_type, arbitrary_enum_discriminant)] +#![feature(never_type)] #![allow(deprecated, invalid_value)] use std::{ diff --git a/src/test/ui/macros/macros-nonfatal-errors.rs b/src/test/ui/macros/macros-nonfatal-errors.rs index 140cc5b0fd80..ab14c35893d0 100644 --- a/src/test/ui/macros/macros-nonfatal-errors.rs +++ b/src/test/ui/macros/macros-nonfatal-errors.rs @@ -4,7 +4,7 @@ // immediately, so that we get more errors listed at a time. #![feature(trace_macros, concat_idents)] -#![feature(stmt_expr_attributes, arbitrary_enum_discriminant)] +#![feature(stmt_expr_attributes)] use std::arch::asm; diff --git a/src/test/ui/parser/issues/issue-17383.rs b/src/test/ui/parser/issues/issue-17383.rs deleted file mode 100644 index 7bf0e64f2c0a..000000000000 --- a/src/test/ui/parser/issues/issue-17383.rs +++ /dev/null @@ -1,7 +0,0 @@ -enum X { - A = 3, - //~^ ERROR custom discriminant values are not allowed in enums with tuple or struct variants - B(usize) -} - -fn main() {} diff --git a/src/test/ui/parser/issues/issue-17383.stderr b/src/test/ui/parser/issues/issue-17383.stderr deleted file mode 100644 index 265d6e148661..000000000000 --- a/src/test/ui/parser/issues/issue-17383.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0658]: custom discriminant values are not allowed in enums with tuple or struct variants - --> $DIR/issue-17383.rs:2:9 - | -LL | A = 3, - | ^ disallowed custom discriminant -LL | -LL | B(usize) - | -------- tuple variant defined here - | - = note: see issue #60553 for more information - = help: add `#![feature(arbitrary_enum_discriminant)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/parser/tag-variant-disr-non-nullary.rs b/src/test/ui/parser/tag-variant-disr-non-nullary.rs deleted file mode 100644 index a9cfdd549c75..000000000000 --- a/src/test/ui/parser/tag-variant-disr-non-nullary.rs +++ /dev/null @@ -1,12 +0,0 @@ -enum Color { - Red = 0xff0000, - //~^ ERROR custom discriminant values are not allowed in enums with tuple or struct variants - Green = 0x00ff00, - Blue = 0x0000ff, - Black = 0x000000, - White = 0xffffff, - Other(usize), - Other2(usize, usize), -} - -fn main() {} diff --git a/src/test/ui/parser/tag-variant-disr-non-nullary.stderr b/src/test/ui/parser/tag-variant-disr-non-nullary.stderr deleted file mode 100644 index 79f044a0675b..000000000000 --- a/src/test/ui/parser/tag-variant-disr-non-nullary.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0658]: custom discriminant values are not allowed in enums with tuple or struct variants - --> $DIR/tag-variant-disr-non-nullary.rs:2:11 - | -LL | Red = 0xff0000, - | ^^^^^^^^ disallowed custom discriminant -LL | -LL | Green = 0x00ff00, - | ^^^^^^^^ disallowed custom discriminant -LL | Blue = 0x0000ff, - | ^^^^^^^^ disallowed custom discriminant -LL | Black = 0x000000, - | ^^^^^^^^ disallowed custom discriminant -LL | White = 0xffffff, - | ^^^^^^^^ disallowed custom discriminant -LL | Other(usize), - | ------------ tuple variant defined here -LL | Other2(usize, usize), - | -------------------- tuple variant defined here - | - = note: see issue #60553 for more information - = help: add `#![feature(arbitrary_enum_discriminant)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. From b614b0e64c2990c60207aa748957728d644a5a8d Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 6 Apr 2022 13:45:30 +1000 Subject: [PATCH 0193/1126] Bless tests --- src/test/ui/cast/issue-88621.stderr | 2 +- .../arbitrary_enum_discriminant-no-repr.stderr | 2 +- src/test/ui/transmutability/enums/should_order_correctly.rs | 1 - .../ui/transmutability/enums/should_respect_endianness.rs | 1 - .../ui/transmutability/enums/should_respect_endianness.stderr | 4 ++-- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test/ui/cast/issue-88621.stderr b/src/test/ui/cast/issue-88621.stderr index e96d86651523..886145c1bafe 100644 --- a/src/test/ui/cast/issue-88621.stderr +++ b/src/test/ui/cast/issue-88621.stderr @@ -1,5 +1,5 @@ error[E0605]: non-primitive cast: `Kind2` as `u8` - --> $DIR/issue-88621.rs:11:13 + --> $DIR/issue-88621.rs:9:13 | LL | let _ = Kind2::Foo() as u8; | ^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object diff --git a/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr index 803bb06fcc21..8cee74696295 100644 --- a/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr +++ b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr @@ -1,5 +1,5 @@ error[E0732]: `#[repr(inttype)]` must be specified - --> $DIR/arbitrary_enum_discriminant-no-repr.rs:4:1 + --> $DIR/arbitrary_enum_discriminant-no-repr.rs:3:1 | LL | enum Enum { | ^^^^^^^^^ diff --git a/src/test/ui/transmutability/enums/should_order_correctly.rs b/src/test/ui/transmutability/enums/should_order_correctly.rs index b753cf0e62d5..1335cc9d2b17 100644 --- a/src/test/ui/transmutability/enums/should_order_correctly.rs +++ b/src/test/ui/transmutability/enums/should_order_correctly.rs @@ -2,7 +2,6 @@ //! The payloads of an enum variant should be ordered after its tag. #![crate_type = "lib"] -#![feature(arbitrary_enum_discriminant)] #![feature(transmutability)] #![allow(dead_code)] diff --git a/src/test/ui/transmutability/enums/should_respect_endianness.rs b/src/test/ui/transmutability/enums/should_respect_endianness.rs index 19ff69005003..f3567b405f40 100644 --- a/src/test/ui/transmutability/enums/should_respect_endianness.rs +++ b/src/test/ui/transmutability/enums/should_respect_endianness.rs @@ -2,7 +2,6 @@ //! an enum with a multi-byte tag. #![crate_type = "lib"] -#![feature(arbitrary_enum_discriminant)] #![feature(transmutability)] #![allow(dead_code)] diff --git a/src/test/ui/transmutability/enums/should_respect_endianness.stderr b/src/test/ui/transmutability/enums/should_respect_endianness.stderr index fcb70813bd96..0845a5edf32e 100644 --- a/src/test/ui/transmutability/enums/should_respect_endianness.stderr +++ b/src/test/ui/transmutability/enums/should_respect_endianness.stderr @@ -1,12 +1,12 @@ error[E0277]: `Src` cannot be safely transmuted into `Unexpected` in the defining scope of `assert::Context`. - --> $DIR/should_respect_endianness.rs:37:36 + --> $DIR/should_respect_endianness.rs:36:36 | LL | assert::is_transmutable::(); | ^^^^^^^^^^ `Src` cannot be safely transmuted into `Unexpected` in the defining scope of `assert::Context`. | = help: the trait `BikeshedIntrinsicFrom` is not implemented for `Unexpected` note: required by a bound in `is_transmutable` - --> $DIR/should_respect_endianness.rs:15:14 + --> $DIR/should_respect_endianness.rs:14:14 | LL | pub fn is_transmutable() | --------------- required by a bound in this From 5c6c3534d9648472940efe00d3d8fb9e15bdd1a2 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 22 Oct 2022 07:37:10 -0400 Subject: [PATCH 0194/1126] Add `lintcheck` to packages linted by `dogfood` test --- tests/dogfood.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 961525bbd910..6d0022f7a5cc 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -20,7 +20,14 @@ fn dogfood_clippy() { } // "" is the root package - for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { + for package in &[ + "", + "clippy_dev", + "clippy_lints", + "clippy_utils", + "lintcheck", + "rustc_tools_util", + ] { run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]); } } From e38bb1a963749ac4e94c13751233bdc61f20e84f Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 22 Oct 2022 07:37:23 -0400 Subject: [PATCH 0195/1126] Apply `--fix` fixes --- lintcheck/src/config.rs | 2 +- lintcheck/src/main.rs | 53 +++++++++++++++++++---------------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index b344db634f61..39ddf0d4d5ab 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -97,7 +97,7 @@ impl LintcheckConfig { Some(&0) => { // automatic choice // Rayon seems to return thread count so half that for core count - (rayon::current_num_threads() / 2) as usize + rayon::current_num_threads() / 2 }, Some(&threads) => threads, // no -j passed, use a single thread diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 95b20d7f0242..b95e889a50fa 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -144,12 +144,12 @@ impl ClippyWarning { } let mut output = String::from("| "); - let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); + let _ = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line); let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message); output.push('\n'); output } else { - format!("{} {} \"{}\"\n", file_with_pos, self.lint_type, self.message) + format!("{file_with_pos} {} \"{}\"\n", self.lint_type, self.message) } } } @@ -161,10 +161,10 @@ fn get(path: &str) -> Result { match ureq::get(path).call() { Ok(res) => return Ok(res), Err(e) if retries >= MAX_RETRIES => return Err(e), - Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e), + Err(ureq::Error::Transport(e)) => eprintln!("Error: {e}"), Err(e) => return Err(e), } - eprintln!("retrying in {} seconds...", retries); + eprintln!("retrying in {retries} seconds..."); thread::sleep(Duration::from_secs(retries as u64)); retries += 1; } @@ -181,11 +181,11 @@ impl CrateSource { let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS); // url to download the crate from crates.io - let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version); - println!("Downloading and extracting {} {} from {}", name, version, url); + let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download"); + println!("Downloading and extracting {name} {version} from {url}"); create_dirs(&krate_download_dir, &extract_dir); - let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version)); + let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz")); // don't download/extract if we already have done so if !krate_file_path.is_file() { // create a file path to download and write the crate data into @@ -205,7 +205,7 @@ impl CrateSource { Crate { version: version.clone(), name: name.clone(), - path: extract_dir.join(format!("{}-{}/", name, version)), + path: extract_dir.join(format!("{name}-{version}/")), options: options.clone(), } }, @@ -218,12 +218,12 @@ impl CrateSource { let repo_path = { let mut repo_path = PathBuf::from(LINTCHECK_SOURCES); // add a -git suffix in case we have the same crate from crates.io and a git repo - repo_path.push(format!("{}-git", name)); + repo_path.push(format!("{name}-git")); repo_path }; // clone the repo if we have not done so if !repo_path.is_dir() { - println!("Cloning {} and checking out {}", url, commit); + println!("Cloning {url} and checking out {commit}"); if !Command::new("git") .arg("clone") .arg(url) @@ -232,7 +232,7 @@ impl CrateSource { .expect("Failed to clone git repo!") .success() { - eprintln!("Failed to clone {} into {}", url, repo_path.display()) + eprintln!("Failed to clone {url} into {}", repo_path.display()) } } // check out the commit/branch/whatever @@ -245,7 +245,7 @@ impl CrateSource { .expect("Failed to check out commit") .success() { - eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display()) + eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()) } Crate { @@ -261,11 +261,11 @@ impl CrateSource { // as a result of this filter. let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name); if dest_crate_root.exists() { - println!("Deleting existing directory at {:?}", dest_crate_root); + println!("Deleting existing directory at {dest_crate_root:?}"); std::fs::remove_dir_all(&dest_crate_root).unwrap(); } - println!("Copying {:?} to {:?}", path, dest_crate_root); + println!("Copying {path:?} to {dest_crate_root:?}"); fn is_cache_dir(entry: &DirEntry) -> bool { std::fs::read(entry.path().join("CACHEDIR.TAG")) @@ -389,10 +389,7 @@ impl Crate { let all_output = Command::new(&cargo_clippy_path) // use the looping index to create individual target dirs - .env( - "CARGO_TARGET_DIR", - shared_target_dir.join(format!("_{:?}", thread_index)), - ) + .env("CARGO_TARGET_DIR", shared_target_dir.join(format!("_{thread_index:?}"))) .args(&cargo_clippy_args) .current_dir(&self.path) .output() @@ -422,8 +419,8 @@ impl Crate { { let subcrate = &stderr[63..]; println!( - "ERROR: failed to apply some suggetion to {} / to (sub)crate {}", - self.name, subcrate + "ERROR: failed to apply some suggetion to {} / to (sub)crate {subcrate}", + self.name ); } // fast path, we don't need the warnings anyway @@ -459,7 +456,7 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = std::fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = - toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e)); + toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display())); // parse the hashmap of the toml file into a list of crates let tomlcrates: Vec = crate_list.crates.into_values().collect(); @@ -498,7 +495,7 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) || tk.git_hash.is_some() != tk.git_url.is_some() { - eprintln!("tomlkrate: {:?}", tk); + eprintln!("tomlkrate: {tk:?}"); if tk.git_hash.is_some() != tk.git_url.is_some() { panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!"); } @@ -526,13 +523,13 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect(); // sort by "000{count} {clippy::lintname}" // to not have a lint with 200 and 2 warnings take the same spot - stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint)); + stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}")); let mut header = String::from("| lint | count |\n"); header.push_str("| -------------------------------------------------- | ----- |\n"); let stats_string = stats .iter() - .map(|(lint, count)| format!("| {:<50} | {:>4} |\n", lint, count)) + .map(|(lint, count)| format!("| {lint:<50} | {count:>4} |\n")) .fold(header, |mut table, line| { table.push_str(&line); table @@ -731,7 +728,7 @@ fn main() { write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); for (cratename, msg) in ices.iter() { - let _ = write!(text, "{}: '{}'", cratename, msg); + let _ = write!(text, "{cratename}: '{msg}'"); } println!("Writing logs to {}", config.lintcheck_results_path.display()); @@ -795,7 +792,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us .iter() .filter(|(new_key, _)| old_stats_deduped.get::(new_key).is_none()) .for_each(|(new_key, new_value)| { - println!("{} 0 => {}", new_key, new_value); + println!("{new_key} 0 => {new_value}"); }); // list all changed counts (key is in both maps but value differs) @@ -804,7 +801,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us .filter(|(new_key, _new_val)| old_stats_deduped.get::(new_key).is_some()) .for_each(|(new_key, new_val)| { let old_val = old_stats_deduped.get::(new_key).unwrap(); - println!("{} {} => {}", new_key, old_val, new_val); + println!("{new_key} {old_val} => {new_val}"); }); // list all gone counts (key is in old status but not in new stats) @@ -813,7 +810,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us .filter(|(old_key, _)| new_stats_deduped.get::<&String>(old_key).is_none()) .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key)) .for_each(|(old_key, old_value)| { - println!("{} {} => 0", old_key, old_value); + println!("{old_key} {old_value} => 0"); }); } From bbee1c9d1f53fc8614fa2dc2737329555bc01b26 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 22 Oct 2022 07:12:07 -0400 Subject: [PATCH 0196/1126] Apply manual fixes --- lintcheck/src/config.rs | 3 +- lintcheck/src/driver.rs | 2 +- lintcheck/src/main.rs | 85 +++++++++++++++++++++----------------- lintcheck/src/recursive.rs | 8 ++-- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index 39ddf0d4d5ab..b8824024e6c7 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -73,8 +73,7 @@ impl LintcheckConfig { let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { clap_config .get_one::("crates-toml") - .map(|s| &**s) - .unwrap_or("lintcheck/lintcheck_crates.toml") + .map_or("lintcheck/lintcheck_crates.toml", |s| &**s) .into() }); diff --git a/lintcheck/src/driver.rs b/lintcheck/src/driver.rs index 63221bab32d3..47724a2fedb0 100644 --- a/lintcheck/src/driver.rs +++ b/lintcheck/src/driver.rs @@ -5,7 +5,7 @@ use std::net::TcpStream; use std::process::{self, Command, Stdio}; use std::{env, mem}; -/// 1. Sends [DriverInfo] to the [crate::recursive::LintcheckServer] running on `addr` +/// 1. Sends [`DriverInfo`] to the [`crate::recursive::LintcheckServer`] running on `addr` /// 2. Receives [bool] from the server, if `false` returns `None` /// 3. Otherwise sends the stderr of running `clippy-driver` to the server fn run_clippy(addr: &str) -> Option { diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index b95e889a50fa..54c1b80c42db 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -116,12 +116,13 @@ impl ClippyWarning { let span = diag.spans.into_iter().find(|span| span.is_primary)?; - let file = match Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { - Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), - Err(_) => format!( + let file = if let Ok(stripped) = Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { + format!("$CARGO_HOME/{}", stripped.display()) + } else { + format!( "target/lintcheck/sources/{}-{}/{}", crate_name, crate_version, span.file_name - ), + ) }; Some(Self { @@ -154,6 +155,7 @@ impl ClippyWarning { } } +#[allow(clippy::result_large_err)] fn get(path: &str) -> Result { const MAX_RETRIES: u8 = 4; let mut retries = 0; @@ -165,7 +167,7 @@ fn get(path: &str) -> Result { Err(e) => return Err(e), } eprintln!("retrying in {retries} seconds..."); - thread::sleep(Duration::from_secs(retries as u64)); + thread::sleep(Duration::from_secs(u64::from(retries))); retries += 1; } } @@ -232,7 +234,7 @@ impl CrateSource { .expect("Failed to clone git repo!") .success() { - eprintln!("Failed to clone {url} into {}", repo_path.display()) + eprintln!("Failed to clone {url} into {}", repo_path.display()); } } // check out the commit/branch/whatever @@ -245,7 +247,7 @@ impl CrateSource { .expect("Failed to check out commit") .success() { - eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()) + eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()); } Crate { @@ -256,6 +258,12 @@ impl CrateSource { } }, CrateSource::Path { name, path, options } => { + fn is_cache_dir(entry: &DirEntry) -> bool { + std::fs::read(entry.path().join("CACHEDIR.TAG")) + .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) + .unwrap_or(false) + } + // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file. // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory // as a result of this filter. @@ -267,12 +275,6 @@ impl CrateSource { println!("Copying {path:?} to {dest_crate_root:?}"); - fn is_cache_dir(entry: &DirEntry) -> bool { - std::fs::read(entry.path().join("CACHEDIR.TAG")) - .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) - .unwrap_or(false) - } - for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) { let entry = entry.unwrap(); let entry_path = entry.path(); @@ -301,6 +303,7 @@ impl CrateSource { impl Crate { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy /// issued + #[allow(clippy::too_many_arguments)] fn run_clippy_lints( &self, cargo_clippy_path: &Path, @@ -345,14 +348,14 @@ impl Crate { clippy_args.push(opt); } } else { - clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]) + clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]); } if lint_filter.is_empty() { clippy_args.push("--cap-lints=warn"); } else { clippy_args.push("--cap-lints=allow"); - clippy_args.extend(lint_filter.iter().map(|filter| filter.as_str())) + clippy_args.extend(lint_filter.iter().map(std::string::String::as_str)); } if let Some(server) = server { @@ -463,7 +466,7 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => // multiple Cratesources) let mut crate_sources = Vec::new(); - tomlcrates.into_iter().for_each(|tk| { + for tk in tomlcrates { if let Some(ref path) = tk.path { crate_sources.push(CrateSource::Path { name: tk.name.clone(), @@ -472,13 +475,13 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { }); } else if let Some(ref versions) = tk.versions { // if we have multiple versions, save each one - versions.iter().for_each(|ver| { + for ver in versions.iter() { crate_sources.push(CrateSource::CratesIo { name: tk.name.clone(), version: ver.to_string(), options: tk.options.clone(), }); - }) + } } else if tk.git_url.is_some() && tk.git_hash.is_some() { // otherwise, we should have a git source crate_sources.push(CrateSource::Git { @@ -496,15 +499,18 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { || tk.git_hash.is_some() != tk.git_url.is_some() { eprintln!("tomlkrate: {tk:?}"); - if tk.git_hash.is_some() != tk.git_url.is_some() { - panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!"); - } - if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) { - panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"); - } + assert_eq!( + tk.git_hash.is_some(), + tk.git_url.is_some(), + "Error: Encountered TomlCrate with only one of git_hash and git_url!" + ); + assert!( + tk.path.is_none() || (tk.git_hash.is_none() && tk.versions.is_none()), + "Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields" + ); unreachable!("Failed to translate TomlCrate into CrateSource!"); } - }); + } // sort the crates crate_sources.sort(); @@ -566,6 +572,7 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path, paths: [&Path; 2]) -> bool logs_modified < clippy_modified } +#[allow(clippy::too_many_lines)] fn main() { // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive` if let Ok(addr) = env::var("LINTCHECK_SERVER") { @@ -671,7 +678,7 @@ fn main() { .unwrap(); let server = config.recursive.then(|| { - let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); + fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive").unwrap_or_default(); LintcheckServer::spawn(recursive_options) }); @@ -727,7 +734,7 @@ fn main() { } write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); - for (cratename, msg) in ices.iter() { + for (cratename, msg) in &ices { let _ = write!(text, "{cratename}: '{msg}'"); } @@ -780,10 +787,10 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us let mut new_stats_deduped = new_stats; // remove duplicates from both hashmaps - same_in_both_hashmaps.iter().for_each(|(k, v)| { + for (k, v) in &same_in_both_hashmaps { assert!(old_stats_deduped.remove(k) == Some(*v)); assert!(new_stats_deduped.remove(k) == Some(*v)); - }); + } println!("\nStats:"); @@ -821,19 +828,21 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us /// This function panics if creating one of the dirs fails. fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create lintcheck target dir"); - } + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create lintcheck target dir" + ); }); std::fs::create_dir(krate_download_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate download dir"); - } + assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir"); }); std::fs::create_dir(extract_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate extraction dir"); - } + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create crate extraction dir" + ); }); } diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs index 67dcfc2b199c..49072e65192f 100644 --- a/lintcheck/src/recursive.rs +++ b/lintcheck/src/recursive.rs @@ -1,7 +1,7 @@ //! In `--recursive` mode we set the `lintcheck` binary as the `RUSTC_WRAPPER` of `cargo check`, -//! this allows [crate::driver] to be run for every dependency. The driver connects to -//! [LintcheckServer] to ask if it should be skipped, and if not sends the stderr of running clippy -//! on the crate to the server +//! this allows [`crate::driver`] to be run for every dependency. The driver connects to +//! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running +//! clippy on the crate to the server use crate::ClippyWarning; use crate::RecursiveOptions; @@ -109,8 +109,8 @@ impl LintcheckServer { Self { local_addr, - sender, receiver, + sender, } } From 7ee72256eba49faa53b6885460b5769279893885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 22 Oct 2022 13:50:02 +0200 Subject: [PATCH 0197/1126] scip: minor clean-ups Avoids a couple redundant hash map lookups and so. --- crates/rust-analyzer/src/cli/scip.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index 2c29b3ee3a6f..d70bc3f195f3 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -75,7 +75,7 @@ impl flags::Scip { let mut symbols_emitted: HashSet = HashSet::default(); let mut tokens_to_symbol: HashMap = HashMap::new(); - for file in si.files { + for StaticIndexedFile { file_id, tokens, .. } in si.files { let mut local_count = 0; let mut new_local_symbol = || { let new_symbol = scip::types::Symbol::new_local(local_count); @@ -84,7 +84,6 @@ impl flags::Scip { new_symbol }; - let StaticIndexedFile { file_id, tokens, .. } = file; let relative_path = match get_relative_filepath(&vfs, &rootpath, file_id) { Some(relative_path) => relative_path, None => continue, @@ -107,28 +106,23 @@ impl flags::Scip { let mut occurrence = scip_types::Occurrence::default(); occurrence.range = text_range_to_scip_range(&line_index, range); - occurrence.symbol = match tokens_to_symbol.get(&id) { - Some(symbol) => symbol.clone(), - None => { + occurrence.symbol = tokens_to_symbol + .entry(id) + .or_insert_with(|| { let symbol = match &token.moniker { Some(moniker) => moniker_to_symbol(&moniker), None => new_local_symbol(), }; - - let symbol = scip::symbol::format_symbol(symbol); - tokens_to_symbol.insert(id, symbol.clone()); - symbol - } - }; + scip::symbol::format_symbol(symbol) + }) + .clone(); if let Some(def) = token.definition { if def.range == range { occurrence.symbol_roles |= scip_types::SymbolRole::Definition as i32; } - if !symbols_emitted.contains(&id) { - symbols_emitted.insert(id); - + if symbols_emitted.insert(id) { let mut symbol_info = scip_types::SymbolInformation::default(); symbol_info.symbol = occurrence.symbol.clone(); if let Some(hover) = &token.hover { From bd49d019063478f2f159165734600ee7c6e72ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 22 Oct 2022 14:04:47 +0200 Subject: [PATCH 0198/1126] ide: Remove unnecessary continue. --- crates/ide/src/static_index.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 9e5eb909508f..27ad1a948d13 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -210,9 +210,7 @@ fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Opt let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); if let Some(&[x]) = def.as_deref() { return Some(x); - } else { - continue; - }; + } } None } From ec6d72baa104fd5428af2d5b6c09ed5546a8af40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 21 Oct 2022 22:04:43 +0200 Subject: [PATCH 0199/1126] scip: Rewrite tests to be closer to what we actually do. It's also less code. --- crates/rust-analyzer/src/cli/scip.rs | 85 ++++++++++------------------ 1 file changed, 30 insertions(+), 55 deletions(-) diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index d70bc3f195f3..8b77ccde0ee4 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -8,8 +8,8 @@ use std::{ use crate::line_index::{LineEndings, LineIndex, OffsetEncoding}; use hir::Name; use ide::{ - LineCol, MonikerDescriptorKind, MonikerResult, StaticIndex, StaticIndexedFile, TextRange, - TokenId, + LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId, + TokenStaticData, }; use ide_db::LineIndexDatabase; use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace}; @@ -109,10 +109,7 @@ impl flags::Scip { occurrence.symbol = tokens_to_symbol .entry(id) .or_insert_with(|| { - let symbol = match &token.moniker { - Some(moniker) => moniker_to_symbol(&moniker), - None => new_local_symbol(), - }; + let symbol = token_to_symbol(&token).unwrap_or_else(&mut new_local_symbol); scip::symbol::format_symbol(symbol) }) .clone(); @@ -201,9 +198,11 @@ fn new_descriptor(name: Name, suffix: scip_types::descriptor::Suffix) -> scip_ty /// /// Only returns a Symbol when it's a non-local symbol. /// So if the visibility isn't outside of a document, then it will return None -fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol { +fn token_to_symbol(token: &TokenStaticData) -> Option { use scip_types::descriptor::Suffix::*; + let moniker = token.moniker.as_ref()?; + let package_name = moniker.package_information.name.clone(); let version = moniker.package_information.version.clone(); let descriptors = moniker @@ -227,7 +226,7 @@ fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol { }) .collect(); - scip_types::Symbol { + Some(scip_types::Symbol { scheme: "rust-analyzer".into(), package: Some(scip_types::Package { manager: "cargo".to_string(), @@ -238,19 +237,15 @@ fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol { .into(), descriptors, ..Default::default() - } + }) } #[cfg(test)] mod test { use super::*; - use hir::Semantics; - use ide::{AnalysisHost, FilePosition}; - use ide_db::defs::IdentClass; - use ide_db::{base_db::fixture::ChangeFixture, helpers::pick_best_token}; + use ide::{AnalysisHost, FilePosition, StaticIndex, TextSize}; + use ide_db::base_db::fixture::ChangeFixture; use scip::symbol::format_symbol; - use syntax::SyntaxKind::*; - use syntax::{AstNode, T}; fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) { let mut host = AnalysisHost::default(); @@ -267,53 +262,33 @@ mod test { fn check_symbol(ra_fixture: &str, expected: &str) { let (host, position) = position(ra_fixture); + let analysis = host.analysis(); + let si = StaticIndex::compute(&analysis); + let FilePosition { file_id, offset } = position; - let db = host.raw_database(); - let sema = &Semantics::new(db); - let file = sema.parse(file_id).syntax().clone(); - let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { - IDENT - | INT_NUMBER - | LIFETIME_IDENT - | T![self] - | T![super] - | T![crate] - | T![Self] - | COMMENT => 2, - kind if kind.is_trivia() => 0, - _ => 1, - }) - .expect("OK OK"); - - let navs = sema - .descend_into_macros(original_token.clone()) - .into_iter() - .filter_map(|token| { - IdentClass::classify_token(sema, &token).map(IdentClass::definitions).map(|it| { - it.into_iter().flat_map(|def| { - let module = def.module(db).unwrap(); - let current_crate = module.krate(); - - match MonikerResult::from_def(sema.db, def, current_crate) { - Some(moniker_result) => Some(moniker_to_symbol(&moniker_result)), - None => None, - } - }) - }) - }) - .flatten() - .collect::>(); + let mut found_symbol = None; + for file in &si.files { + if file.file_id != file_id { + continue; + } + for &(range, id) in &file.tokens { + if range.contains(offset - TextSize::from(1)) { + let token = si.tokens.get(id).unwrap(); + found_symbol = token_to_symbol(token); + break; + } + } + } if expected == "" { - assert_eq!(0, navs.len(), "must have no symbols {:?}", navs); + assert!(found_symbol.is_none(), "must have no symbols {:?}", found_symbol); return; } - assert_eq!(1, navs.len(), "must have one symbol {:?}", navs); - - let res = navs.get(0).unwrap(); - let formatted = format_symbol(res.clone()); + assert!(found_symbol.is_some(), "must have one symbol {:?}", found_symbol); + let res = found_symbol.unwrap(); + let formatted = format_symbol(res); assert_eq!(formatted, expected); } From 6459d7f817ddb91fa2bdc4de8ff61e7905177686 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Sat, 22 Oct 2022 17:49:38 +0900 Subject: [PATCH 0200/1126] Support const generics for builtin derive macro --- .../builtin_derive_macro.rs | 36 +++++--- crates/hir-expand/src/builtin_derive_macro.rs | 86 ++++++++----------- 2 files changed, 62 insertions(+), 60 deletions(-) diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index 6819e9114a02..fafcde25ae70 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -12,11 +12,11 @@ fn test_copy_expand_simple() { #[derive(Copy)] struct Foo; "#, - expect![[r##" + expect![[r#" #[derive(Copy)] struct Foo; -impl < > core::marker::Copy for Foo< > {}"##]], +impl < > core::marker::Copy for Foo< > {}"#]], ); } @@ -33,7 +33,7 @@ macro Copy {} #[derive(Copy)] struct Foo; "#, - expect![[r##" + expect![[r#" #[rustc_builtin_macro] macro derive {} #[rustc_builtin_macro] @@ -41,7 +41,7 @@ macro Copy {} #[derive(Copy)] struct Foo; -impl < > crate ::marker::Copy for Foo< > {}"##]], +impl < > crate ::marker::Copy for Foo< > {}"#]], ); } @@ -53,11 +53,11 @@ fn test_copy_expand_with_type_params() { #[derive(Copy)] struct Foo; "#, - expect![[r##" + expect![[r#" #[derive(Copy)] struct Foo; -impl core::marker::Copy for Foo {}"##]], +impl core::marker::Copy for Foo {}"#]], ); } @@ -70,11 +70,11 @@ fn test_copy_expand_with_lifetimes() { #[derive(Copy)] struct Foo; "#, - expect![[r##" + expect![[r#" #[derive(Copy)] struct Foo; -impl core::marker::Copy for Foo {}"##]], +impl core::marker::Copy for Foo {}"#]], ); } @@ -86,10 +86,26 @@ fn test_clone_expand() { #[derive(Clone)] struct Foo; "#, - expect![[r##" + expect![[r#" #[derive(Clone)] struct Foo; -impl core::clone::Clone for Foo {}"##]], +impl core::clone::Clone for Foo {}"#]], + ); +} + +#[test] +fn test_clone_expand_with_const_generics() { + check( + r#" +//- minicore: derive, clone +#[derive(Clone)] +struct Foo(u32); +"#, + expect![[r#" +#[derive(Clone)] +struct Foo(u32); + +impl core::clone::Clone for Foo {}"#]], ); } diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs index 79989bc2e38b..8966047c9b25 100644 --- a/crates/hir-expand/src/builtin_derive_macro.rs +++ b/crates/hir-expand/src/builtin_derive_macro.rs @@ -60,7 +60,8 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option struct BasicAdtInfo { name: tt::Ident, - type_or_const_params: usize, + /// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param. + param_types: Vec>, } fn parse_adt(tt: &tt::Subtree) -> Result { @@ -92,50 +93,22 @@ fn parse_adt(tt: &tt::Subtree) -> Result { let name_token_id = token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified); let name_token = tt::Ident { id: name_token_id, text: name.text().into() }; - let type_or_const_params = - params.map_or(0, |type_param_list| type_param_list.type_or_const_params().count()); - Ok(BasicAdtInfo { name: name_token, type_or_const_params }) -} - -fn make_type_args(n: usize, bound: Vec) -> Vec { - let mut result = Vec::::with_capacity(n * 2); - result.push( - tt::Leaf::Punct(tt::Punct { - char: '<', - spacing: tt::Spacing::Alone, - id: tt::TokenId::unspecified(), + let param_types = params + .into_iter() + .flat_map(|param_list| param_list.type_or_const_params()) + .map(|param| { + if let ast::TypeOrConstParam::Const(param) = param { + let ty = param + .ty() + .map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0) + .unwrap_or_default(); + Some(ty) + } else { + None + } }) - .into(), - ); - for i in 0..n { - if i > 0 { - result.push( - tt::Leaf::Punct(tt::Punct { - char: ',', - spacing: tt::Spacing::Alone, - id: tt::TokenId::unspecified(), - }) - .into(), - ); - } - result.push( - tt::Leaf::Ident(tt::Ident { - id: tt::TokenId::unspecified(), - text: format!("T{}", i).into(), - }) - .into(), - ); - result.extend(bound.iter().cloned()); - } - result.push( - tt::Leaf::Punct(tt::Punct { - char: '>', - spacing: tt::Spacing::Alone, - id: tt::TokenId::unspecified(), - }) - .into(), - ); - result + .collect(); + Ok(BasicAdtInfo { name: name_token, param_types }) } fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult { @@ -143,14 +116,27 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu Ok(info) => info, Err(e) => return ExpandResult::only_err(e), }; + let (params, args): (Vec<_>, Vec<_>) = info + .param_types + .into_iter() + .enumerate() + .map(|(idx, param_ty)| { + let ident = tt::Leaf::Ident(tt::Ident { + id: tt::TokenId::unspecified(), + text: format!("T{idx}").into(), + }); + let ident_ = ident.clone(); + if let Some(ty) = param_ty { + (quote! { const #ident : #ty , }, quote! { #ident_ , }) + } else { + let bound = trait_path.clone(); + (quote! { #ident : #bound , }, quote! { #ident_ , }) + } + }) + .unzip(); let name = info.name; - let trait_path_clone = trait_path.token_trees.clone(); - let bound = (quote! { : ##trait_path_clone }).token_trees; - let type_params = make_type_args(info.type_or_const_params, bound); - let type_args = make_type_args(info.type_or_const_params, Vec::new()); - let trait_path = trait_path.token_trees; let expanded = quote! { - impl ##type_params ##trait_path for #name ##type_args {} + impl < ##params > #trait_path for #name < ##args > {} }; ExpandResult::ok(expanded) } From 8039a07a5e13fa0690b56f3c6074e5581a4fd4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 22 Oct 2022 15:10:03 +0200 Subject: [PATCH 0201/1126] ide: Generate monikers for local crates. --- crates/ide/src/moniker.rs | 28 +++++++++---------- crates/rust-analyzer/src/cli/lsif.rs | 6 ++--- crates/rust-analyzer/src/cli/scip.rs | 40 +++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 852a8fd83761..07d117aff10b 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -73,8 +73,8 @@ impl MonikerResult { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PackageInformation { pub name: String, - pub repo: String, - pub version: String, + pub repo: Option, + pub version: Option, } pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option { @@ -256,18 +256,18 @@ pub(crate) fn def_to_moniker( let (name, repo, version) = match krate.origin(db) { CrateOrigin::CratesIo { repo, name } => ( name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()), - repo?, - krate.version(db)?, + repo, + krate.version(db), ), CrateOrigin::Lang(lang) => ( krate.display_name(db)?.canonical_name().to_string(), - "https://github.com/rust-lang/rust/".to_string(), - match lang { + Some("https://github.com/rust-lang/rust/".to_string()), + Some(match lang { LangCrateOrigin::Other => { "https://github.com/rust-lang/rust/library/".into() } lang => format!("https://github.com/rust-lang/rust/library/{lang}",), - }, + }), ), }; PackageInformation { name, repo, version } @@ -315,7 +315,7 @@ pub mod module { } "#, "foo::module::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Import, ); check_moniker( @@ -331,7 +331,7 @@ pub mod module { } "#, "foo::module::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -348,7 +348,7 @@ pub mod module { } "#, "foo::module::MyTrait::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -365,7 +365,7 @@ pub mod module { } "#, "foo::module::MyTrait::MY_CONST", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -382,7 +382,7 @@ pub mod module { } "#, "foo::module::MyTrait::MyType", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -405,7 +405,7 @@ pub mod module { } "#, "foo::module::MyStruct::MyTrait::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -425,7 +425,7 @@ pub struct St { } "#, "foo::St::a", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Import, ); } diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index 748306ea57d4..76abe6589807 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/crates/rust-analyzer/src/cli/lsif.rs @@ -106,12 +106,12 @@ impl LsifManager<'_> { manager: "cargo".to_string(), uri: None, content: None, - repository: Some(lsif::Repository { - url: pi.repo, + repository: pi.repo.map(|url| lsif::Repository { + url, r#type: "git".to_string(), commit_id: None, }), - version: Some(pi.version), + version: pi.version, })); self.package_map.insert(package_information, result_set_id); result_set_id diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index 8b77ccde0ee4..b03f085d692e 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -231,7 +231,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option { package: Some(scip_types::Package { manager: "cargo".to_string(), name: package_name, - version, + version: version.unwrap_or_else(|| ".".to_string()), ..Default::default() }) .into(), @@ -415,4 +415,42 @@ pub mod module { "", ); } + + #[test] + fn global_symbol_for_pub_struct() { + check_symbol( + r#" + //- /lib.rs crate:main + mod foo; + + fn main() { + let _bar = foo::Bar { i: 0 }; + } + //- /foo.rs + pub struct Bar$0 { + pub i: i32, + } + "#, + "rust-analyzer cargo main . foo/Bar#", + ); + } + + #[test] + fn global_symbol_for_pub_struct_reference() { + check_symbol( + r#" + //- /lib.rs crate:main + mod foo; + + fn main() { + let _bar = foo::Bar$0 { i: 0 }; + } + //- /foo.rs + pub struct Bar { + pub i: i32, + } + "#, + "rust-analyzer cargo main . foo/Bar#", + ); + } } From def755edab084e971394e9d9ba929e205e9a1baa Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 22 Oct 2022 17:48:31 +0000 Subject: [PATCH 0202/1126] Clean middle RPITITs correctly in rustdoc --- src/librustdoc/clean/mod.rs | 122 ++++++++++-------- src/test/rustdoc/async-trait.rs | 16 +++ src/test/rustdoc/auxiliary/async-trait-dep.rs | 9 ++ 3 files changed, 94 insertions(+), 53 deletions(-) create mode 100644 src/test/rustdoc/async-trait.rs create mode 100644 src/test/rustdoc/auxiliary/async-trait-dep.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 13d63ffa0ee3..6947e8e8e25a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -415,6 +415,16 @@ fn clean_projection<'tcx>( cx: &mut DocContext<'tcx>, def_id: Option, ) -> Type { + if cx.tcx.def_kind(ty.item_def_id) == DefKind::ImplTraitPlaceholder { + let bounds = cx + .tcx + .explicit_item_bounds(ty.item_def_id) + .iter() + .map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, ty.substs)) + .collect::>(); + return clean_middle_opaque_bounds(cx, bounds); + } + let trait_ = clean_trait_ref_with_bindings(cx, ty.trait_ref(cx.tcx), ThinVec::new()); let self_type = clean_middle_ty(ty.self_ty(), cx, None); let self_def_id = if let Some(def_id) = def_id { @@ -1715,59 +1725,7 @@ pub(crate) fn clean_middle_ty<'tcx>( .iter() .map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, substs)) .collect::>(); - let mut regions = vec![]; - let mut has_sized = false; - let mut bounds = bounds - .iter() - .filter_map(|bound| { - let bound_predicate = bound.kind(); - let trait_ref = match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { - if let Some(r) = clean_middle_region(reg) { - regions.push(GenericBound::Outlives(r)); - } - return None; - } - _ => return None, - }; - - if let Some(sized) = cx.tcx.lang_items().sized_trait() { - if trait_ref.def_id() == sized { - has_sized = true; - return None; - } - } - - let bindings: ThinVec<_> = bounds - .iter() - .filter_map(|bound| { - if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder() - { - if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() { - Some(TypeBinding { - assoc: projection_to_path_segment(proj.projection_ty, cx), - kind: TypeBindingKind::Equality { - term: clean_middle_term(proj.term, cx), - }, - }) - } else { - None - } - } else { - None - } - }) - .collect(); - - Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings)) - }) - .collect::>(); - bounds.extend(regions); - if !has_sized && !bounds.is_empty() { - bounds.insert(0, GenericBound::maybe_sized(cx)); - } - ImplTrait(bounds) + clean_middle_opaque_bounds(cx, bounds) } ty::Closure(..) => panic!("Closure"), @@ -1780,6 +1738,64 @@ pub(crate) fn clean_middle_ty<'tcx>( } } +fn clean_middle_opaque_bounds<'tcx>( + cx: &mut DocContext<'tcx>, + bounds: Vec>, +) -> Type { + let mut regions = vec![]; + let mut has_sized = false; + let mut bounds = bounds + .iter() + .filter_map(|bound| { + let bound_predicate = bound.kind(); + let trait_ref = match bound_predicate.skip_binder() { + ty::PredicateKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { + if let Some(r) = clean_middle_region(reg) { + regions.push(GenericBound::Outlives(r)); + } + return None; + } + _ => return None, + }; + + if let Some(sized) = cx.tcx.lang_items().sized_trait() { + if trait_ref.def_id() == sized { + has_sized = true; + return None; + } + } + + let bindings: ThinVec<_> = bounds + .iter() + .filter_map(|bound| { + if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder() { + if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() { + Some(TypeBinding { + assoc: projection_to_path_segment(proj.projection_ty, cx), + kind: TypeBindingKind::Equality { + term: clean_middle_term(proj.term, cx), + }, + }) + } else { + None + } + } else { + None + } + }) + .collect(); + + Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings)) + }) + .collect::>(); + bounds.extend(regions); + if !has_sized && !bounds.is_empty() { + bounds.insert(0, GenericBound::maybe_sized(cx)); + } + ImplTrait(bounds) +} + pub(crate) fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> Item { let def_id = cx.tcx.hir().local_def_id(field.hir_id).to_def_id(); clean_field_with_def_id(def_id, field.ident.name, clean_ty(field.ty, cx), cx) diff --git a/src/test/rustdoc/async-trait.rs b/src/test/rustdoc/async-trait.rs new file mode 100644 index 000000000000..a473e467473e --- /dev/null +++ b/src/test/rustdoc/async-trait.rs @@ -0,0 +1,16 @@ +// aux-build:async-trait-dep.rs +// edition:2021 + +#![feature(async_fn_in_trait)] +#![allow(incomplete_features)] + +extern crate async_trait_dep; + +pub struct Oink {} + +// @has 'async_trait/struct.Oink.html' '//h4[@class="code-header"]' "async fn woof()" +impl async_trait_dep::Meow for Oink { + async fn woof() { + todo!() + } +} diff --git a/src/test/rustdoc/auxiliary/async-trait-dep.rs b/src/test/rustdoc/auxiliary/async-trait-dep.rs new file mode 100644 index 000000000000..10a55dd0260e --- /dev/null +++ b/src/test/rustdoc/auxiliary/async-trait-dep.rs @@ -0,0 +1,9 @@ +// edition:2021 + +#![feature(async_fn_in_trait)] +#![allow(incomplete_features)] + +pub trait Meow { + /// Who's a good dog? + async fn woof(); +} From 4216caed31b56d1980812960b740af5d93de36b9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 22 Oct 2022 20:34:37 +0000 Subject: [PATCH 0203/1126] filter candidates in pick probe for diagnostics --- compiler/rustc_hir_typeck/src/demand.rs | 41 +++++++++++-------- compiler/rustc_hir_typeck/src/method/probe.rs | 13 ++++-- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 2974ac97f236..7c74ac6f1703 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -530,24 +530,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { checked_ty: Ty<'tcx>, hir_id: hir::HirId, ) -> Vec { - let mut methods = - self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id); - methods.retain(|m| { - self.has_only_self_parameter(m) - && self - .tcx - // This special internal attribute is used to permit - // "identity-like" conversion methods to be suggested here. - // - // FIXME (#46459 and #46460): ideally - // `std::convert::Into::into` and `std::borrow:ToOwned` would - // also be `#[rustc_conversion_suggestion]`, if not for - // method-probing false-positives and -negatives (respectively). - // - // FIXME? Other potential candidate methods: `as_ref` and - // `as_mut`? - .has_attr(m.def_id, sym::rustc_conversion_suggestion) - }); + let methods = self.probe_for_return_type( + span, + probe::Mode::MethodCall, + expected, + checked_ty, + hir_id, + |m| { + self.has_only_self_parameter(m) + && self + .tcx + // This special internal attribute is used to permit + // "identity-like" conversion methods to be suggested here. + // + // FIXME (#46459 and #46460): ideally + // `std::convert::Into::into` and `std::borrow:ToOwned` would + // also be `#[rustc_conversion_suggestion]`, if not for + // method-probing false-positives and -negatives (respectively). + // + // FIXME? Other potential candidate methods: `as_ref` and + // `as_mut`? + .has_attr(m.def_id, sym::rustc_conversion_suggestion) + }, + ); methods } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 74cf2ac32aab..28aa2302f882 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -252,7 +252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// would result in an error (basically, the same criteria we /// would use to decide if a method is a plausible fit for /// ambiguity purposes). - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self, candidate_filter))] pub fn probe_for_return_type( &self, span: Span, @@ -260,6 +260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_type: Ty<'tcx>, self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, + candidate_filter: impl Fn(&ty::AssocItem) -> bool, ) -> Vec { let method_names = self .probe_op( @@ -271,7 +272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, scope_expr_id, ProbeScope::AllTraits, - |probe_cx| Ok(probe_cx.candidate_method_names()), + |probe_cx| Ok(probe_cx.candidate_method_names(candidate_filter)), ) .unwrap_or_default(); method_names @@ -966,12 +967,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - fn candidate_method_names(&self) -> Vec { + fn candidate_method_names( + &self, + candidate_filter: impl Fn(&ty::AssocItem) -> bool, + ) -> Vec { let mut set = FxHashSet::default(); let mut names: Vec<_> = self .inherent_candidates .iter() .chain(&self.extension_candidates) + .filter(|candidate| candidate_filter(&candidate.item)) .filter(|candidate| { if let Some(return_ty) = self.return_type { self.matches_return_type(&candidate.item, None, return_ty) @@ -1689,7 +1694,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pcx.allow_similar_names = true; pcx.assemble_inherent_candidates(); - let method_names = pcx.candidate_method_names(); + let method_names = pcx.candidate_method_names(|_| true); pcx.allow_similar_names = false; let applicable_close_candidates: Vec = method_names .iter() From 44808edeac9d888dba1e40fe7cb7ed11be74c1fa Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Oct 2022 22:48:48 +0200 Subject: [PATCH 0204/1126] add scalar-abi-only field retagging option --- src/tools/miri/README.md | 5 +++ src/tools/miri/src/bin/miri.rs | 11 ++++- src/tools/miri/src/eval.rs | 4 +- src/tools/miri/src/lib.rs | 2 +- src/tools/miri/src/stacked_borrows/mod.rs | 32 ++++++++++++-- .../stacked_borrows/newtype_pair_retagging.rs | 19 ++++++++ .../newtype_pair_retagging.stderr | 44 +++++++++++++++++++ .../fail/stacked_borrows/newtype_retagging.rs | 2 +- .../non_scalar_field_retagging.rs | 19 ++++++++ 9 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.rs create mode 100644 src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.stderr create mode 100644 src/tools/miri/tests/pass/stacked-borrows/non_scalar_field_retagging.rs diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 4f5d406288ff..81c4f5ffef4e 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -377,6 +377,11 @@ to Miri failing to detect cases of undefined behavior in a program. * `-Zmiri-retag-fields` changes Stacked Borrows retagging to recurse into fields. This means that references in fields of structs/enums/tuples/arrays/... are retagged, and in particular, they are protected when passed as function arguments. +* `-Zmiri-retag-fields=` controls when Stacked Borrows retagging recurses into + fields. `all` means it always recurses (like `-Zmiri-retag-fields`), `none` means it never + recurses (the default), `scalar` means it only recurses for types where we would also emit + `noalias` annotations in the generated LLVM IR (types passed as indivudal scalars or pairs of + scalars). * `-Zmiri-tag-gc=` configures how often the pointer tag garbage collector runs. The default is to search for and remove unreachable tags once every `10000` basic blocks. Setting this to `0` disables the garbage collector, which causes some programs to have explosive memory usage diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 5b16fc2948cb..2e114c71d662 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -32,7 +32,7 @@ use rustc_middle::{ }; use rustc_session::{config::CrateType, search_paths::PathKind, CtfeBacktrace}; -use miri::{BacktraceStyle, ProvenanceMode}; +use miri::{BacktraceStyle, ProvenanceMode, RetagFields}; struct MiriCompilerCalls { miri_config: miri::MiriConfig, @@ -426,7 +426,14 @@ fn main() { } else if arg == "-Zmiri-mute-stdout-stderr" { miri_config.mute_stdout_stderr = true; } else if arg == "-Zmiri-retag-fields" { - miri_config.retag_fields = true; + miri_config.retag_fields = RetagFields::Yes; + } else if let Some(retag_fields) = arg.strip_prefix("-Zmiri-retag-fields=") { + miri_config.retag_fields = match retag_fields { + "all" => RetagFields::Yes, + "none" => RetagFields::No, + "scalar" => RetagFields::OnlyScalar, + _ => show_error!("`-Zmiri-retag-fields` can only be `all`, `none`, or `scalar`"), + }; } else if arg == "-Zmiri-track-raw-pointers" { eprintln!( "WARNING: `-Zmiri-track-raw-pointers` has no effect; it is enabled by default" diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index b211f3c5f713..a3fc343f8b67 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -126,7 +126,7 @@ pub struct MiriConfig { /// Report the current instruction being executed every N basic blocks. pub report_progress: Option, /// Whether Stacked Borrows retagging should recurse into fields of datatypes. - pub retag_fields: bool, + pub retag_fields: RetagFields, /// The location of a shared object file to load when calling external functions /// FIXME! consider allowing users to specify paths to multiple SO files, or to a directory pub external_so_file: Option, @@ -163,7 +163,7 @@ impl Default for MiriConfig { mute_stdout_stderr: false, preemption_rate: 0.01, // 1% report_progress: None, - retag_fields: false, + retag_fields: RetagFields::No, external_so_file: None, gc_interval: 10_000, num_cpus: 1, diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 97271c33a2e6..21e65bb1b706 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -105,7 +105,7 @@ pub use crate::mono_hash_map::MonoHashMap; pub use crate::operator::EvalContextExt as _; pub use crate::range_map::RangeMap; pub use crate::stacked_borrows::{ - CallId, EvalContextExt as _, Item, Permission, SbTag, Stack, Stacks, + CallId, EvalContextExt as _, Item, Permission, RetagFields, SbTag, Stack, Stacks, }; pub use crate::tag_gc::{EvalContextExt as _, VisitTags}; diff --git a/src/tools/miri/src/stacked_borrows/mod.rs b/src/tools/miri/src/stacked_borrows/mod.rs index f1dd38e5fc1e..959e351d1a14 100644 --- a/src/tools/miri/src/stacked_borrows/mod.rs +++ b/src/tools/miri/src/stacked_borrows/mod.rs @@ -17,6 +17,7 @@ use rustc_middle::ty::{ Ty, }; use rustc_span::DUMMY_SP; +use rustc_target::abi::Abi; use rustc_target::abi::Size; use smallvec::SmallVec; @@ -114,7 +115,18 @@ pub struct GlobalStateInner { /// The call ids to trace tracked_call_ids: FxHashSet, /// Whether to recurse into datatypes when searching for pointers to retag. - retag_fields: bool, + retag_fields: RetagFields, +} + +#[derive(Copy, Clone, Debug)] +pub enum RetagFields { + /// Don't retag any fields. + No, + /// Retag all fields. + Yes, + /// Only retag fields of types with Scalar and ScalarPair layout, + /// to match the LLVM `noalias` we generate. + OnlyScalar, } impl VisitTags for GlobalStateInner { @@ -173,7 +185,7 @@ impl GlobalStateInner { pub fn new( tracked_pointer_tags: FxHashSet, tracked_call_ids: FxHashSet, - retag_fields: bool, + retag_fields: RetagFields, ) -> Self { GlobalStateInner { next_ptr_tag: SbTag(NonZeroU64::new(1).unwrap()), @@ -999,7 +1011,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>, kind: RetagKind, retag_cause: RetagCause, - retag_fields: bool, + retag_fields: RetagFields, } impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> { #[inline(always)] // yes this helps in our benchmarks @@ -1046,6 +1058,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(()); } + let recurse_for_fields = || { + match self.retag_fields { + RetagFields::No => false, + RetagFields::Yes => true, + RetagFields::OnlyScalar => { + // Matching `ArgAbi::new` at the time of writing, only fields of + // `Scalar` and `ScalarPair` ABI are considered. + matches!(place.layout.abi, Abi::Scalar(..) | Abi::ScalarPair(..)) + } + } + }; + if let Some((ref_kind, protector)) = qualify(place.layout.ty, self.kind) { self.retag_place(place, ref_kind, self.retag_cause, protector)?; } else if matches!(place.layout.ty.kind(), ty::RawPtr(..)) { @@ -1054,7 +1078,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Do *not* recurse into them. // (No need to worry about wide references, those always "qualify". And Boxes // are handles specially by the visitor anyway.) - } else if self.retag_fields + } else if recurse_for_fields() || place.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_box()) { // Recurse deeper. Need to always recurse for `Box` to even hit `visit_box`. diff --git a/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.rs b/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.rs new file mode 100644 index 000000000000..5cefdb08e787 --- /dev/null +++ b/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.rs @@ -0,0 +1,19 @@ +//@compile-flags: -Zmiri-retag-fields=scalar +//@error-pattern: which is protected +struct Newtype<'a>(&'a mut i32, i32); + +fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) { + dealloc(); +} + +// Make sure that we protect references inside structs that are passed as ScalarPair. +fn main() { + let ptr = Box::into_raw(Box::new(0i32)); + #[rustfmt::skip] // I like my newlines + unsafe { + dealloc_while_running( + Newtype(&mut *ptr, 0), + || drop(Box::from_raw(ptr)), + ) + }; +} diff --git a/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.stderr b/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.stderr new file mode 100644 index 000000000000..60a8b2a6260b --- /dev/null +++ b/src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.stderr @@ -0,0 +1,44 @@ +error: Undefined Behavior: not granting access to tag because that would remove [Unique for ] which is protected because it is an argument of call ID + --> RUSTLIB/alloc/src/boxed.rs:LL:CC + | +LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag because that would remove [Unique for ] which is protected because it is an argument of call ID + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: was created by a SharedReadWrite retag at offsets [0x0..0x4] + --> $DIR/newtype_pair_retagging.rs:LL:CC + | +LL | let ptr = Box::into_raw(Box::new(0i32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: is this argument + --> $DIR/newtype_pair_retagging.rs:LL:CC + | +LL | fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) { + | ^^ + = note: BACKTRACE: + = note: inside `std::boxed::Box::::from_raw_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC + = note: inside `std::boxed::Box::::from_raw` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside closure at $DIR/newtype_pair_retagging.rs:LL:CC + --> $DIR/newtype_pair_retagging.rs:LL:CC + | +LL | || drop(Box::from_raw(ptr)), + | ^^^^^^^^^^^^^^^^^^ +note: inside `dealloc_while_running::<[closure@$DIR/newtype_pair_retagging.rs:LL:CC]>` at $DIR/newtype_pair_retagging.rs:LL:CC + --> $DIR/newtype_pair_retagging.rs:LL:CC + | +LL | dealloc(); + | ^^^^^^^^^ +note: inside `main` at $DIR/newtype_pair_retagging.rs:LL:CC + --> $DIR/newtype_pair_retagging.rs:LL:CC + | +LL | / dealloc_while_running( +LL | | Newtype(&mut *ptr, 0), +LL | | || drop(Box::from_raw(ptr)), +LL | | ) + | |_________^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.rs b/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.rs index 6e7413cff5d4..bc3883575c33 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-retag-fields +//@compile-flags: -Zmiri-retag-fields=scalar //@error-pattern: which is protected struct Newtype<'a>(&'a mut i32); diff --git a/src/tools/miri/tests/pass/stacked-borrows/non_scalar_field_retagging.rs b/src/tools/miri/tests/pass/stacked-borrows/non_scalar_field_retagging.rs new file mode 100644 index 000000000000..ddedc19c9998 --- /dev/null +++ b/src/tools/miri/tests/pass/stacked-borrows/non_scalar_field_retagging.rs @@ -0,0 +1,19 @@ +//@compile-flags: -Zmiri-retag-fields=scalar + +struct Newtype<'a>(&'a mut i32, i32, i32); + +fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) { + dealloc(); +} + +// Make sure that with -Zmiri-retag-fields=scalar, we do *not* retag the fields of `Newtype`. +fn main() { + let ptr = Box::into_raw(Box::new(0i32)); + #[rustfmt::skip] // I like my newlines + unsafe { + dealloc_while_running( + Newtype(&mut *ptr, 0, 0), + || drop(Box::from_raw(ptr)), + ) + }; +} From 0f8904ec9cc0d26fda0164bc4a61f8e8ceb4d4ee Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 22 Oct 2022 23:02:59 +0200 Subject: [PATCH 0205/1126] Implement invocation location config --- crates/flycheck/src/lib.rs | 48 ++++++++++++--- crates/project-model/src/build_scripts.rs | 41 +++++++------ crates/project-model/src/cargo_workspace.rs | 3 +- crates/project-model/src/lib.rs | 7 +++ crates/project-model/src/workspace.rs | 4 +- crates/rust-analyzer/src/config.rs | 68 ++++++++++++++++----- crates/rust-analyzer/src/reload.rs | 6 +- docs/user/generated_config.adoc | 30 +++++++-- editors/code/package.json | 38 ++++++++++-- 9 files changed, 188 insertions(+), 57 deletions(-) diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 0debf3270f61..73c3a48b4c5a 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -28,6 +28,13 @@ pub enum InvocationStrategy { PerWorkspace, } +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub enum InvocationLocation { + Root(AbsPathBuf), + #[default] + Workspace, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum FlycheckConfig { CargoCommand { @@ -39,13 +46,13 @@ pub enum FlycheckConfig { features: Vec, extra_args: Vec, extra_env: FxHashMap, - invocation_strategy: InvocationStrategy, }, CustomCommand { command: String, args: Vec, extra_env: FxHashMap, invocation_strategy: InvocationStrategy, + invocation_location: InvocationLocation, }, } @@ -275,7 +282,7 @@ impl FlycheckActor { } fn check_command(&self) -> Command { - let (mut cmd, args, invocation_strategy) = match &self.config { + let (mut cmd, args) = match &self.config { FlycheckConfig::CargoCommand { command, target_triple, @@ -285,7 +292,6 @@ impl FlycheckActor { extra_args, features, extra_env, - invocation_strategy, } => { let mut cmd = Command::new(toolchain::cargo()); cmd.arg(command); @@ -309,18 +315,40 @@ impl FlycheckActor { } } cmd.envs(extra_env); - (cmd, extra_args, invocation_strategy) + (cmd, extra_args) } - FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => { + FlycheckConfig::CustomCommand { + command, + args, + extra_env, + invocation_strategy, + invocation_location, + } => { let mut cmd = Command::new(command); cmd.envs(extra_env); - (cmd, args, invocation_strategy) + + match invocation_location { + InvocationLocation::Workspace => { + match invocation_strategy { + InvocationStrategy::Once => { + cmd.current_dir(&self.root); + } + InvocationStrategy::PerWorkspace => { + // FIXME: cmd.current_dir(&affected_workspace); + cmd.current_dir(&self.root); + } + } + } + InvocationLocation::Root(root) => { + cmd.current_dir(root); + } + } + + (cmd, args) } }; - match invocation_strategy { - InvocationStrategy::PerWorkspace => cmd.current_dir(&self.root), - InvocationStrategy::Once => cmd.args(args), - }; + + cmd.args(args); cmd } diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index 0bb9bd65dccf..b5f837d3c6b5 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -21,7 +21,8 @@ use semver::Version; use serde::Deserialize; use crate::{ - cfg_flag::CfgFlag, CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, Package, + cfg_flag::CfgFlag, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation, + InvocationStrategy, Package, }; #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -55,10 +56,7 @@ impl BuildScriptOutput { } impl WorkspaceBuildScripts { - fn build_command( - config: &CargoConfig, - workspace_root: Option<&path::Path>, - ) -> io::Result { + fn build_command(config: &CargoConfig, current_dir: &path::Path) -> io::Result { let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = Command::new(program); @@ -94,14 +92,11 @@ impl WorkspaceBuildScripts { } } - if let Some(workspace_root) = workspace_root { - cmd.current_dir(workspace_root); - } - cmd } }; + cmd.current_dir(current_dir); cmd.envs(&config.extra_env); if config.wrap_rustc_in_build_scripts { // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use @@ -124,19 +119,21 @@ impl WorkspaceBuildScripts { ) -> io::Result { const RUST_1_62: Version = Version::new(1, 62, 0); - let workspace_root: &path::Path = &workspace.workspace_root().as_ref(); + let current_dir = match &config.invocation_location { + InvocationLocation::Root(root) if config.run_build_script_command.is_some() => { + root.as_path() + } + _ => &workspace.workspace_root(), + } + .as_ref(); - match Self::run_per_ws( - Self::build_command(config, Some(workspace_root))?, - workspace, - progress, - ) { + match Self::run_per_ws(Self::build_command(config, current_dir)?, workspace, progress) { Ok(WorkspaceBuildScripts { error: Some(error), .. }) if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) => { // building build scripts failed, attempt to build with --keep-going so // that we potentially get more build data - let mut cmd = Self::build_command(config, Some(workspace_root))?; + let mut cmd = Self::build_command(config, current_dir)?; cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1"); let mut res = Self::run_per_ws(cmd, workspace, progress)?; res.error = Some(error); @@ -154,7 +151,17 @@ impl WorkspaceBuildScripts { progress: &dyn Fn(String), ) -> io::Result> { assert_eq!(config.invocation_strategy, InvocationStrategy::Once); - let cmd = Self::build_command(config, None)?; + + let current_dir = match &config.invocation_location { + InvocationLocation::Root(root) => root, + InvocationLocation::Workspace => { + return Err(io::Error::new( + io::ErrorKind::Other, + "Cannot run build scripts from workspace with invocation strategy `once`", + )) + } + }; + let cmd = Self::build_command(config, current_dir.as_path().as_ref())?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are // exactly those from `config`. diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index d8f6d62349eb..b4c2ba436772 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -14,7 +14,7 @@ use rustc_hash::FxHashMap; use serde::Deserialize; use serde_json::from_value; -use crate::{utf8_stdout, ManifestPath}; +use crate::{utf8_stdout, InvocationLocation, ManifestPath}; use crate::{CfgOverrides, InvocationStrategy}; /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo @@ -107,6 +107,7 @@ pub struct CargoConfig { /// Extra env vars to set when invoking the cargo command pub extra_env: FxHashMap, pub invocation_strategy: InvocationStrategy, + pub invocation_location: InvocationLocation, } impl CargoConfig { diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index d116ff3c0104..575581fa543a 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -164,3 +164,10 @@ pub enum InvocationStrategy { #[default] PerWorkspace, } + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub enum InvocationLocation { + Root(AbsPathBuf), + #[default] + Workspace, +} diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 01180fcf4c3e..0ec5320997ca 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -332,7 +332,9 @@ impl ProjectWorkspace { config: &CargoConfig, progress: &dyn Fn(String), ) -> Vec> { - if let InvocationStrategy::PerWorkspace = config.invocation_strategy { + if matches!(config.invocation_strategy, InvocationStrategy::PerWorkspace) + || config.run_build_script_command.is_some() + { return workspaces.iter().map(|it| it.run_build_scripts(config, progress)).collect(); } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 3669fda926a7..85322f12a834 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -69,10 +69,16 @@ config_data! { cargo_autoreload: bool = "true", /// Run build scripts (`build.rs`) for more precise code analysis. cargo_buildScripts_enable: bool = "true", + /// Specifies the working directory for running build scripts. + /// - "workspace": run build scripts for a workspace in the workspace's root directory. + /// This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`. + /// - "root": run build scripts in the project's root directory. + /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` + /// is set. + cargo_buildScripts_invocationLocation: InvocationLocation = "\"workspace\"", /// Specifies the invocation strategy to use when running the build scripts command. - /// If `per_workspace` is set, the command will be executed for each workspace from the - /// corresponding workspace root. - /// If `once` is set, the command will be executed once in the project root. + /// If `per_workspace` is set, the command will be executed for each workspace. + /// If `once` is set, the command will be executed once. /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` /// is set. cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"", @@ -129,10 +135,17 @@ config_data! { /// /// Set to `"all"` to pass `--all-features` to Cargo. checkOnSave_features: Option = "null", + /// Specifies the working directory for running checks. + /// - "workspace": run checks for workspaces in the corresponding workspaces' root directories. + // FIXME: Ideally we would support this in some way + /// This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`. + /// - "root": run checks in the project's root directory. + /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` + /// is set. + checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"", /// Specifies the invocation strategy to use when running the checkOnSave command. - /// If `per_workspace` is set, the command will be executed for each workspace from the - /// corresponding workspace root. - /// If `once` is set, the command will be executed once in the project root. + /// If `per_workspace` is set, the command will be executed for each workspace. + /// If `once` is set, the command will be executed once. /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` /// is set. checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"", @@ -1074,6 +1087,12 @@ impl Config { InvocationStrategy::Once => project_model::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace, }, + invocation_location: match self.data.cargo_buildScripts_invocationLocation { + InvocationLocation::Root => { + project_model::InvocationLocation::Root(self.root_path.clone()) + } + InvocationLocation::Workspace => project_model::InvocationLocation::Workspace, + }, run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(), extra_env: self.data.cargo_extraEnv.clone(), } @@ -1097,10 +1116,6 @@ impl Config { if !self.data.checkOnSave_enable { return None; } - let invocation_strategy = match self.data.checkOnSave_invocationStrategy { - InvocationStrategy::Once => flycheck::InvocationStrategy::Once, - InvocationStrategy::PerWorkspace => flycheck::InvocationStrategy::PerWorkspace, - }; let flycheck_config = match &self.data.checkOnSave_overrideCommand { Some(args) if !args.is_empty() => { let mut args = args.clone(); @@ -1109,7 +1124,18 @@ impl Config { command, args, extra_env: self.check_on_save_extra_env(), - invocation_strategy, + invocation_strategy: match self.data.checkOnSave_invocationStrategy { + InvocationStrategy::Once => flycheck::InvocationStrategy::Once, + InvocationStrategy::PerWorkspace => { + flycheck::InvocationStrategy::PerWorkspace + } + }, + invocation_location: match self.data.checkOnSave_invocationLocation { + InvocationLocation::Root => { + flycheck::InvocationLocation::Root(self.root_path.clone()) + } + InvocationLocation::Workspace => flycheck::InvocationLocation::Workspace, + }, } } Some(_) | None => FlycheckConfig::CargoCommand { @@ -1139,7 +1165,6 @@ impl Config { }, extra_args: self.data.checkOnSave_extraArgs.clone(), extra_env: self.check_on_save_extra_env(), - invocation_strategy, }, }; Some(flycheck_config) @@ -1618,6 +1643,13 @@ enum InvocationStrategy { PerWorkspace, } +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] +enum InvocationLocation { + Root, + Workspace, +} + #[derive(Deserialize, Debug, Clone)] #[serde(untagged)] enum LifetimeElisionDef { @@ -2036,8 +2068,16 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "type": "string", "enum": ["per_workspace", "once"], "enumDescriptions": [ - "The command will be executed for each workspace from the corresponding workspace root.", - "The command will be executed once in the project root." + "The command will be executed for each workspace.", + "The command will be executed once." + ], + }, + "InvocationLocation" => set! { + "type": "string", + "enum": ["workspace", "root"], + "enumDescriptions": [ + "The command will be executed in the corresponding workspace root.", + "The command will be executed in the project root." ], }, _ => panic!("missing entry for {}: {}", ty, default), diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index cc7600a2fa98..e1f651786dee 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -473,8 +473,10 @@ impl GlobalState { }; let sender = self.flycheck_sender.clone(); - let (FlycheckConfig::CargoCommand { invocation_strategy, .. } - | FlycheckConfig::CustomCommand { invocation_strategy, .. }) = config; + let invocation_strategy = match config { + FlycheckConfig::CargoCommand { .. } => flycheck::InvocationStrategy::PerWorkspace, + FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy, + }; self.flycheck = match invocation_strategy { flycheck::InvocationStrategy::Once => vec![FlycheckHandle::spawn( diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index e5d4395c345c..502833de72c1 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -24,13 +24,22 @@ Automatically refresh project info via `cargo metadata` on -- Run build scripts (`build.rs`) for more precise code analysis. -- +[[rust-analyzer.cargo.buildScripts.invocationLocation]]rust-analyzer.cargo.buildScripts.invocationLocation (default: `"workspace"`):: ++ +-- +Specifies the working directory for running build scripts. +- "workspace": run build scripts for a workspace in the workspace's root directory. + This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`. +- "root": run build scripts in the project's root directory. +This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` +is set. +-- [[rust-analyzer.cargo.buildScripts.invocationStrategy]]rust-analyzer.cargo.buildScripts.invocationStrategy (default: `"per_workspace"`):: + -- Specifies the invocation strategy to use when running the build scripts command. -If `per_workspace` is set, the command will be executed for each workspace from the -corresponding workspace root. -If `once` is set, the command will be executed once in the project root. +If `per_workspace` is set, the command will be executed for each workspace. +If `once` is set, the command will be executed once. This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` is set. -- @@ -128,13 +137,22 @@ List of features to activate. Defaults to Set to `"all"` to pass `--all-features` to Cargo. -- +[[rust-analyzer.checkOnSave.invocationLocation]]rust-analyzer.checkOnSave.invocationLocation (default: `"workspace"`):: ++ +-- +Specifies the working directory for running checks. +- "workspace": run checks for workspaces in the corresponding workspaces' root directories. + This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`. +- "root": run checks in the project's root directory. +This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` +is set. +-- [[rust-analyzer.checkOnSave.invocationStrategy]]rust-analyzer.checkOnSave.invocationStrategy (default: `"per_workspace"`):: + -- Specifies the invocation strategy to use when running the checkOnSave command. -If `per_workspace` is set, the command will be executed for each workspace from the -corresponding workspace root. -If `once` is set, the command will be executed once in the project root. +If `per_workspace` is set, the command will be executed for each workspace. +If `once` is set, the command will be executed once. This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` is set. -- diff --git a/editors/code/package.json b/editors/code/package.json index 62ac1e60b00a..6771cad28a79 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -432,8 +432,21 @@ "default": true, "type": "boolean" }, + "rust-analyzer.cargo.buildScripts.invocationLocation": { + "markdownDescription": "Specifies the working directory for running build scripts.\n- \"workspace\": run build scripts for a workspace in the workspace's root directory.\n This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.\n- \"root\": run build scripts in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "default": "workspace", + "type": "string", + "enum": [ + "workspace", + "root" + ], + "enumDescriptions": [ + "The command will be executed in the corresponding workspace root.", + "The command will be executed in the project root." + ] + }, "rust-analyzer.cargo.buildScripts.invocationStrategy": { - "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace` is set, the command will be executed for each workspace from the\ncorresponding workspace root.\nIf `once` is set, the command will be executed once in the project root.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", "default": "per_workspace", "type": "string", "enum": [ @@ -441,8 +454,8 @@ "once" ], "enumDescriptions": [ - "The command will be executed for each workspace from the corresponding workspace root.", - "The command will be executed once in the project root." + "The command will be executed for each workspace.", + "The command will be executed once." ] }, "rust-analyzer.cargo.buildScripts.overrideCommand": { @@ -570,8 +583,21 @@ } ] }, + "rust-analyzer.checkOnSave.invocationLocation": { + "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n This falls back to \"root\" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "default": "workspace", + "type": "string", + "enum": [ + "workspace", + "root" + ], + "enumDescriptions": [ + "The command will be executed in the corresponding workspace root.", + "The command will be executed in the project root." + ] + }, "rust-analyzer.checkOnSave.invocationStrategy": { - "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace` is set, the command will be executed for each workspace from the\ncorresponding workspace root.\nIf `once` is set, the command will be executed once in the project root.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", "default": "per_workspace", "type": "string", "enum": [ @@ -579,8 +605,8 @@ "once" ], "enumDescriptions": [ - "The command will be executed for each workspace from the corresponding workspace root.", - "The command will be executed once in the project root." + "The command will be executed for each workspace.", + "The command will be executed once." ] }, "rust-analyzer.checkOnSave.noDefaultFeatures": { From ecc5d26a61f5c95a671bf1a337df15adcc03d14e Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 22 Oct 2022 17:11:37 -0500 Subject: [PATCH 0206/1126] Bump cargotest servo to 785a344e32db58d4e631fd3cae17fd1f29a721ab This updates the 'cssparser' and 'procedural-masquerade' deps to versions that no longer depend on the proc-macro back-compat hack. --- src/tools/cargotest/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargotest/main.rs b/src/tools/cargotest/main.rs index 95fe98a683ff..61ccc51580f8 100644 --- a/src/tools/cargotest/main.rs +++ b/src/tools/cargotest/main.rs @@ -73,7 +73,7 @@ const TEST_REPOS: &[Test] = &[ Test { name: "servo", repo: "https://github.com/servo/servo", - sha: "caac107ae8145ef2fd20365e2b8fadaf09c2eb3b", + sha: "785a344e32db58d4e631fd3cae17fd1f29a721ab", lock: None, // Only test Stylo a.k.a. Quantum CSS, the parts of Servo going into Firefox. // This takes much less time to build than all of Servo and supports stable Rust. From 74d4eefc1346a8b9242e072df16de4f664b0873c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 23 Oct 2022 09:22:19 +0000 Subject: [PATCH 0207/1126] Workaround unstable stmt_expr_attributes for method receiver expressions. --- compiler/rustc_ast/src/mut_visit.rs | 14 ++++- compiler/rustc_ast/src/visit.rs | 5 ++ compiler/rustc_builtin_macros/src/cfg_eval.rs | 9 ++- compiler/rustc_expand/src/config.rs | 10 ++- compiler/rustc_expand/src/expand.rs | 63 +++++++++++++++++++ compiler/rustc_expand/src/placeholders.rs | 8 +++ src/test/ui/cfg/cfg-method-receiver-ok.rs | 14 +++++ src/test/ui/cfg/cfg-method-receiver.rs | 3 +- src/test/ui/cfg/cfg-method-receiver.stderr | 14 +---- 9 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 src/test/ui/cfg/cfg-method-receiver-ok.rs diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 25022a02f4bb..b970e57e0173 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -152,6 +152,12 @@ pub trait MutVisitor: Sized { noop_visit_expr(e, self); } + /// This method is a hack to workaround unstable of `stmt_expr_attributes`. + /// It can be removed once that feature is stabilized. + fn visit_method_receiver_expr(&mut self, ex: &mut P) { + self.visit_expr(ex) + } + fn filter_map_expr(&mut self, e: P) -> Option> { noop_filter_map_expr(e, self) } @@ -1301,7 +1307,7 @@ pub fn noop_visit_expr( vis.visit_ident(ident); vis.visit_id(id); visit_opt(args, |args| vis.visit_generic_args(args)); - vis.visit_expr(receiver); + vis.visit_method_receiver_expr(receiver); visit_exprs(exprs, vis); vis.visit_span(span); } @@ -1589,3 +1595,9 @@ impl DummyAstNode for Crate { } } } + +impl DummyAstNode for crate::ast_traits::AstNodeWrapper { + fn dummy() -> Self { + crate::ast_traits::AstNodeWrapper::new(N::dummy(), T::dummy()) + } +} diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index e752cc7dc2dc..6f56c1ef0e8d 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -140,6 +140,11 @@ pub trait Visitor<'ast>: Sized { fn visit_expr(&mut self, ex: &'ast Expr) { walk_expr(self, ex) } + /// This method is a hack to workaround unstable of `stmt_expr_attributes`. + /// It can be removed once that feature is stabilized. + fn visit_method_receiver_expr(&mut self, ex: &'ast Expr) { + self.visit_expr(ex) + } fn visit_expr_post(&mut self, _ex: &'ast Expr) {} fn visit_ty(&mut self, t: &'ast Ty) { walk_ty(self, t) diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 009f3c783d4c..750f1fe121f6 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -210,8 +210,15 @@ impl CfgEval<'_, '_> { } impl MutVisitor for CfgEval<'_, '_> { + #[instrument(level = "trace", skip(self))] fn visit_expr(&mut self, expr: &mut P) { - self.cfg.configure_expr(expr); + self.cfg.configure_expr(expr, false); + mut_visit::noop_visit_expr(expr, self); + } + + #[instrument(level = "trace", skip(self))] + fn visit_method_receiver_expr(&mut self, expr: &mut P) { + self.cfg.configure_expr(expr, true); mut_visit::noop_visit_expr(expr, self); } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 8d4e36407486..1d2b1298a68f 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -469,6 +469,7 @@ impl<'a> StripUnconfigured<'a> { } /// If attributes are not allowed on expressions, emit an error for `attr` + #[instrument(level = "trace", skip(self))] pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { if !self.features.map_or(true, |features| features.stmt_expr_attributes) { let mut err = feature_err( @@ -486,9 +487,12 @@ impl<'a> StripUnconfigured<'a> { } } - pub fn configure_expr(&self, expr: &mut P) { - for attr in expr.attrs.iter() { - self.maybe_emit_expr_attr_err(attr); + #[instrument(level = "trace", skip(self))] + pub fn configure_expr(&self, expr: &mut P, method_receiver: bool) { + if !method_receiver { + for attr in expr.attrs.iter() { + self.maybe_emit_expr_attr_err(attr); + } } // If an expr is valid to cfg away it will have been removed by the diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 15e9a8db3c60..57713fb3cd61 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -50,6 +50,7 @@ macro_rules! ast_fragments { /// Can also serve as an input and intermediate result for macro expansion operations. pub enum AstFragment { OptExpr(Option>), + MethodReceiverExpr(P), $($Kind($AstTy),)* } @@ -57,6 +58,7 @@ macro_rules! ast_fragments { #[derive(Copy, Clone, PartialEq, Eq)] pub enum AstFragmentKind { OptExpr, + MethodReceiverExpr, $($Kind,)* } @@ -64,6 +66,7 @@ macro_rules! ast_fragments { pub fn name(self) -> &'static str { match self { AstFragmentKind::OptExpr => "expression", + AstFragmentKind::MethodReceiverExpr => "expression", $(AstFragmentKind::$Kind => $kind_name,)* } } @@ -72,6 +75,8 @@ macro_rules! ast_fragments { match self { AstFragmentKind::OptExpr => result.make_expr().map(Some).map(AstFragment::OptExpr), + AstFragmentKind::MethodReceiverExpr => + result.make_expr().map(AstFragment::MethodReceiverExpr), $(AstFragmentKind::$Kind => result.$make_ast().map(AstFragment::$Kind),)* } } @@ -98,6 +103,13 @@ macro_rules! ast_fragments { } } + pub fn make_method_receiver_expr(self) -> P { + match self { + AstFragment::MethodReceiverExpr(expr) => expr, + _ => panic!("AstFragment::make_* called on the wrong kind of fragment"), + } + } + $(pub fn $make_ast(self) -> $AstTy { match self { AstFragment::$Kind(ast) => ast, @@ -120,6 +132,7 @@ macro_rules! ast_fragments { } }); } + AstFragment::MethodReceiverExpr(expr) => vis.visit_method_receiver_expr(expr), $($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)?)* $($(AstFragment::$Kind(ast) => ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast)),)?)* @@ -130,6 +143,7 @@ macro_rules! ast_fragments { match *self { AstFragment::OptExpr(Some(ref expr)) => visitor.visit_expr(expr), AstFragment::OptExpr(None) => {} + AstFragment::MethodReceiverExpr(ref expr) => visitor.visit_method_receiver_expr(expr), $($(AstFragment::$Kind(ref ast) => visitor.$visit_ast(ast),)?)* $($(AstFragment::$Kind(ref ast) => for ast_elt in &ast[..] { visitor.$visit_ast_elt(ast_elt, $($args)*); @@ -222,6 +236,7 @@ impl AstFragmentKind { match self { AstFragmentKind::OptExpr | AstFragmentKind::Expr + | AstFragmentKind::MethodReceiverExpr | AstFragmentKind::Stmts | AstFragmentKind::Ty | AstFragmentKind::Pat => SupportsMacroExpansion::Yes { supports_inner_attrs: false }, @@ -285,6 +300,9 @@ impl AstFragmentKind { AstFragmentKind::Expr => AstFragment::Expr( items.next().expect("expected exactly one expression").expect_expr(), ), + AstFragmentKind::MethodReceiverExpr => AstFragment::MethodReceiverExpr( + items.next().expect("expected exactly one expression").expect_expr(), + ), AstFragmentKind::OptExpr => { AstFragment::OptExpr(items.next().map(Annotatable::expect_expr)) } @@ -893,6 +911,7 @@ pub fn parse_ast_fragment<'a>( AstFragment::Stmts(stmts) } AstFragmentKind::Expr => AstFragment::Expr(this.parse_expr()?), + AstFragmentKind::MethodReceiverExpr => AstFragment::MethodReceiverExpr(this.parse_expr()?), AstFragmentKind::OptExpr => { if this.token != token::Eof { AstFragment::OptExpr(Some(this.parse_expr()?)) @@ -1477,6 +1496,42 @@ impl InvocationCollectorNode for AstNodeWrapper, OptExprTag> { } } +/// This struct is a hack to workaround unstable of `stmt_expr_attributes`. +/// It can be removed once that feature is stabilized. +struct MethodReceiverTag; +impl DummyAstNode for MethodReceiverTag { + fn dummy() -> MethodReceiverTag { + MethodReceiverTag + } +} +impl InvocationCollectorNode for AstNodeWrapper, MethodReceiverTag> { + type OutputTy = Self; + type AttrsTy = ast::AttrVec; + const KIND: AstFragmentKind = AstFragmentKind::MethodReceiverExpr; + fn descr() -> &'static str { + "an expression" + } + fn to_annotatable(self) -> Annotatable { + Annotatable::Expr(self.wrapped) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + AstNodeWrapper::new(fragment.make_method_receiver_expr(), MethodReceiverTag) + } + fn noop_visit(&mut self, visitor: &mut V) { + noop_visit_expr(&mut self.wrapped, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, ast::ExprKind::MacCall(..)) + } + fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + let node = self.wrapped.into_inner(); + match node.kind { + ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, invocations: Vec<(Invocation, Option>)>, @@ -1840,6 +1895,14 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { self.visit_node(node) } + fn visit_method_receiver_expr(&mut self, node: &mut P) { + visit_clobber(node, |node| { + let mut wrapper = AstNodeWrapper::new(node, MethodReceiverTag); + self.visit_node(&mut wrapper); + wrapper.wrapped + }) + } + fn filter_map_expr(&mut self, node: P) -> Option> { self.flat_map_node(AstNodeWrapper::new(node, OptExprTag)) } diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 3b0d5ddb97b4..faaf3b3fea58 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -55,6 +55,7 @@ pub fn placeholder( }), AstFragmentKind::Expr => AstFragment::Expr(expr_placeholder()), AstFragmentKind::OptExpr => AstFragment::OptExpr(Some(expr_placeholder())), + AstFragmentKind::MethodReceiverExpr => AstFragment::MethodReceiverExpr(expr_placeholder()), AstFragmentKind::Items => AstFragment::Items(smallvec![P(ast::Item { id, span, @@ -296,6 +297,13 @@ impl MutVisitor for PlaceholderExpander { } } + fn visit_method_receiver_expr(&mut self, expr: &mut P) { + match expr.kind { + ast::ExprKind::MacCall(_) => *expr = self.remove(expr.id).make_method_receiver_expr(), + _ => noop_visit_expr(expr, self), + } + } + fn filter_map_expr(&mut self, expr: P) -> Option> { match expr.kind { ast::ExprKind::MacCall(_) => self.remove(expr.id).make_opt_expr(), diff --git a/src/test/ui/cfg/cfg-method-receiver-ok.rs b/src/test/ui/cfg/cfg-method-receiver-ok.rs new file mode 100644 index 000000000000..61ad3b8c17ab --- /dev/null +++ b/src/test/ui/cfg/cfg-method-receiver-ok.rs @@ -0,0 +1,14 @@ +// check-pass + +macro_rules! foo { + () => { + #[allow(unreachable_patterns)] + { + 123i32 + } + }; +} + +fn main() { + let _ = foo!().abs(); +} diff --git a/src/test/ui/cfg/cfg-method-receiver.rs b/src/test/ui/cfg/cfg-method-receiver.rs index 78a072f503fa..71134ff17b52 100644 --- a/src/test/ui/cfg/cfg-method-receiver.rs +++ b/src/test/ui/cfg/cfg-method-receiver.rs @@ -7,6 +7,5 @@ macro_rules! cbor_map { fn main() { cbor_map! { #[cfg(test)] 4}; - //~^ ERROR attributes on expressions are experimental - //~| ERROR removing an expression is not supported in this position + //~^ ERROR removing an expression is not supported in this position } diff --git a/src/test/ui/cfg/cfg-method-receiver.stderr b/src/test/ui/cfg/cfg-method-receiver.stderr index 517fc8168e72..5767a7c1b4b1 100644 --- a/src/test/ui/cfg/cfg-method-receiver.stderr +++ b/src/test/ui/cfg/cfg-method-receiver.stderr @@ -1,12 +1,3 @@ -error[E0658]: attributes on expressions are experimental - --> $DIR/cfg-method-receiver.rs:9:17 - | -LL | cbor_map! { #[cfg(test)] 4}; - | ^^^^^^^^^^^^ - | - = note: see issue #15701 for more information - = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable - error: removing an expression is not supported in this position --> $DIR/cfg-method-receiver.rs:9:17 | @@ -28,7 +19,6 @@ help: you must specify a concrete type for this numeric value, like `i32` LL | cbor_map! { #[cfg(test)] 4_i32}; | ~~~~~ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0658, E0689. -For more information about an error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0689`. From 4bd98443ed368a781812545915cf758e2091128d Mon Sep 17 00:00:00 2001 From: ouz-a Date: Sun, 16 Oct 2022 21:30:32 +0300 Subject: [PATCH 0208/1126] remove misc_cast and validate types --- .../rustc_const_eval/src/interpret/cast.rs | 63 +++++++++++++------ .../src/transform/validate.rs | 25 ++++++-- src/tools/miri/src/shims/intrinsics/simd.rs | 6 +- 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 764224fd0072..f980e606b932 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -42,10 +42,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let res = self.pointer_from_exposed_address_cast(&src, cast_ty)?; self.write_immediate(res, dest)?; } - // FIXME: We shouldn't use `misc_cast` for these but handle them separately. - IntToInt | FloatToInt | FloatToFloat | IntToFloat | FnPtrToPtr | PtrToPtr => { + + IntToInt | IntToFloat => { let src = self.read_immediate(src)?; - let res = self.misc_cast(&src, cast_ty)?; + let res = self.int_to_int_or_float(&src, cast_ty)?; + self.write_immediate(res, dest)?; + } + + FloatToFloat | FloatToInt => { + let src = self.read_immediate(src)?; + let res = self.float_to_float_or_int(&src, cast_ty)?; + self.write_immediate(res, dest)?; + } + + FnPtrToPtr | PtrToPtr => { + let src = self.read_immediate(&src)?; + let res = self.ptr_to_ptr(&src, cast_ty)?; self.write_immediate(res, dest)?; } @@ -126,13 +138,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } - pub fn misc_cast( + pub fn int_to_int_or_float( + &mut self, + src: &ImmTy<'tcx, M::Provenance>, + cast_ty: Ty<'tcx>, + ) -> InterpResult<'tcx, Immediate> { + if (src.layout.ty.is_integral() || src.layout.ty.is_char() || src.layout.ty.is_bool()) + && (cast_ty.is_floating_point() || cast_ty.is_integral() || cast_ty.is_char()) + { + let scalar = src.to_scalar(); + Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into()) + } else { + bug!("Unexpected cast from type {:?}", src.layout.ty) + } + } + + pub fn float_to_float_or_int( &mut self, src: &ImmTy<'tcx, M::Provenance>, cast_ty: Ty<'tcx>, ) -> InterpResult<'tcx, Immediate> { use rustc_type_ir::sty::TyKind::*; - trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty); match src.layout.ty.kind() { // Floating point @@ -142,19 +168,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Float(FloatTy::F64) => { return Ok(self.cast_from_float(src.to_scalar().to_f64()?, cast_ty).into()); } - // The rest is integer/pointer-"like", including fn ptr casts - _ => assert!( - src.layout.ty.is_bool() - || src.layout.ty.is_char() - || src.layout.ty.is_integral() - || src.layout.ty.is_any_ptr(), - "Unexpected cast from type {:?}", - src.layout.ty - ), + _ => { + bug!("Can't cast 'Float' type into {:?}", cast_ty); + } } + } - // # First handle non-scalar source values. - + /// Handles 'FnPtrToPtr' and 'PtrToPtr' casts. + pub fn ptr_to_ptr( + &mut self, + src: &ImmTy<'tcx, M::Provenance>, + cast_ty: Ty<'tcx>, + ) -> InterpResult<'tcx, Immediate> { // Handle casting any ptr to raw ptr (might be a fat ptr). if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() { let dest_layout = self.layout_of(cast_ty)?; @@ -178,11 +203,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)), }; } + } else { + bug!("Can't cast 'Ptr' or 'FnPtr' into {:?}", cast_ty); } - - // # The remaining source values are scalar and "int-like". - let scalar = src.to_scalar(); - Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into()) } pub fn pointer_expose_address_cast( diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 8abf767f89f8..40946344a35d 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -556,21 +556,36 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..)); } Rvalue::Cast(kind, operand, target_type) => { + let op_ty = operand.ty(self.body, self.tcx); match kind { CastKind::DynStar => { // FIXME(dyn-star): make sure nothing needs to be done here. } - // Nothing to check here + // FIXME: Add Checks for these CastKind::PointerFromExposedAddress | CastKind::PointerExposeAddress | CastKind::Pointer(_) => {} - _ => { - let op_ty = operand.ty(self.body, self.tcx); - if op_ty.is_enum() { + CastKind::IntToInt | CastKind::IntToFloat => { + let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool(); + let target_valid = target_type.is_numeric() || target_type.is_char(); + if !input_valid || !target_valid { + self.fail( + location, + format!("Wrong cast kind {kind:?} for the type {op_ty}",), + ); + } + } + CastKind::FnPtrToPtr | CastKind::PtrToPtr => { + if !(op_ty.is_any_ptr() && target_type.is_unsafe_ptr()) { + self.fail(location, "Can't cast {op_ty} into 'Ptr'"); + } + } + CastKind::FloatToFloat | CastKind::FloatToInt => { + if !op_ty.is_floating_point() || !target_type.is_numeric() { self.fail( location, format!( - "enum -> int casts should go through `Rvalue::Discriminant`: {operand:?}:{op_ty} as {target_type}", + "Trying to cast non 'Float' as {kind:?} into {target_type:?}" ), ); } diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index 163d185f66f3..c1b949b1f799 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -437,13 +437,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) { // Int-to-(int|float): always safe (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_)) => - this.misc_cast(&op, dest.layout.ty)?, + this.int_to_int_or_float(&op, dest.layout.ty)?, // Float-to-float: always safe (ty::Float(_), ty::Float(_)) => - this.misc_cast(&op, dest.layout.ty)?, + this.float_to_float_or_int(&op, dest.layout.ty)?, // Float-to-int in safe mode (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast => - this.misc_cast(&op, dest.layout.ty)?, + this.float_to_float_or_int(&op, dest.layout.ty)?, // Float-to-int in unchecked mode (ty::Float(FloatTy::F32), ty::Int(_) | ty::Uint(_)) if !safe_cast => this.float_to_int_unchecked(op.to_scalar().to_f32()?, dest.layout.ty)?.into(), From 859f5594acced1ebd9ca3b0f4705c94a326f84e9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 23 Oct 2022 18:01:35 +0200 Subject: [PATCH 0209/1126] Handle multiple projects sharing dependency correctly in `once` strategy --- crates/project-model/src/build_scripts.rs | 34 +++++++++++++++++------ crates/project-model/src/workspace.rs | 2 +- crates/rust-analyzer/src/main_loop.rs | 5 +++- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index b5f837d3c6b5..a26a7c57acfc 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -56,7 +56,7 @@ impl BuildScriptOutput { } impl WorkspaceBuildScripts { - fn build_command(config: &CargoConfig, current_dir: &path::Path) -> io::Result { + fn build_command(config: &CargoConfig) -> io::Result { let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = Command::new(program); @@ -96,7 +96,6 @@ impl WorkspaceBuildScripts { } }; - cmd.current_dir(current_dir); cmd.envs(&config.extra_env); if config.wrap_rustc_in_build_scripts { // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use @@ -127,15 +126,15 @@ impl WorkspaceBuildScripts { } .as_ref(); - match Self::run_per_ws(Self::build_command(config, current_dir)?, workspace, progress) { + match Self::run_per_ws(Self::build_command(config)?, workspace, current_dir, progress) { Ok(WorkspaceBuildScripts { error: Some(error), .. }) if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) => { // building build scripts failed, attempt to build with --keep-going so // that we potentially get more build data - let mut cmd = Self::build_command(config, current_dir)?; + let mut cmd = Self::build_command(config)?; cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1"); - let mut res = Self::run_per_ws(cmd, workspace, progress)?; + let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?; res.error = Some(error); Ok(res) } @@ -161,11 +160,14 @@ impl WorkspaceBuildScripts { )) } }; - let cmd = Self::build_command(config, current_dir.as_path().as_ref())?; + let cmd = Self::build_command(config)?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are // exactly those from `config`. let mut by_id = FxHashMap::default(); + // some workspaces might depend on the same crates, so we need to duplicate the outputs + // to those collisions + let mut collisions = Vec::new(); let mut res: Vec<_> = workspaces .iter() .enumerate() @@ -173,7 +175,11 @@ impl WorkspaceBuildScripts { let mut res = WorkspaceBuildScripts::default(); for package in workspace.packages() { res.outputs.insert(package, BuildScriptOutput::default()); - by_id.insert(workspace[package].id.clone(), (package, idx)); + if by_id.contains_key(&workspace[package].id) { + collisions.push((&workspace[package].id, idx, package)); + } else { + by_id.insert(workspace[package].id.clone(), (package, idx)); + } } res }) @@ -181,6 +187,7 @@ impl WorkspaceBuildScripts { let errors = Self::run_command( cmd, + current_dir.as_path().as_ref(), |package, cb| { if let Some(&(package, workspace)) = by_id.get(package) { cb(&workspaces[workspace][package].name, &mut res[workspace].outputs[package]); @@ -189,6 +196,11 @@ impl WorkspaceBuildScripts { progress, )?; res.iter_mut().for_each(|it| it.error = errors.clone()); + collisions.into_iter().for_each(|(id, workspace, package)| { + if let Some(&(p, w)) = by_id.get(id) { + res[workspace].outputs[package] = res[w].outputs[p].clone(); + } + }); if tracing::enabled!(tracing::Level::INFO) { for (idx, workspace) in workspaces.iter().enumerate() { @@ -211,6 +223,7 @@ impl WorkspaceBuildScripts { fn run_per_ws( cmd: Command, workspace: &CargoWorkspace, + current_dir: &path::Path, progress: &dyn Fn(String), ) -> io::Result { let mut res = WorkspaceBuildScripts::default(); @@ -226,6 +239,7 @@ impl WorkspaceBuildScripts { res.error = Self::run_command( cmd, + current_dir, |package, cb| { if let Some(&package) = by_id.get(package) { cb(&workspace[package].name, &mut outputs[package]); @@ -251,7 +265,8 @@ impl WorkspaceBuildScripts { } fn run_command( - cmd: Command, + mut cmd: Command, + current_dir: &path::Path, // ideally this would be something like: // with_output_for: impl FnMut(&str, dyn FnOnce(&mut BuildScriptOutput)), // but owned trait objects aren't a thing @@ -265,7 +280,8 @@ impl WorkspaceBuildScripts { e.push('\n'); }; - tracing::info!("Running build scripts: {:?}", cmd); + tracing::info!("Running build scripts in {}: {:?}", current_dir.display(), cmd); + cmd.current_dir(current_dir); let output = stdx::process::spawn_with_streaming_output( cmd, &mut |line| { diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 0ec5320997ca..2780c62ed118 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -333,7 +333,7 @@ impl ProjectWorkspace { progress: &dyn Fn(String), ) -> Vec> { if matches!(config.invocation_strategy, InvocationStrategy::PerWorkspace) - || config.run_build_script_command.is_some() + || config.run_build_script_command.is_none() { return workspaces.iter().map(|it| it.run_build_scripts(config, progress)).collect(); } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 319b86c58b5c..2c928a580405 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -543,7 +543,10 @@ impl GlobalState { diag.fix, ), Err(err) => { - tracing::error!("File with cargo diagnostic not found in VFS: {}", err); + tracing::error!( + "flycheck {id}: File with cargo diagnostic not found in VFS: {}", + err + ); } }; } From 8bc43f99e91a94868fe08bb72b7ce66d7656d0b5 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Mon, 17 Oct 2022 19:41:49 +0200 Subject: [PATCH 0210/1126] Allow specifying multiple alternative suggestions This allows porting uses of span_suggestions() to diagnostic structs. Doesn't work for multipart_suggestions() because the rank would be reversed - the struct would specify multiple spans, each of which has multiple possible replacements, while multipart_suggestions() creates multiple possible replacements, each with multiple spans. --- compiler/rustc_errors/src/diagnostic.rs | 26 ++++- .../src/diagnostics/diagnostic_builder.rs | 2 +- .../src/diagnostics/subdiagnostic.rs | 21 ++-- .../rustc_macros/src/diagnostics/utils.rs | 105 ++++++++++++++++-- .../session-diagnostic/diagnostic-derive.rs | 38 +++++++ .../diagnostic-derive.stderr | 20 +++- .../subdiagnostic-derive.rs | 45 ++++++++ .../subdiagnostic-derive.stderr | 32 +++++- 8 files changed, 263 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index a63fc0ca285d..23f29a24fe79 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -690,6 +690,24 @@ impl Diagnostic { msg: impl Into, suggestions: impl Iterator, applicability: Applicability, + ) -> &mut Self { + self.span_suggestions_with_style( + sp, + msg, + suggestions, + applicability, + SuggestionStyle::ShowCode, + ) + } + + /// [`Diagnostic::span_suggestions()`] but you can set the [`SuggestionStyle`]. + pub fn span_suggestions_with_style( + &mut self, + sp: Span, + msg: impl Into, + suggestions: impl Iterator, + applicability: Applicability, + style: SuggestionStyle, ) -> &mut Self { let mut suggestions: Vec<_> = suggestions.collect(); suggestions.sort(); @@ -706,14 +724,15 @@ impl Diagnostic { self.push_suggestion(CodeSuggestion { substitutions, msg: self.subdiagnostic_message_to_diagnostic_message(msg), - style: SuggestionStyle::ShowCode, + style, applicability, }); self } - /// Prints out a message with multiple suggested edits of the code. - /// See also [`Diagnostic::span_suggestion()`]. + /// Prints out a message with multiple suggested edits of the code, where each edit consists of + /// multiple parts. + /// See also [`Diagnostic::multipart_suggestion()`]. pub fn multipart_suggestions( &mut self, msg: impl Into, @@ -745,6 +764,7 @@ impl Diagnostic { }); self } + /// Prints out a message with a suggested edit of the code. If the suggestion is presented /// inline, it will only show the message and not the suggestion. /// diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 9f7d2661a3e8..3ea83fd09c79 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -454,7 +454,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { self.formatting_init.extend(code_init); Ok(quote! { - #diag.span_suggestion_with_style( + #diag.span_suggestions_with_style( #span_field, rustc_errors::fluent::#slug, #code_field, diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index d1acb7138422..fa0ca5a52423 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -11,9 +11,11 @@ use crate::diagnostics::utils::{ }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path}; +use syn::{spanned::Spanned, Attribute, Meta, MetaList, NestedMeta, Path}; use synstructure::{BindingInfo, Structure, VariantInfo}; +use super::utils::{build_suggestion_code, AllowMultipleAlternatives}; + /// The central struct for constructing the `add_to_diagnostic` method from an annotated struct. pub(crate) struct SubdiagnosticDeriveBuilder { diag: syn::Ident, @@ -414,15 +416,16 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let nested_name = meta.path().segments.last().unwrap().ident.to_string(); let nested_name = nested_name.as_str(); - let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) = meta else { - throw_invalid_nested_attr!(attr, &nested_attr); - }; - match nested_name { "code" => { - let formatted_str = self.build_format(&value.value(), value.span()); let code_field = new_code_ident(); - code.set_once((code_field, formatted_str), span); + let formatting_init = build_suggestion_code( + &code_field, + meta, + self, + AllowMultipleAlternatives::No, + ); + code.set_once((code_field, formatting_init), span); } _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { diag.help("`code` is the only valid nested attribute") @@ -430,14 +433,14 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } } - let Some((code_field, formatted_str)) = code.value() else { + let Some((code_field, formatting_init)) = code.value() else { span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`") .emit(); return Ok(quote! {}); }; let binding = info.binding; - self.formatting_init.extend(quote! { let #code_field = #formatted_str; }); + self.formatting_init.extend(formatting_init); let code_field = if clone_suggestion_code { quote! { #code_field.clone() } } else { diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 61d5007fc30f..374c795d0a63 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -2,7 +2,7 @@ use crate::diagnostics::error::{ span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, DiagnosticDeriveError, }; use proc_macro::Span; -use proc_macro2::TokenStream; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote, ToTokens}; use std::cell::RefCell; use std::collections::{BTreeSet, HashMap}; @@ -395,6 +395,82 @@ pub(super) fn build_field_mapping<'v>(variant: &VariantInfo<'v>) -> HashMap TokenStream { + let values = match meta { + // `code = "foo"` + Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => vec![s], + // `code("foo", "bar")` + Meta::List(MetaList { nested, .. }) => { + if let AllowMultipleAlternatives::No = allow_multiple { + span_err( + meta.span().unwrap(), + "expected exactly one string literal for `code = ...`", + ) + .emit(); + vec![] + } else if nested.is_empty() { + span_err( + meta.span().unwrap(), + "expected at least one string literal for `code(...)`", + ) + .emit(); + vec![] + } else { + nested + .into_iter() + .filter_map(|item| { + if let NestedMeta::Lit(syn::Lit::Str(s)) = item { + Some(s) + } else { + span_err( + item.span().unwrap(), + "`code(...)` must contain only string literals", + ) + .emit(); + None + } + }) + .collect() + } + } + _ => { + span_err( + meta.span().unwrap(), + r#"`code = "..."`/`code(...)` must contain only string literals"#, + ) + .emit(); + vec![] + } + }; + + if let AllowMultipleAlternatives::Yes = allow_multiple { + let formatted_strings: Vec<_> = values + .into_iter() + .map(|value| fields.build_format(&value.value(), value.span())) + .collect(); + quote! { let #code_field = [#(#formatted_strings),*].into_iter(); } + } else if let [value] = values.as_slice() { + let formatted_str = fields.build_format(&value.value(), value.span()); + quote! { let #code_field = #formatted_str; } + } else { + // error handled previously + quote! { let #code_field = String::new(); } + } +} + /// Possible styles for suggestion subdiagnostics. #[derive(Clone, Copy)] pub(super) enum SuggestionKind { @@ -571,21 +647,23 @@ impl SubdiagnosticKind { let nested_name = meta.path().segments.last().unwrap().ident.to_string(); let nested_name = nested_name.as_str(); - let value = match meta { - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value, + let string_value = match meta { + Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => Some(value), + Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { diag.help("a diagnostic slug must be the first argument to the attribute") }), - _ => { - invalid_nested_attr(attr, &nested_attr).emit(); - continue; - } + _ => None, }; match (nested_name, &mut kind) { ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { - let formatted_str = fields.build_format(&value.value(), value.span()); - let code_init = quote! { let #code_field = #formatted_str; }; + let code_init = build_suggestion_code( + code_field, + meta, + fields, + AllowMultipleAlternatives::Yes, + ); code.set_once(code_init, span); } ( @@ -593,6 +671,11 @@ impl SubdiagnosticKind { SubdiagnosticKind::Suggestion { ref mut applicability, .. } | SubdiagnosticKind::MultipartSuggestion { ref mut applicability, .. }, ) => { + let Some(value) = string_value else { + invalid_nested_attr(attr, &nested_attr).emit(); + continue; + }; + let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| { span_err(span, "invalid applicability").emit(); Applicability::Unspecified @@ -623,7 +706,7 @@ impl SubdiagnosticKind { init } else { span_err(span, "suggestion without `code = \"...\"`").emit(); - quote! { let #code_field: String = unreachable!(); } + quote! { let #code_field = std::iter::empty(); } }; } SubdiagnosticKind::Label @@ -644,7 +727,7 @@ impl quote::IdentFragment for SubdiagnosticKind { SubdiagnosticKind::Note => write!(f, "note"), SubdiagnosticKind::Help => write!(f, "help"), SubdiagnosticKind::Warn => write!(f, "warn"), - SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"), + SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestions_with_style"), SubdiagnosticKind::MultipartSuggestion { .. } => { write!(f, "multipart_suggestion_with_style") } diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 46164d573b0b..ca77e483d6ff 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -758,3 +758,41 @@ struct WithDocComment { #[primary_span] span: Span, } + +#[derive(Diagnostic)] +#[diag(compiletest_example)] +struct SuggestionsGood { + #[suggestion(code("foo", "bar"))] + sub: Span, +} + +#[derive(Diagnostic)] +#[diag(compiletest_example)] +struct SuggestionsSingleItem { + #[suggestion(code("foo"))] + sub: Span, +} + +#[derive(Diagnostic)] +#[diag(compiletest_example)] +struct SuggestionsNoItem { + #[suggestion(code())] + //~^ ERROR expected at least one string literal for `code(...)` + sub: Span, +} + +#[derive(Diagnostic)] +#[diag(compiletest_example)] +struct SuggestionsInvalidItem { + #[suggestion(code(foo))] + //~^ ERROR `code(...)` must contain only string literals + sub: Span, +} + +#[derive(Diagnostic)] +#[diag(compiletest_example)] +struct SuggestionsInvalidLiteral { + #[suggestion(code = 3)] + //~^ ERROR `code = "..."`/`code(...)` must contain only string literals + sub: Span, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 0a1c4bddb06a..859c272b6ba9 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -573,6 +573,24 @@ LL | #[subdiagnostic(eager)] | = help: eager subdiagnostics are not supported on lints +error: expected at least one string literal for `code(...)` + --> $DIR/diagnostic-derive.rs:779:18 + | +LL | #[suggestion(code())] + | ^^^^^^ + +error: `code(...)` must contain only string literals + --> $DIR/diagnostic-derive.rs:787:23 + | +LL | #[suggestion(code(foo))] + | ^^^ + +error: `code = "..."`/`code(...)` must contain only string literals + --> $DIR/diagnostic-derive.rs:795:18 + | +LL | #[suggestion(code = 3)] + | ^^^^^^^^ + error: cannot find attribute `nonsense` in this scope --> $DIR/diagnostic-derive.rs:55:3 | @@ -647,7 +665,7 @@ LL | arg: impl IntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg` = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 80 previous errors +error: aborting due to 83 previous errors Some errors have detailed explanations: E0277, E0425. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index 9088ca6ce462..efec85eb52c2 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -661,3 +661,48 @@ enum BL { span: Span, } } + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parser_add_paren)] +struct BM { + #[suggestion_part(code("foo"))] + //~^ ERROR expected exactly one string literal for `code = ...` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parser_add_paren)] +struct BN { + #[suggestion_part(code("foo", "bar"))] + //~^ ERROR expected exactly one string literal for `code = ...` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parser_add_paren)] +struct BO { + #[suggestion_part(code(3))] + //~^ ERROR expected exactly one string literal for `code = ...` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parser_add_paren)] +struct BP { + #[suggestion_part(code())] + //~^ ERROR expected exactly one string literal for `code = ...` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parser_add_paren)] +struct BQ { + #[suggestion_part(code = 3)] + //~^ ERROR `code = "..."`/`code(...)` must contain only string literals + span: Span, + r#type: String, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index b21f9cc94a98..a85a8711eaca 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -415,6 +415,36 @@ error: `#[applicability]` has no effect if all `#[suggestion]`/`#[multipart_sugg LL | #[applicability] | ^^^^^^^^^^^^^^^^ +error: expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive.rs:668:23 + | +LL | #[suggestion_part(code("foo"))] + | ^^^^^^^^^^^ + +error: expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive.rs:677:23 + | +LL | #[suggestion_part(code("foo", "bar"))] + | ^^^^^^^^^^^^^^^^^^ + +error: expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive.rs:686:23 + | +LL | #[suggestion_part(code(3))] + | ^^^^^^^ + +error: expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive.rs:695:23 + | +LL | #[suggestion_part(code())] + | ^^^^^^ + +error: `code = "..."`/`code(...)` must contain only string literals + --> $DIR/subdiagnostic-derive.rs:704:23 + | +LL | #[suggestion_part(code = 3)] + | ^^^^^^^^ + error: cannot find attribute `foo` in this scope --> $DIR/subdiagnostic-derive.rs:63:3 | @@ -475,6 +505,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent` LL | #[label(slug)] | ^^^^ not found in `rustc_errors::fluent` -error: aborting due to 67 previous errors +error: aborting due to 72 previous errors For more information about this error, try `rustc --explain E0425`. From dd51b36fb261513a0886d98c16be7fcc43c66272 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Oct 2022 01:52:23 +0000 Subject: [PATCH 0211/1126] Add normalize hack back --- compiler/rustc_middle/src/ty/relate.rs | 15 ++++++++-- src/test/ui/consts/unnormalized-param-env.rs | 31 ++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/consts/unnormalized-param-env.rs diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index cdb618e030af..b25b4bd4fe36 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -574,8 +574,8 @@ pub fn super_relate_tys<'tcx, R: TypeRelation<'tcx>>( /// it. pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( relation: &mut R, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, + mut a: ty::Const<'tcx>, + mut b: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> { debug!("{}.super_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b); let tcx = relation.tcx(); @@ -596,6 +596,17 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( ); } + // HACK(const_generics): We still need to eagerly evaluate consts when + // relating them because during `normalize_param_env_or_error`, + // we may relate an evaluated constant in a obligation against + // an unnormalized (i.e. unevaluated) const in the param-env. + // FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants + // these `eval` calls can be removed. + if !relation.tcx().features().generic_const_exprs { + a = a.eval(tcx, relation.param_env()); + b = b.eval(tcx, relation.param_env()); + } + // Currently, the values that can be unified are primitive types, // and those that derive both `PartialEq` and `Eq`, corresponding // to structural-match types. diff --git a/src/test/ui/consts/unnormalized-param-env.rs b/src/test/ui/consts/unnormalized-param-env.rs new file mode 100644 index 000000000000..a7bbe4db9929 --- /dev/null +++ b/src/test/ui/consts/unnormalized-param-env.rs @@ -0,0 +1,31 @@ +// check-pass + +pub trait CSpace { + type Traj; +} + +pub struct Const; + +pub trait Obstacle { + fn trajectory_free(&self, t: &FT) + where + CS::Traj: Sized, + CS: CSpace; +} + +// ----- + +const N: usize = 4; + +struct ObstacleSpace2df32; + +impl Obstacle for ObstacleSpace2df32 { + fn trajectory_free(&self, t: &TF) + where + CS::Traj: Sized, + CS: CSpace, + { + } +} + +fn main() {} From 6e6fe30d0f7e3dcbbf3988650311cee179992e5d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 20 Oct 2022 17:53:29 +0000 Subject: [PATCH 0212/1126] Comment why normalization is needed for debug assertions --- compiler/rustc_const_eval/src/interpret/operand.rs | 9 ++++++++- compiler/rustc_mir_build/src/thir/pattern/mod.rs | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 719588a936ce..0c212cf59e17 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -554,6 +554,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { val: &mir::ConstantKind<'tcx>, layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + // FIXME(const_prop): normalization needed b/c const prop lint in + // `mir_drops_elaborated_and_const_checked`, which happens before + // optimized MIR. Only after optimizing the MIR can we guarantee + // that the `RevealAll` pass has happened and that the body's consts + // are normalized, so any call to resolve before that needs to be + // manually normalized. + let val = self.tcx.normalize_erasing_regions(self.param_env, *val); match val { mir::ConstantKind::Ty(ct) => { match ct.kind() { @@ -585,7 +592,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } } - mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, *ty, layout), + mir::ConstantKind::Val(val, ty) => self.const_val_to_op(val, ty, layout), mir::ConstantKind::Unevaluated(uv, _) => { let instance = self.resolve(uv.def, uv.substs)?; Ok(self.eval_to_allocation(GlobalId { instance, promoted: uv.promoted })?.into()) diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 895af80bd7f3..2526522a25c8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -483,7 +483,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // Use `Reveal::All` here because patterns are always monomorphic even if their function // isn't. let param_env_reveal_all = self.param_env.with_reveal_all_normalized(self.tcx); - let substs = self.typeck_results.node_substs(id); + // N.B. There is no guarantee that substs collected in typeck results are fully normalized, + // so they need to be normalized in order to pass to `Instance::resolve`, which will ICE + // if given unnormalized types. + let substs = self + .tcx + .normalize_erasing_regions(param_env_reveal_all, self.typeck_results.node_substs(id)); let instance = match ty::Instance::resolve(self.tcx, param_env_reveal_all, def_id, substs) { Ok(Some(i)) => i, Ok(None) => { From 449a4404f5216a085cc0aa89042b39a4bdff5b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 23 Oct 2022 19:12:23 +0200 Subject: [PATCH 0213/1126] test attr: point at return type if Termination bound unsatisfied --- compiler/rustc_builtin_macros/src/test.rs | 18 ++++++++++++------ .../termination-trait-test-wrong-type.stderr | 12 +++++------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 705141614e25..ad4c84430710 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -103,7 +103,7 @@ pub fn expand_test_or_bench( }; // Note: non-associated fn items are already handled by `expand_test_or_bench` - if !matches!(item.kind, ast::ItemKind::Fn(_)) { + let ast::ItemKind::Fn(fn_) = &item.kind else { let diag = &cx.sess.parse_sess.span_diagnostic; let msg = "the `#[test]` attribute may only be used on a non-associated function"; let mut err = match item.kind { @@ -121,7 +121,7 @@ pub fn expand_test_or_bench( .emit(); return vec![Annotatable::Item(item)]; - } + }; // has_*_signature will report any errors in the type so compilation // will fail. We shouldn't try to expand in this case because the errors @@ -132,12 +132,14 @@ pub fn expand_test_or_bench( return vec![Annotatable::Item(item)]; } - let (sp, attr_sp) = (cx.with_def_site_ctxt(item.span), cx.with_def_site_ctxt(attr_sp)); + let sp = cx.with_def_site_ctxt(item.span); + let ret_ty_sp = cx.with_def_site_ctxt(fn_.sig.decl.output.span()); + let attr_sp = cx.with_def_site_ctxt(attr_sp); let test_id = Ident::new(sym::test, attr_sp); // creates test::$name - let test_path = |name| cx.path(sp, vec![test_id, Ident::from_str_and_span(name, sp)]); + let test_path = |name| cx.path(ret_ty_sp, vec![test_id, Ident::from_str_and_span(name, sp)]); // creates test::ShouldPanic::$name let should_panic_path = |name| { @@ -183,7 +185,7 @@ pub fn expand_test_or_bench( vec![ // super::$test_fn(b) cx.expr_call( - sp, + ret_ty_sp, cx.expr_path(cx.path(sp, vec![item.ident])), vec![cx.expr_ident(sp, b)], ), @@ -207,7 +209,11 @@ pub fn expand_test_or_bench( cx.expr_path(test_path("assert_test_result")), vec![ // $test_fn() - cx.expr_call(sp, cx.expr_path(cx.path(sp, vec![item.ident])), vec![]), // ) + cx.expr_call( + ret_ty_sp, + cx.expr_path(cx.path(sp, vec![item.ident])), + vec![], + ), // ) ], ), // } ), // ) diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr b/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr index 6ee32314607a..9577952119ad 100644 --- a/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr @@ -1,12 +1,10 @@ error[E0277]: the trait bound `f32: Termination` is not satisfied - --> $DIR/termination-trait-test-wrong-type.rs:6:1 + --> $DIR/termination-trait-test-wrong-type.rs:6:31 | -LL | #[test] - | ------- in this procedural macro expansion -LL | / fn can_parse_zero_as_f32() -> Result { -LL | | "0".parse() -LL | | } - | |_^ the trait `Termination` is not implemented for `f32` +LL | #[test] + | ------- in this procedural macro expansion +LL | fn can_parse_zero_as_f32() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Termination` is not implemented for `f32` | = note: required for `Result` to implement `Termination` note: required by a bound in `assert_test_result` From 15cfeb33b08c91090d52685651aa9762b8f3c43f Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sun, 23 Oct 2022 11:53:39 -0700 Subject: [PATCH 0214/1126] Only test pthread_getname_np on linux-gnu --- library/std/src/thread/tests.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 71eb41cd564d..6c9ce6fa0ddb 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -37,7 +37,13 @@ fn test_named_thread() { .unwrap(); } -#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any( + // Note: musl didn't add pthread_getname_np until 1.2.3 + all(target_os = "linux", target_env = "gnu"), + target_os = "macos", + target_os = "ios", + target_os = "watchos" +))] #[test] fn test_named_thread_truncation() { use crate::ffi::CStr; From e521a8d46b4437f94ba1bb85826bb7d00ba13436 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 5 Oct 2022 19:59:45 +0100 Subject: [PATCH 0215/1126] Prevent foreign Rust exceptions from being caught --- library/panic_unwind/src/gcc.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index 261404e8795f..777ae41fab12 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -38,12 +38,23 @@ use alloc::boxed::Box; use core::any::Any; +use core::ptr; use unwind as uw; +// In case where multiple copies of std is compiled into a single binary, +// we use address of this static variable to distinguish an exception raised by +// this copy and some other copy (which needs to be treated as foreign exception). +static CANARY: u8 = 0; + +// NOTE(nbdd0121) +// Once `c_unwind` feature is stabilized, there will be ABI stability requirement +// on this struct. The first two field must be `_Unwind_Exception` and `canary`, +// as it may be accessed by a different version of the std with a different compiler. #[repr(C)] struct Exception { _uwe: uw::_Unwind_Exception, + canary: *const u8, cause: Box, } @@ -54,6 +65,7 @@ pub unsafe fn panic(data: Box) -> u32 { exception_cleanup, private: [0; uw::unwinder_private_data_size], }, + canary: &CANARY, cause: data, }); let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception; @@ -75,10 +87,22 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box { if (*exception).exception_class != rust_exception_class() { uw::_Unwind_DeleteException(exception); super::__rust_foreign_exception(); - } else { - let exception = Box::from_raw(exception as *mut Exception); - exception.cause } + + let exception = exception.cast::(); + // Just access the canary field, avoid accessing the entire `Exception` as + // it can be a foreign Rust exception. + let canary = ptr::addr_of!((*exception).canary).read(); + if !ptr::eq(canary, &CANARY) { + // A foreign Rust exception, treat it slightly differently from other + // foreign exceptions, because call into `_Unwind_DeleteException` will + // call into `__rust_drop_panic` which produces a confusing + // "Rust panic must be rethrown" message. + super::__rust_foreign_exception(); + } + + let exception = Box::from_raw(exception as *mut Exception); + exception.cause } // Rust's exception class identifier. This is used by personality routines to From 86c65d2c1ce5aba2c1c74298c3e42696cff1d41a Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 5 Oct 2022 22:40:07 +0100 Subject: [PATCH 0216/1126] Implement Rust foreign exception protection for EMCC and SEH --- library/panic_unwind/src/emcc.rs | 37 +++++++++++++++++++++++--------- library/panic_unwind/src/seh.rs | 20 ++++++++++++----- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs index 7c233c7c3a1c..57e817ce6ad5 100644 --- a/library/panic_unwind/src/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -47,7 +47,12 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo { name: b"rust_panic\0".as_ptr(), }; +// NOTE(nbdd0121): The `canary` field will be part of stable ABI after `c_unwind` stabilization. +#[repr(C)] struct Exception { + // See `gcc.rs` on why this is present. We already have a static here so just use it. + canary: *const TypeInfo, + // This is necessary because C++ code can capture our exception with // std::exception_ptr and rethrow it multiple times, possibly even in // another thread. @@ -70,16 +75,21 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box { let catch_data = &*(ptr as *mut CatchData); let adjusted_ptr = __cxa_begin_catch(catch_data.ptr as *mut libc::c_void) as *mut Exception; - let out = if catch_data.is_rust_panic { - let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst); - if was_caught { - // Since cleanup() isn't allowed to panic, we just abort instead. - intrinsics::abort(); - } - (*adjusted_ptr).data.take().unwrap() - } else { + if !catch_data.is_rust_panic { super::__rust_foreign_exception(); - }; + } + + let canary = ptr::addr_of!((*adjusted_ptr).canary).read(); + if !ptr::eq(canary, &EXCEPTION_TYPE_INFO) { + super::__rust_foreign_exception(); + } + + let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst); + if was_caught { + // Since cleanup() isn't allowed to panic, we just abort instead. + intrinsics::abort(); + } + let out = (*adjusted_ptr).data.take().unwrap(); __cxa_end_catch(); out } @@ -90,7 +100,14 @@ pub unsafe fn panic(data: Box) -> u32 { if exception.is_null() { return uw::_URC_FATAL_PHASE1_ERROR as u32; } - ptr::write(exception, Exception { caught: AtomicBool::new(false), data: Some(data) }); + ptr::write( + exception, + Exception { + canary: &EXCEPTION_TYPE_INFO, + caught: AtomicBool::new(false), + data: Some(data), + }, + ); __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup); } diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 6b8d06568611..651115a8248a 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -49,9 +49,15 @@ use alloc::boxed::Box; use core::any::Any; use core::mem::{self, ManuallyDrop}; +use core::ptr; use libc::{c_int, c_uint, c_void}; +// NOTE(nbdd0121): The `canary` field will be part of stable ABI after `c_unwind` stabilization. +#[repr(C)] struct Exception { + // See `gcc.rs` on why this is present. We already have a static here so just use it. + canary: *const _TypeDescriptor, + // This needs to be an Option because we catch the exception by reference // and its destructor is executed by the C++ runtime. When we take the Box // out of the exception, we need to leave the exception in a valid state @@ -235,7 +241,7 @@ static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor { macro_rules! define_cleanup { ($abi:tt $abi2:tt) => { unsafe extern $abi fn exception_cleanup(e: *mut Exception) { - if let Exception { data: Some(b) } = e.read() { + if let Exception { data: Some(b), .. } = e.read() { drop(b); super::__rust_drop_panic(); } @@ -265,7 +271,7 @@ pub unsafe fn panic(data: Box) -> u32 { // The ManuallyDrop is needed here since we don't want Exception to be // dropped when unwinding. Instead it will be dropped by exception_cleanup // which is invoked by the C++ runtime. - let mut exception = ManuallyDrop::new(Exception { data: Some(data) }); + let mut exception = ManuallyDrop::new(Exception { canary: &TYPE_DESCRIPTOR, data: Some(data) }); let throw_ptr = &mut exception as *mut _ as *mut _; // This... may seems surprising, and justifiably so. On 32-bit MSVC the @@ -321,8 +327,12 @@ pub unsafe fn cleanup(payload: *mut u8) -> Box { // __rust_try. This happens when a non-Rust foreign exception is caught. if payload.is_null() { super::__rust_foreign_exception(); - } else { - let exception = &mut *(payload as *mut Exception); - exception.data.take().unwrap() } + let exception = payload as *mut Exception; + let canary = ptr::addr_of!((*exception).canary).read(); + if !ptr::eq(canary, &TYPE_DESCRIPTOR) { + // A foreign Rust exception. + super::__rust_foreign_exception(); + } + (*exception).data.take().unwrap() } From daf3063056d6c3dbd402d5b940f0b28aac0e1dff Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 5 Oct 2022 23:58:35 +0100 Subject: [PATCH 0217/1126] Add test case for foreign Rust exceptions --- .../foreign-rust-exceptions/Makefile | 6 ++++++ .../run-make-fulldeps/foreign-rust-exceptions/bar.rs | 7 +++++++ .../run-make-fulldeps/foreign-rust-exceptions/foo.rs | 12 ++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 src/test/run-make-fulldeps/foreign-rust-exceptions/Makefile create mode 100644 src/test/run-make-fulldeps/foreign-rust-exceptions/bar.rs create mode 100644 src/test/run-make-fulldeps/foreign-rust-exceptions/foo.rs diff --git a/src/test/run-make-fulldeps/foreign-rust-exceptions/Makefile b/src/test/run-make-fulldeps/foreign-rust-exceptions/Makefile new file mode 100644 index 000000000000..24d9742aef0b --- /dev/null +++ b/src/test/run-make-fulldeps/foreign-rust-exceptions/Makefile @@ -0,0 +1,6 @@ +include ../tools.mk + +all: + $(RUSTC) bar.rs --crate-type=cdylib + $(RUSTC) foo.rs + $(call RUN,foo) 2>&1 | $(CGREP) "Rust cannot catch foreign exceptions" diff --git a/src/test/run-make-fulldeps/foreign-rust-exceptions/bar.rs b/src/test/run-make-fulldeps/foreign-rust-exceptions/bar.rs new file mode 100644 index 000000000000..5f9efe323609 --- /dev/null +++ b/src/test/run-make-fulldeps/foreign-rust-exceptions/bar.rs @@ -0,0 +1,7 @@ +#![crate_type = "cdylib"] +#![feature(c_unwind)] + +#[no_mangle] +extern "C-unwind" fn panic() { + panic!(); +} diff --git a/src/test/run-make-fulldeps/foreign-rust-exceptions/foo.rs b/src/test/run-make-fulldeps/foreign-rust-exceptions/foo.rs new file mode 100644 index 000000000000..d6a6d94a1949 --- /dev/null +++ b/src/test/run-make-fulldeps/foreign-rust-exceptions/foo.rs @@ -0,0 +1,12 @@ +#![feature(c_unwind)] + +#[link(name = "bar")] +extern "C-unwind" { + fn panic(); +} + +fn main() { + let _ = std::panic::catch_unwind(|| { + unsafe { panic() }; + }); +} From 979d1a2c78a3fd45807eb28ccff5aeea37128e0f Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 11 Oct 2022 18:02:07 +0100 Subject: [PATCH 0218/1126] Apply suggestion Co-authored-by: Amanieu d'Antras --- library/panic_unwind/src/gcc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index 777ae41fab12..0b7a873a691c 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -42,7 +42,7 @@ use core::ptr; use unwind as uw; -// In case where multiple copies of std is compiled into a single binary, +// In case where multiple copies of std exist in a single process, // we use address of this static variable to distinguish an exception raised by // this copy and some other copy (which needs to be treated as foreign exception). static CANARY: u8 = 0; From 4e6d60c837e178950b844bd1235c67ebbb6a5899 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 12 Oct 2022 12:24:59 +0100 Subject: [PATCH 0219/1126] Fix alloc size --- library/panic_unwind/src/emcc.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs index 57e817ce6ad5..c6d42308596c 100644 --- a/library/panic_unwind/src/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -95,8 +95,7 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box { } pub unsafe fn panic(data: Box) -> u32 { - let sz = mem::size_of_val(&data); - let exception = __cxa_allocate_exception(sz) as *mut Exception; + let exception = __cxa_allocate_exception(mem::size_of::()) as *mut Exception; if exception.is_null() { return uw::_URC_FATAL_PHASE1_ERROR as u32; } From 47704bbcc063c2a8f3e88b06cf2f54f6b64b5ae7 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 23 Oct 2022 19:35:47 +0000 Subject: [PATCH 0220/1126] Do not consider repeated lifetime params for elision. --- compiler/rustc_resolve/src/late.rs | 10 +++++----- ...ision-return-type-requires-explicit-lifetime.rs | 3 +++ ...n-return-type-requires-explicit-lifetime.stderr | 14 +++++++++++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index ba3d8f64bbc7..40e71d4fc751 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -565,7 +565,7 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { /// They will be used to determine the correct lifetime for the fn return type. /// The `LifetimeElisionCandidate` is used for diagnostics, to suggest introducing named /// lifetimes. - lifetime_elision_candidates: Option>, + lifetime_elision_candidates: Option>, /// The trait that the current context can refer to. current_trait_ref: Option<(Module<'a>, TraitRef)>, @@ -1799,7 +1799,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { match res { LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => { if let Some(ref mut candidates) = self.lifetime_elision_candidates { - candidates.insert(res, candidate); + candidates.push((res, candidate)); } } LifetimeRes::Infer | LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {} @@ -1910,8 +1910,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // We do not have a `self` candidate, look at the full list. let all_candidates = all_candidates.unwrap(); - if all_candidates.len() == 1 { - Ok(*all_candidates.first().unwrap().0) + if let [(res, _)] = &all_candidates[..] { + Ok(*res) } else { let all_candidates = all_candidates .into_iter() @@ -2391,7 +2391,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Do not account for the parameters we just bound for function lifetime elision. if let Some(ref mut candidates) = self.lifetime_elision_candidates { for (_, res) in function_lifetime_rib.bindings.values() { - candidates.remove(res); + candidates.retain(|(r, _)| r != res); } } diff --git a/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs b/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs index 7a2eba518fef..ba769a4bcc0a 100644 --- a/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs +++ b/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs @@ -42,4 +42,7 @@ fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize { panic!() } +fn l<'a>(_: &'a str, _: &'a str) -> &str { "" } +//~^ ERROR missing lifetime specifier + fn main() {} diff --git a/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr b/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr index d07754879559..5eee953ef189 100644 --- a/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr +++ b/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr @@ -70,6 +70,18 @@ help: consider using the `'a` lifetime LL | fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &'a isize { | ++ -error: aborting due to 6 previous errors +error[E0106]: missing lifetime specifier + --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:45:37 + | +LL | fn l<'a>(_: &'a str, _: &'a str) -> &str { "" } + | ------- ------- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'a` lifetime + | +LL | fn l<'a>(_: &'a str, _: &'a str) -> &'a str { "" } + | ++ + +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0106`. From 79eedef984cb0a4e703e6c2398319d4e818f41e9 Mon Sep 17 00:00:00 2001 From: BlackHoleFox Date: Sun, 23 Oct 2022 15:23:04 -0500 Subject: [PATCH 0221/1126] Fix x86_64-apple-ios target to use the correct target_abi --- compiler/rustc_target/src/spec/apple/tests.rs | 15 ++++++++++++++ .../rustc_target/src/spec/apple_sdk_base.rs | 20 +++++++++++++------ .../rustc_target/src/spec/x86_64_apple_ios.rs | 2 +- 3 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 compiler/rustc_target/src/spec/apple/tests.rs diff --git a/compiler/rustc_target/src/spec/apple/tests.rs b/compiler/rustc_target/src/spec/apple/tests.rs new file mode 100644 index 000000000000..23bfb95c1985 --- /dev/null +++ b/compiler/rustc_target/src/spec/apple/tests.rs @@ -0,0 +1,15 @@ +use crate::spec::{aarch64_apple_ios_sim, aarch64_apple_watchos_sim, x86_64_apple_ios}; + +#[test] +fn simulator_targets_set_abi() { + let all_sim_targets = [ + x86_64_apple_ios::target(), + aarch64_apple_ios_sim::target(), + aarch64_apple_watchos_sim::target(), + // TODO: x86_64-apple-tvos and x86_64-apple-watchos-sim + ]; + + for target in all_sim_targets { + assert_eq!(target.abi, "sim") + } +} diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs index 49e302676a7b..f920ce8444f7 100644 --- a/compiler/rustc_target/src/spec/apple_sdk_base.rs +++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs @@ -1,6 +1,10 @@ use crate::spec::{cvs, TargetOptions}; use std::borrow::Cow; +#[cfg(test)] +#[path = "apple/tests.rs"] +mod tests; + use Arch::*; #[allow(non_camel_case_types)] #[derive(Copy, Clone)] @@ -12,6 +16,7 @@ pub enum Arch { Arm64_32, I386, X86_64, + X86_64_sim, X86_64_macabi, Arm64_macabi, Arm64_sim, @@ -25,7 +30,7 @@ fn target_arch_name(arch: Arch) -> &'static str { Arm64 | Arm64_macabi | Arm64_sim => "arm64", Arm64_32 => "arm64_32", I386 => "i386", - X86_64 | X86_64_macabi => "x86_64", + X86_64 | X86_64_sim | X86_64_macabi => "x86_64", } } @@ -33,7 +38,9 @@ fn target_abi(arch: Arch) -> &'static str { match arch { Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | X86_64 => "", X86_64_macabi | Arm64_macabi => "macabi", - Arm64_sim => "sim", + // x86_64-apple-ios is a simulator target, even though it isn't + // declared that way in the target like the other ones... + Arm64_sim | X86_64_sim => "sim", } } @@ -45,7 +52,7 @@ fn target_cpu(arch: Arch) -> &'static str { Arm64 => "apple-a7", Arm64_32 => "apple-s4", I386 => "yonah", - X86_64 => "core2", + X86_64 | X86_64_sim => "core2", X86_64_macabi => "core2", Arm64_macabi => "apple-a12", Arm64_sim => "apple-a12", @@ -54,7 +61,7 @@ fn target_cpu(arch: Arch) -> &'static str { fn link_env_remove(arch: Arch) -> Cow<'static, [Cow<'static, str>]> { match arch { - Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | X86_64 | Arm64_sim => { + Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | X86_64 | X86_64_sim | Arm64_sim => { cvs!["MACOSX_DEPLOYMENT_TARGET"] } X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"], @@ -62,11 +69,12 @@ fn link_env_remove(arch: Arch) -> Cow<'static, [Cow<'static, str>]> { } pub fn opts(os: &'static str, arch: Arch) -> TargetOptions { + let abi = target_abi(arch); TargetOptions { - abi: target_abi(arch).into(), + abi: abi.into(), cpu: target_cpu(arch).into(), link_env_remove: link_env_remove(arch), has_thread_local: false, - ..super::apple_base::opts(os, target_arch_name(arch), target_abi(arch)) + ..super::apple_base::opts(os, target_arch_name(arch), abi) } } diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs index e6143025d6d2..db23f01c2332 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs @@ -2,7 +2,7 @@ use super::apple_sdk_base::{opts, Arch}; use crate::spec::{StackProbeType, Target, TargetOptions}; pub fn target() -> Target { - let base = opts("ios", Arch::X86_64); + let base = opts("ios", Arch::X86_64_sim); let llvm_target = super::apple_base::ios_sim_llvm_target("x86_64"); Target { From d2a37847809bc3df86afb4c65b5fe5f06d99ce20 Mon Sep 17 00:00:00 2001 From: BlackHoleFox Date: Sun, 23 Oct 2022 15:33:01 -0500 Subject: [PATCH 0222/1126] Fix x86_64-apple-tvos target to use the correct target_abi --- compiler/rustc_target/src/spec/apple/tests.rs | 8 ++++++-- compiler/rustc_target/src/spec/x86_64_apple_tvos.rs | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/spec/apple/tests.rs b/compiler/rustc_target/src/spec/apple/tests.rs index 23bfb95c1985..b45e6e309c11 100644 --- a/compiler/rustc_target/src/spec/apple/tests.rs +++ b/compiler/rustc_target/src/spec/apple/tests.rs @@ -1,12 +1,16 @@ -use crate::spec::{aarch64_apple_ios_sim, aarch64_apple_watchos_sim, x86_64_apple_ios}; +use crate::spec::{ + aarch64_apple_ios_sim, aarch64_apple_watchos_sim, x86_64_apple_ios, x86_64_apple_tvos, +}; #[test] fn simulator_targets_set_abi() { let all_sim_targets = [ x86_64_apple_ios::target(), + x86_64_apple_tvos::target(), aarch64_apple_ios_sim::target(), + // Note: There is currently no ARM64 tvOS simulator target aarch64_apple_watchos_sim::target(), - // TODO: x86_64-apple-tvos and x86_64-apple-watchos-sim + // TODO: x86_64-apple-watchos-sim ]; for target in all_sim_targets { diff --git a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs index 3d54da0867cf..c1fd8e1c8b90 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs @@ -2,7 +2,7 @@ use super::apple_sdk_base::{opts, Arch}; use crate::spec::{StackProbeType, Target, TargetOptions}; pub fn target() -> Target { - let base = opts("tvos", Arch::X86_64); + let base = opts("tvos", Arch::X86_64_sim); Target { llvm_target: "x86_64-apple-tvos".into(), pointer_width: 64, From c9cca33e85352c6ffd0c29a22c7302ca5f761ba3 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 23 Oct 2022 21:30:37 +0100 Subject: [PATCH 0223/1126] Fix windows compilation --- src/test/run-make-fulldeps/foreign-rust-exceptions/foo.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/run-make-fulldeps/foreign-rust-exceptions/foo.rs b/src/test/run-make-fulldeps/foreign-rust-exceptions/foo.rs index d6a6d94a1949..266987c5b6d6 100644 --- a/src/test/run-make-fulldeps/foreign-rust-exceptions/foo.rs +++ b/src/test/run-make-fulldeps/foreign-rust-exceptions/foo.rs @@ -1,6 +1,7 @@ #![feature(c_unwind)] -#[link(name = "bar")] +#[cfg_attr(not(windows), link(name = "bar"))] +#[cfg_attr(windows, link(name = "bar.dll"))] extern "C-unwind" { fn panic(); } From be2401b8bfc824026b477f11b876b5611cb204c0 Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Mon, 26 Sep 2022 18:43:35 -0700 Subject: [PATCH 0224/1126] Split phase change from `MirPass` --- .../src/transform/promote_consts.rs | 4 - compiler/rustc_middle/src/mir/mod.rs | 39 ++++++-- compiler/rustc_mir_transform/src/lib.rs | 21 ++--- compiler/rustc_mir_transform/src/marker.rs | 20 ----- .../rustc_mir_transform/src/pass_manager.rs | 89 ++++++++++++------- compiler/rustc_mir_transform/src/shim.rs | 4 +- 6 files changed, 101 insertions(+), 76 deletions(-) delete mode 100644 compiler/rustc_mir_transform/src/marker.rs diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 4b219300739c..f3ae16da43bd 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -41,10 +41,6 @@ pub struct PromoteTemps<'tcx> { } impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { - fn phase_change(&self) -> Option { - Some(MirPhase::Analysis(AnalysisPhase::Initial)) - } - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // There's not really any point in promoting errorful MIR. // diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index e0e823e2090e..3b80af184be4 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -116,11 +116,6 @@ pub trait MirPass<'tcx> { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>); - /// If this pass causes the MIR to enter a new phase, return that phase. - fn phase_change(&self) -> Option { - None - } - fn is_mir_dump_enabled(&self) -> bool { true } @@ -145,6 +140,35 @@ impl MirPhase { } } +impl Display for MirPhase { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + MirPhase::Built => write!(f, "built"), + MirPhase::Analysis(p) => write!(f, "analysis-{}", p), + MirPhase::Runtime(p) => write!(f, "runtime-{}", p), + } + } +} + +impl Display for AnalysisPhase { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + AnalysisPhase::Initial => write!(f, "initial"), + AnalysisPhase::PostCleanup => write!(f, "post_cleanup"), + } + } +} + +impl Display for RuntimePhase { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + RuntimePhase::Initial => write!(f, "initial"), + RuntimePhase::PostCleanup => write!(f, "post_cleanup"), + RuntimePhase::Optimized => write!(f, "optimized"), + } + } +} + /// Where a specific `mir::Body` comes from. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)] @@ -207,6 +231,9 @@ pub struct Body<'tcx> { /// us to see the difference and forego optimization on the inlined promoted items. pub phase: MirPhase, + /// How many passses we have executed since starting the current phase. Used for debug output. + pub pass_count: usize, + pub source: MirSource<'tcx>, /// A list of source scopes; these are referenced by statements @@ -292,6 +319,7 @@ impl<'tcx> Body<'tcx> { let mut body = Body { phase: MirPhase::Built, + pass_count: 1, source, basic_blocks: BasicBlocks::new(basic_blocks), source_scopes, @@ -325,6 +353,7 @@ impl<'tcx> Body<'tcx> { pub fn new_cfg_only(basic_blocks: IndexVec>) -> Self { let mut body = Body { phase: MirPhase::Built, + pass_count: 1, source: MirSource::item(CRATE_DEF_ID.to_def_id()), basic_blocks: BasicBlocks::new(basic_blocks), source_scopes: IndexVec::new(), diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 5c411fa56578..5be2232547bd 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -71,7 +71,6 @@ mod inline; mod instcombine; mod lower_intrinsics; mod lower_slice_len; -mod marker; mod match_branches; mod multiple_return_terminators; mod normalize_array_len; @@ -303,6 +302,7 @@ fn mir_const<'tcx>( &simplify::SimplifyCfg::new("initial"), &rustc_peek::SanityCheck, // Just a lint ], + None, ); tcx.alloc_steal_mir(body) } @@ -342,6 +342,7 @@ fn mir_promoted<'tcx>( &simplify::SimplifyCfg::new("promote-consts"), &coverage::InstrumentCoverage, ], + Some(MirPhase::Analysis(AnalysisPhase::Initial)), ); let promoted = promote_pass.promoted_fragments.into_inner(); @@ -409,10 +410,8 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) - pm::run_passes( tcx, &mut body, - &[ - &const_prop::ConstProp, - &marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Optimized)), - ], + &[&const_prop::ConstProp], + Some(MirPhase::Runtime(RuntimePhase::Optimized)), ); } } @@ -474,6 +473,7 @@ fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx> &remove_uninit_drops::RemoveUninitDrops, &simplify::SimplifyCfg::new("remove-false-edges"), ], + None, ); check_consts::post_drop_elaboration::check_live_drops(tcx, &body); // FIXME: make this a MIR lint } @@ -498,10 +498,9 @@ fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &cleanup_post_borrowck::CleanupNonCodegenStatements, &simplify::SimplifyCfg::new("early-opt"), &deref_separator::Derefer, - &marker::PhaseChange(MirPhase::Analysis(AnalysisPhase::PostCleanup)), ]; - pm::run_passes(tcx, body, passes); + pm::run_passes(tcx, body, passes, Some(MirPhase::Analysis(AnalysisPhase::PostCleanup))); } /// Returns the sequence of passes that lowers analysis to runtime MIR. @@ -526,9 +525,8 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // CTFE support for aggregates. &deaggregator::Deaggregator, &Lint(const_prop_lint::ConstProp), - &marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Initial)), ]; - pm::run_passes_no_validate(tcx, body, passes); + pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial))); } /// Returns the sequence of passes that do the initial cleanup of runtime MIR. @@ -537,10 +535,9 @@ fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &elaborate_box_derefs::ElaborateBoxDerefs, &lower_intrinsics::LowerIntrinsics, &simplify::SimplifyCfg::new("elaborate-drops"), - &marker::PhaseChange(MirPhase::Runtime(RuntimePhase::PostCleanup)), ]; - pm::run_passes(tcx, body, passes); + pm::run_passes(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::PostCleanup))); } fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { @@ -591,10 +588,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &deduplicate_blocks::DeduplicateBlocks, // Some cleanup necessary at least for LLVM and potentially other codegen backends. &add_call_guards::CriticalCallEdges, - &marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Optimized)), // Dump the end result for testing and debugging purposes. &dump_mir::Marker("PreCodegen"), ], + Some(MirPhase::Runtime(RuntimePhase::Optimized)), ); } diff --git a/compiler/rustc_mir_transform/src/marker.rs b/compiler/rustc_mir_transform/src/marker.rs deleted file mode 100644 index 06819fc1d37d..000000000000 --- a/compiler/rustc_mir_transform/src/marker.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::borrow::Cow; - -use crate::MirPass; -use rustc_middle::mir::{Body, MirPhase}; -use rustc_middle::ty::TyCtxt; - -/// Changes the MIR phase without changing the MIR itself. -pub struct PhaseChange(pub MirPhase); - -impl<'tcx> MirPass<'tcx> for PhaseChange { - fn phase_change(&self) -> Option { - Some(self.0) - } - - fn name(&self) -> Cow<'_, str> { - Cow::from(format!("PhaseChange-{:?}", self.0)) - } - - fn run_pass(&self, _: TyCtxt<'tcx>, _body: &mut Body<'tcx>) {} -} diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 67dae71468f9..230c6a7cb4b0 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -66,10 +66,6 @@ where fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { self.1.run_pass(tcx, body) } - - fn phase_change(&self) -> Option { - self.1.phase_change() - } } /// Run the sequence of passes without validating the MIR after each pass. The MIR is still @@ -78,23 +74,28 @@ pub fn run_passes_no_validate<'tcx>( tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>], + phase_change: Option, ) { - run_passes_inner(tcx, body, passes, false); + run_passes_inner(tcx, body, passes, phase_change, false); } -pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>]) { - run_passes_inner(tcx, body, passes, true); +/// The optional `phase_change` is applied after executing all the passes, if present +pub fn run_passes<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + passes: &[&dyn MirPass<'tcx>], + phase_change: Option, +) { + run_passes_inner(tcx, body, passes, phase_change, true); } fn run_passes_inner<'tcx>( tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>], + phase_change: Option, validate_each: bool, ) { - let start_phase = body.phase; - let mut cnt = 0; - let validate = validate_each & tcx.sess.opts.unstable_opts.validate_mir; let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes; trace!(?overridden_passes); @@ -102,7 +103,6 @@ fn run_passes_inner<'tcx>( for pass in passes { let name = pass.name(); - // Gather information about what we should be doing for this pass let overridden = overridden_passes.iter().rev().find(|(s, _)| s == &*name).map(|(_name, polarity)| { trace!( @@ -112,32 +112,44 @@ fn run_passes_inner<'tcx>( ); *polarity }); - let is_enabled = overridden.unwrap_or_else(|| pass.is_enabled(&tcx.sess)); - let new_phase = pass.phase_change(); - let dump_enabled = (is_enabled && pass.is_mir_dump_enabled()) || new_phase.is_some(); - let validate = (validate && is_enabled) - || new_phase == Some(MirPhase::Runtime(RuntimePhase::Optimized)); + if !overridden.unwrap_or_else(|| pass.is_enabled(&tcx.sess)) { + continue; + } + + let dump_enabled = pass.is_mir_dump_enabled(); if dump_enabled { - dump_mir(tcx, body, start_phase, &name, cnt, false); + dump_mir_for_pass(tcx, body, &name, false); } - if is_enabled { - pass.run_pass(tcx, body); + if validate { + validate_body(tcx, body, format!("before pass {}", name)); } - if dump_enabled { - dump_mir(tcx, body, start_phase, &name, cnt, true); - cnt += 1; - } - if let Some(new_phase) = pass.phase_change() { - if body.phase >= new_phase { - panic!("Invalid MIR phase transition from {:?} to {:?}", body.phase, new_phase); - } - body.phase = new_phase; + pass.run_pass(tcx, body); + + if dump_enabled { + dump_mir_for_pass(tcx, body, &name, true); } if validate { validate_body(tcx, body, format!("after pass {}", name)); } + + body.pass_count += 1; + } + + if let Some(new_phase) = phase_change { + if body.phase >= new_phase { + panic!("Invalid MIR phase transition from {:?} to {:?}", body.phase, new_phase); + } + + body.phase = new_phase; + + dump_mir_for_phase_change(tcx, body); + if validate || new_phase == MirPhase::Runtime(RuntimePhase::Optimized) { + validate_body(tcx, body, format!("after phase change to {}", new_phase)); + } + + body.pass_count = 1; } } @@ -145,22 +157,33 @@ pub fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: Strin validate::Validator { when, mir_phase: body.phase }.run_pass(tcx, body); } -pub fn dump_mir<'tcx>( +pub fn dump_mir_for_pass<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - phase: MirPhase, pass_name: &str, - cnt: usize, is_after: bool, ) { - let phase_index = phase.phase_index(); + let phase_index = body.phase.phase_index(); mir::dump_mir( tcx, - Some(&format_args!("{:03}-{:03}", phase_index, cnt)), + Some(&format_args!("{:03}-{:03}", phase_index, body.pass_count)), pass_name, if is_after { &"after" } else { &"before" }, body, |_, _| Ok(()), ); } + +pub fn dump_mir_for_phase_change<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let phase_index = body.phase.phase_index(); + + mir::dump_mir( + tcx, + Some(&format_args!("{:03}-000", phase_index)), + &format!("{}", body.phase), + &"after", + body, + |_, _| Ok(()), + ) +} diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 6ca58ee458c5..c19380ef89cc 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -17,7 +17,7 @@ use std::iter; use crate::util::expand_aggregate; use crate::{ - abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, marker, + abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, pass_manager as pm, remove_noop_landing_pads, simplify, }; use rustc_middle::mir::patch::MirPatch; @@ -97,8 +97,8 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' &simplify::SimplifyCfg::new("make_shim"), &add_call_guards::CriticalCallEdges, &abort_unwinding_calls::AbortUnwindingCalls, - &marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Optimized)), ], + Some(MirPhase::Runtime(RuntimePhase::Optimized)), ); debug!("make_shim({:?}) = {:?}", instance, result); From ffccfa1eedd049c87b1ad828297ae4c4121b2e40 Mon Sep 17 00:00:00 2001 From: BlackHoleFox Date: Sun, 23 Oct 2022 15:41:07 -0500 Subject: [PATCH 0225/1126] Fix x86_64-apple-watchos-sim target to use the correct target_abi --- compiler/rustc_target/src/spec/apple/tests.rs | 3 ++- compiler/rustc_target/src/spec/apple_sdk_base.rs | 1 + compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_target/src/spec/apple/tests.rs b/compiler/rustc_target/src/spec/apple/tests.rs index b45e6e309c11..d062b36742d6 100644 --- a/compiler/rustc_target/src/spec/apple/tests.rs +++ b/compiler/rustc_target/src/spec/apple/tests.rs @@ -1,5 +1,6 @@ use crate::spec::{ aarch64_apple_ios_sim, aarch64_apple_watchos_sim, x86_64_apple_ios, x86_64_apple_tvos, + x86_64_apple_watchos_sim, }; #[test] @@ -7,10 +8,10 @@ fn simulator_targets_set_abi() { let all_sim_targets = [ x86_64_apple_ios::target(), x86_64_apple_tvos::target(), + x86_64_apple_watchos_sim::target(), aarch64_apple_ios_sim::target(), // Note: There is currently no ARM64 tvOS simulator target aarch64_apple_watchos_sim::target(), - // TODO: x86_64-apple-watchos-sim ]; for target in all_sim_targets { diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs index f920ce8444f7..148031b15697 100644 --- a/compiler/rustc_target/src/spec/apple_sdk_base.rs +++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs @@ -15,6 +15,7 @@ pub enum Arch { Arm64, Arm64_32, I386, + #[allow(dead_code)] // Some targets don't use this enum... X86_64, X86_64_sim, X86_64_macabi, diff --git a/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs index e499b1985e76..550566b2aa75 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs @@ -2,7 +2,7 @@ use super::apple_sdk_base::{opts, Arch}; use crate::spec::{StackProbeType, Target, TargetOptions}; pub fn target() -> Target { - let base = opts("watchos", Arch::X86_64); + let base = opts("watchos", Arch::X86_64_sim); let arch = "x86_64"; let llvm_target = super::apple_base::watchos_sim_llvm_target(arch); From ba847cad6d1403feed2bba8b501c69d0a749f6de Mon Sep 17 00:00:00 2001 From: Soveu Date: Mon, 8 Aug 2022 15:31:32 +0200 Subject: [PATCH 0226/1126] Enable varargs support for calling conventions other than C or cdecl This patch makes it possible to use varargs for calling conventions, which are either based on C (like efiapi) or C is based on them (for example sysv64 and win64). --- compiler/rustc_feature/src/active.rs | 3 ++ compiler/rustc_hir_analysis/src/lib.rs | 48 ++++++++++++------ compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/spec/abi.rs | 22 +++++++++ .../extended-varargs-abi-support.md | 10 ++++ ...ature-gate-extended_varargs_abi_support.rs | 19 +++++++ ...e-gate-extended_varargs_abi_support.stderr | 49 +++++++++++++++++++ src/test/ui/c-variadic/variadic-ffi-1.rs | 4 +- src/test/ui/c-variadic/variadic-ffi-1.stderr | 28 +++++------ src/test/ui/c-variadic/variadic-ffi-2.rs | 15 +++++- src/test/ui/c-variadic/variadic-ffi-2.stderr | 6 +-- src/test/ui/error-codes/E0045.stderr | 4 +- 12 files changed, 174 insertions(+), 35 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md create mode 100644 src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs create mode 100644 src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 1b8d683b1336..0f6561b04c0c 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -390,6 +390,9 @@ declare_features! ( (active, exclusive_range_pattern, "1.11.0", Some(37854), None), /// Allows exhaustive pattern matching on types that contain uninhabited types. (active, exhaustive_patterns, "1.13.0", Some(51085), None), + /// Allows using `efiapi`, `sysv64` and `win64` as calling convention + /// for functions with varargs. + (active, extended_varargs_abi_support, "1.65.0", Some(100189), None), /// Allows defining `extern type`s. (active, extern_types, "1.23.0", Some(43467), None), /// Allows the use of `#[ffi_const]` on foreign functions. diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index dba505149de8..782c95d63355 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -106,7 +106,7 @@ use rustc_middle::middle; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::util; -use rustc_session::config::EntryFnType; +use rustc_session::{config::EntryFnType, parse::feature_err}; use rustc_span::{symbol::sym, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; @@ -118,20 +118,40 @@ use astconv::AstConv; use bounds::Bounds; fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) { - match (decl.c_variadic, abi) { - // The function has the correct calling convention, or isn't a "C-variadic" function. - (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl { .. }) => {} - // The function is a "C-variadic" function with an incorrect calling convention. - (true, _) => { - let mut err = struct_span_err!( - tcx.sess, - span, - E0045, - "C-variadic function must have C or cdecl calling convention" - ); - err.span_label(span, "C-variadics require C or cdecl calling convention").emit(); - } + const ERROR_HEAD: &str = "C-variadic function must have a compatible calling convention"; + const CONVENTIONS_UNSTABLE: &str = "C, cdecl, win64, sysv64 or efiapi"; + const CONVENTIONS_STABLE: &str = "C or cdecl"; + const UNSTABLE_EXPLAIN: &str = + "using different calling convention than C or cdecl for varargs functions is unstable"; + + if !decl.c_variadic || matches!(abi, Abi::C { .. } | Abi::Cdecl { .. }) { + return; } + + let extended_abi_support = tcx.features().extended_varargs_abi_support; + let conventions = match (extended_abi_support, abi.supports_varargs()) { + // User enabled additional ABI support for varargs and function ABI matches those ones. + (true, true) => return, + + // Using this ABI would be ok, if the feature for additional ABI support was enabled. + // Return CONVENTIONS_STABLE, because we want the other error to look the same. + (false, true) => { + feature_err( + &tcx.sess.parse_sess, + sym::extended_varargs_abi_support, + span, + UNSTABLE_EXPLAIN, + ) + .emit(); + CONVENTIONS_STABLE + } + + (false, false) => CONVENTIONS_STABLE, + (true, false) => CONVENTIONS_UNSTABLE, + }; + + let mut err = struct_span_err!(tcx.sess, span, E0045, "{}, like {}", ERROR_HEAD, conventions); + err.span_label(span, ERROR_HEAD).emit(); } fn require_same_types<'tcx>( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3fe79370c374..4a1b20297d9d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -694,6 +694,7 @@ symbols! { export_name, expr, extended_key_value_attributes, + extended_varargs_abi_support, extern_absolute_paths, extern_crate_item_prelude, extern_crate_self, diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs index ce45fa13970b..cb2a0c04c6aa 100644 --- a/compiler/rustc_target/src/spec/abi.rs +++ b/compiler/rustc_target/src/spec/abi.rs @@ -40,6 +40,28 @@ pub enum Abi { RustCold, } +impl Abi { + pub fn supports_varargs(self) -> bool { + // * C and Cdecl obviously support varargs. + // * C can be based on SysV64 or Win64, so they must support varargs. + // * EfiApi is based on Win64 or C, so it also supports it. + // + // * Stdcall does not, because it would be impossible for the callee to clean + // up the arguments. (callee doesn't know how many arguments are there) + // * Same for Fastcall, Vectorcall and Thiscall. + // * System can become Stdcall, so is also a no-no. + // * Other calling conventions are related to hardware or the compiler itself. + match self { + Self::C { .. } + | Self::Cdecl { .. } + | Self::Win64 { .. } + | Self::SysV64 { .. } + | Self::EfiApi => true, + _ => false, + } + } +} + #[derive(Copy, Clone)] pub struct AbiData { abi: Abi, diff --git a/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md b/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md new file mode 100644 index 000000000000..b20c30ec8f1c --- /dev/null +++ b/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md @@ -0,0 +1,10 @@ +# `extended_varargs_abi_support` + +The tracking issue for this feature is: [#100189] + +[#100189]: https://github.com/rust-lang/rust/issues/100189 + +------------------------ + +This feature adds the possibility of using `sysv64`, `win64` or `efiapi` calling +conventions on functions with varargs. diff --git a/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs b/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs new file mode 100644 index 000000000000..e391ee8a0b11 --- /dev/null +++ b/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs @@ -0,0 +1,19 @@ +#![feature(abi_efiapi)] + +fn efiapi(f: extern "efiapi" fn(usize, ...)) { + //~^ ERROR: C-variadic function must have a compatible calling convention, like C or cdecl + //~^^ ERROR: using different calling convention than C or cdecl for varargs functions is unstable + f(22, 44); +} +fn sysv(f: extern "sysv64" fn(usize, ...)) { + //~^ ERROR: C-variadic function must have a compatible calling convention, like C or cdecl + //~^^ ERROR: using different calling convention than C or cdecl for varargs functions is unstable + f(22, 44); +} +fn win(f: extern "win64" fn(usize, ...)) { + //~^ ERROR: C-variadic function must have a compatible calling convention, like C or cdecl + //~^^ ERROR: using different calling convention than C or cdecl for varargs functions is unstable + f(22, 44); +} + +fn main() {} diff --git a/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr b/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr new file mode 100644 index 000000000000..3442d53c1b5f --- /dev/null +++ b/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr @@ -0,0 +1,49 @@ +error[E0658]: using different calling convention than C or cdecl for varargs functions is unstable + --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14 + | +LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #100189 for more information + = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable + +error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl + --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14 + | +LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error[E0658]: using different calling convention than C or cdecl for varargs functions is unstable + --> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12 + | +LL | fn sysv(f: extern "sysv64" fn(usize, ...)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #100189 for more information + = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable + +error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl + --> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12 + | +LL | fn sysv(f: extern "sysv64" fn(usize, ...)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error[E0658]: using different calling convention than C or cdecl for varargs functions is unstable + --> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11 + | +LL | fn win(f: extern "win64" fn(usize, ...)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #100189 for more information + = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable + +error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl + --> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11 + | +LL | fn win(f: extern "win64" fn(usize, ...)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0045, E0658. +For more information about an error, try `rustc --explain E0045`. diff --git a/src/test/ui/c-variadic/variadic-ffi-1.rs b/src/test/ui/c-variadic/variadic-ffi-1.rs index a76efd9a2050..24407a71ce69 100644 --- a/src/test/ui/c-variadic/variadic-ffi-1.rs +++ b/src/test/ui/c-variadic/variadic-ffi-1.rs @@ -6,7 +6,9 @@ trait Sized { } extern "stdcall" { - fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling + fn printf(_: *const u8, ...); + //~^ ERROR: C-variadic function must have a compatible calling convention, + // like C, cdecl, win64, sysv64 or efiapi } extern "C" { diff --git a/src/test/ui/c-variadic/variadic-ffi-1.stderr b/src/test/ui/c-variadic/variadic-ffi-1.stderr index 2ffb80f7ef61..f9d6928b3df1 100644 --- a/src/test/ui/c-variadic/variadic-ffi-1.stderr +++ b/src/test/ui/c-variadic/variadic-ffi-1.stderr @@ -1,17 +1,17 @@ -error[E0045]: C-variadic function must have C or cdecl calling convention +error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl --> $DIR/variadic-ffi-1.rs:9:5 | LL | fn printf(_: *const u8, ...); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention error[E0060]: this function takes at least 2 arguments but 0 arguments were supplied - --> $DIR/variadic-ffi-1.rs:20:9 + --> $DIR/variadic-ffi-1.rs:22:9 | LL | foo(); | ^^^-- two arguments of type `isize` and `u8` are missing | note: function defined here - --> $DIR/variadic-ffi-1.rs:13:8 + --> $DIR/variadic-ffi-1.rs:15:8 | LL | fn foo(f: isize, x: u8, ...); | ^^^ @@ -21,13 +21,13 @@ LL | foo(/* isize */, /* u8 */); | ~~~~~~~~~~~~~~~~~~~~~~~ error[E0060]: this function takes at least 2 arguments but 1 argument was supplied - --> $DIR/variadic-ffi-1.rs:21:9 + --> $DIR/variadic-ffi-1.rs:23:9 | LL | foo(1); | ^^^--- an argument of type `u8` is missing | note: function defined here - --> $DIR/variadic-ffi-1.rs:13:8 + --> $DIR/variadic-ffi-1.rs:15:8 | LL | fn foo(f: isize, x: u8, ...); | ^^^ @@ -37,7 +37,7 @@ LL | foo(1, /* u8 */); | ~~~~~~~~~~~~~ error[E0308]: mismatched types - --> $DIR/variadic-ffi-1.rs:23:56 + --> $DIR/variadic-ffi-1.rs:25:56 | LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo; | ------------------------------------- ^^^ expected non-variadic fn, found variadic function @@ -48,7 +48,7 @@ LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo; found fn item `unsafe extern "C" fn(_, _, ...) {foo}` error[E0308]: mismatched types - --> $DIR/variadic-ffi-1.rs:24:54 + --> $DIR/variadic-ffi-1.rs:26:54 | LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar; | ----------------------------------- ^^^ expected variadic fn, found non-variadic function @@ -59,37 +59,37 @@ LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar; found fn item `extern "C" fn(_, _) {bar}` error[E0617]: can't pass `f32` to variadic function - --> $DIR/variadic-ffi-1.rs:26:19 + --> $DIR/variadic-ffi-1.rs:28:19 | LL | foo(1, 2, 3f32); | ^^^^ help: cast the value to `c_double`: `3f32 as c_double` error[E0617]: can't pass `bool` to variadic function - --> $DIR/variadic-ffi-1.rs:27:19 + --> $DIR/variadic-ffi-1.rs:29:19 | LL | foo(1, 2, true); | ^^^^ help: cast the value to `c_int`: `true as c_int` error[E0617]: can't pass `i8` to variadic function - --> $DIR/variadic-ffi-1.rs:28:19 + --> $DIR/variadic-ffi-1.rs:30:19 | LL | foo(1, 2, 1i8); | ^^^ help: cast the value to `c_int`: `1i8 as c_int` error[E0617]: can't pass `u8` to variadic function - --> $DIR/variadic-ffi-1.rs:29:19 + --> $DIR/variadic-ffi-1.rs:31:19 | LL | foo(1, 2, 1u8); | ^^^ help: cast the value to `c_uint`: `1u8 as c_uint` error[E0617]: can't pass `i16` to variadic function - --> $DIR/variadic-ffi-1.rs:30:19 + --> $DIR/variadic-ffi-1.rs:32:19 | LL | foo(1, 2, 1i16); | ^^^^ help: cast the value to `c_int`: `1i16 as c_int` error[E0617]: can't pass `u16` to variadic function - --> $DIR/variadic-ffi-1.rs:31:19 + --> $DIR/variadic-ffi-1.rs:33:19 | LL | foo(1, 2, 1u16); | ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint` diff --git a/src/test/ui/c-variadic/variadic-ffi-2.rs b/src/test/ui/c-variadic/variadic-ffi-2.rs index 224ac16f4586..96cea87546e7 100644 --- a/src/test/ui/c-variadic/variadic-ffi-2.rs +++ b/src/test/ui/c-variadic/variadic-ffi-2.rs @@ -1,7 +1,20 @@ // ignore-arm stdcall isn't supported +#![feature(extended_varargs_abi_support)] +#![feature(abi_efiapi)] fn baz(f: extern "stdcall" fn(usize, ...)) { - //~^ ERROR: variadic function must have C or cdecl calling convention + //~^ ERROR: C-variadic function must have a compatible calling convention, + // like C, cdecl, win64, sysv64 or efiapi + f(22, 44); +} + +fn sysv(f: extern "sysv64" fn(usize, ...)) { + f(22, 44); +} +fn win(f: extern "win64" fn(usize, ...)) { + f(22, 44); +} +fn efiapi(f: extern "efiapi" fn(usize, ...)) { f(22, 44); } diff --git a/src/test/ui/c-variadic/variadic-ffi-2.stderr b/src/test/ui/c-variadic/variadic-ffi-2.stderr index 4c8b8d2b2e1a..117d75301fb7 100644 --- a/src/test/ui/c-variadic/variadic-ffi-2.stderr +++ b/src/test/ui/c-variadic/variadic-ffi-2.stderr @@ -1,8 +1,8 @@ -error[E0045]: C-variadic function must have C or cdecl calling convention - --> $DIR/variadic-ffi-2.rs:3:11 +error[E0045]: C-variadic function must have a compatible calling convention, like C, cdecl, win64, sysv64 or efiapi + --> $DIR/variadic-ffi-2.rs:5:11 | LL | fn baz(f: extern "stdcall" fn(usize, ...)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0045.stderr b/src/test/ui/error-codes/E0045.stderr index d163128bc8b6..ecb916d02df5 100644 --- a/src/test/ui/error-codes/E0045.stderr +++ b/src/test/ui/error-codes/E0045.stderr @@ -1,8 +1,8 @@ -error[E0045]: C-variadic function must have C or cdecl calling convention +error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl --> $DIR/E0045.rs:1:17 | LL | extern "Rust" { fn foo(x: u8, ...); } - | ^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention + | ^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention error: aborting due to previous error From 65ef62597b82778a1cb7b295932663671a0156c8 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Tue, 16 Aug 2022 14:29:19 -0400 Subject: [PATCH 0227/1126] Apply suggestions from code review Use ticks around abis. Co-authored-by: Esteban Kuber --- compiler/rustc_hir_analysis/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 782c95d63355..46d57563a4fd 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -119,10 +119,10 @@ use bounds::Bounds; fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) { const ERROR_HEAD: &str = "C-variadic function must have a compatible calling convention"; - const CONVENTIONS_UNSTABLE: &str = "C, cdecl, win64, sysv64 or efiapi"; - const CONVENTIONS_STABLE: &str = "C or cdecl"; + const CONVENTIONS_UNSTABLE: &str = "`C`, `cdecl`, `win64`, `sysv64` or `efiapi`"; + const CONVENTIONS_STABLE: &str = "`C` or `cdecl`"; const UNSTABLE_EXPLAIN: &str = - "using different calling convention than C or cdecl for varargs functions is unstable"; + "using different calling convention than `C` or `cdecl` for varargs functions is unstable"; if !decl.c_variadic || matches!(abi, Abi::C { .. } | Abi::Cdecl { .. }) { return; From de78c32b854d6f76167bb6fcf76fc2760c8b7d2a Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sun, 23 Oct 2022 19:11:25 -0400 Subject: [PATCH 0228/1126] Cleanup message and bless tests --- compiler/rustc_hir_analysis/src/lib.rs | 2 +- .../feature-gate-extended_varargs_abi_support.rs | 12 ++++++------ .../feature-gate-extended_varargs_abi_support.stderr | 12 ++++++------ src/test/ui/c-variadic/variadic-ffi-1.stderr | 2 +- src/test/ui/c-variadic/variadic-ffi-2.stderr | 2 +- src/test/ui/error-codes/E0045.stderr | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 46d57563a4fd..3f51005a5f03 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -122,7 +122,7 @@ fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi const CONVENTIONS_UNSTABLE: &str = "`C`, `cdecl`, `win64`, `sysv64` or `efiapi`"; const CONVENTIONS_STABLE: &str = "`C` or `cdecl`"; const UNSTABLE_EXPLAIN: &str = - "using different calling convention than `C` or `cdecl` for varargs functions is unstable"; + "using calling conventions other than `C` or `cdecl` for varargs functions is unstable"; if !decl.c_variadic || matches!(abi, Abi::C { .. } | Abi::Cdecl { .. }) { return; diff --git a/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs b/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs index e391ee8a0b11..087743e505d2 100644 --- a/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs +++ b/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs @@ -1,18 +1,18 @@ #![feature(abi_efiapi)] fn efiapi(f: extern "efiapi" fn(usize, ...)) { - //~^ ERROR: C-variadic function must have a compatible calling convention, like C or cdecl - //~^^ ERROR: using different calling convention than C or cdecl for varargs functions is unstable + //~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl` + //~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable f(22, 44); } fn sysv(f: extern "sysv64" fn(usize, ...)) { - //~^ ERROR: C-variadic function must have a compatible calling convention, like C or cdecl - //~^^ ERROR: using different calling convention than C or cdecl for varargs functions is unstable + //~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl` + //~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable f(22, 44); } fn win(f: extern "win64" fn(usize, ...)) { - //~^ ERROR: C-variadic function must have a compatible calling convention, like C or cdecl - //~^^ ERROR: using different calling convention than C or cdecl for varargs functions is unstable + //~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl` + //~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable f(22, 44); } diff --git a/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr b/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr index 3442d53c1b5f..007d7d7953c9 100644 --- a/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr +++ b/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr @@ -1,4 +1,4 @@ -error[E0658]: using different calling convention than C or cdecl for varargs functions is unstable +error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14 | LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) { @@ -7,13 +7,13 @@ LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) { = note: see issue #100189 for more information = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable -error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl +error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl` --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14 | LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention -error[E0658]: using different calling convention than C or cdecl for varargs functions is unstable +error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable --> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12 | LL | fn sysv(f: extern "sysv64" fn(usize, ...)) { @@ -22,13 +22,13 @@ LL | fn sysv(f: extern "sysv64" fn(usize, ...)) { = note: see issue #100189 for more information = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable -error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl +error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl` --> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12 | LL | fn sysv(f: extern "sysv64" fn(usize, ...)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention -error[E0658]: using different calling convention than C or cdecl for varargs functions is unstable +error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable --> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11 | LL | fn win(f: extern "win64" fn(usize, ...)) { @@ -37,7 +37,7 @@ LL | fn win(f: extern "win64" fn(usize, ...)) { = note: see issue #100189 for more information = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable -error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl +error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl` --> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11 | LL | fn win(f: extern "win64" fn(usize, ...)) { diff --git a/src/test/ui/c-variadic/variadic-ffi-1.stderr b/src/test/ui/c-variadic/variadic-ffi-1.stderr index f9d6928b3df1..4beea83d8a52 100644 --- a/src/test/ui/c-variadic/variadic-ffi-1.stderr +++ b/src/test/ui/c-variadic/variadic-ffi-1.stderr @@ -1,4 +1,4 @@ -error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl +error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl` --> $DIR/variadic-ffi-1.rs:9:5 | LL | fn printf(_: *const u8, ...); diff --git a/src/test/ui/c-variadic/variadic-ffi-2.stderr b/src/test/ui/c-variadic/variadic-ffi-2.stderr index 117d75301fb7..4e74c9d92278 100644 --- a/src/test/ui/c-variadic/variadic-ffi-2.stderr +++ b/src/test/ui/c-variadic/variadic-ffi-2.stderr @@ -1,4 +1,4 @@ -error[E0045]: C-variadic function must have a compatible calling convention, like C, cdecl, win64, sysv64 or efiapi +error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `win64`, `sysv64` or `efiapi` --> $DIR/variadic-ffi-2.rs:5:11 | LL | fn baz(f: extern "stdcall" fn(usize, ...)) { diff --git a/src/test/ui/error-codes/E0045.stderr b/src/test/ui/error-codes/E0045.stderr index ecb916d02df5..fcc613b11b8d 100644 --- a/src/test/ui/error-codes/E0045.stderr +++ b/src/test/ui/error-codes/E0045.stderr @@ -1,4 +1,4 @@ -error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl +error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl` --> $DIR/E0045.rs:1:17 | LL | extern "Rust" { fn foo(x: u8, ...); } From 541128dcb374c7eaa36872eb3a9085c401db7938 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 23 Oct 2022 19:32:33 -0500 Subject: [PATCH 0229/1126] Override linker in cargotest on windows --- src/tools/cargotest/main.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/cargotest/main.rs b/src/tools/cargotest/main.rs index 61ccc51580f8..7044cb892869 100644 --- a/src/tools/cargotest/main.rs +++ b/src/tools/cargotest/main.rs @@ -206,6 +206,10 @@ fn run_cargo_test( .env("CFG_DISABLE_CROSS_TESTS", "1") // Relax #![deny(warnings)] in some crates .env("RUSTFLAGS", "--cap-lints warn") + // servo tries to use 'lld-link.exe' on windows, but we don't + // have lld on our PATH in CI. Override it to use 'link.exe' + .env("CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER", "link.exe") + .env("CARGO_TARGET_I686_PC_WINDOWS_MSVC_LINKER", "link.exe") .current_dir(crate_path) .status() .unwrap(); From f54c336c8019432803dd9a67b575053ffc03c372 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 22 Oct 2022 07:56:26 +0800 Subject: [PATCH 0230/1126] fix #103425, remove extra type error after missing semicolon error --- compiler/rustc_parse/src/parser/stmt.rs | 63 ++++++++++++++----------- src/test/ui/parser/issue-103425.rs | 15 ++++++ src/test/ui/parser/issue-103425.stderr | 29 ++++++++++++ 3 files changed, 79 insertions(+), 28 deletions(-) create mode 100644 src/test/ui/parser/issue-103425.rs create mode 100644 src/test/ui/parser/issue-103425.stderr diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index a61e77b7c3bf..12753c6785c9 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -553,39 +553,46 @@ impl<'a> Parser<'a> { match stmt.kind { // Expression without semicolon. StmtKind::Expr(ref mut expr) - if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => - { + if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => { // Just check for errors and recover; do not eat semicolon yet. - if let Err(mut e) = - self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]) - { - if let TokenKind::DocComment(..) = self.token.kind { - if let Ok(snippet) = self.span_to_snippet(self.token.span) { - let sp = self.token.span; - let marker = &snippet[..3]; - let (comment_marker, doc_comment_marker) = marker.split_at(2); + // `expect_one_of` returns PResult<'a, bool /* recovered */> + let replace_with_err = + match self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]) { + // Recover from parser, skip type error to avoid extra errors. + Ok(true) => true, + Err(mut e) => { + if let TokenKind::DocComment(..) = self.token.kind && + let Ok(snippet) = self.span_to_snippet(self.token.span) { + let sp = self.token.span; + let marker = &snippet[..3]; + let (comment_marker, doc_comment_marker) = marker.split_at(2); - e.span_suggestion( - sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), - &format!( - "add a space before `{}` to use a regular comment", - doc_comment_marker, - ), - format!("{} {}", comment_marker, doc_comment_marker), - Applicability::MaybeIncorrect, - ); + e.span_suggestion( + sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), + &format!( + "add a space before `{}` to use a regular comment", + doc_comment_marker, + ), + format!("{} {}", comment_marker, doc_comment_marker), + Applicability::MaybeIncorrect, + ); } - } - if let Err(mut e) = - self.check_mistyped_turbofish_with_multiple_type_params(e, expr) - { - if recover.no() { - return Err(e); + + if let Err(mut e) = + self.check_mistyped_turbofish_with_multiple_type_params(e, expr) + { + if recover.no() { + return Err(e); + } + e.emit(); + self.recover_stmt(); } - e.emit(); - self.recover_stmt(); + true } - // Don't complain about type errors in body tail after parse error (#57383). + _ => false + }; + if replace_with_err { + // We already emitted an error, so don't emit another type error let sp = expr.span.to(self.prev_token.span); *expr = self.mk_expr_err(sp); } diff --git a/src/test/ui/parser/issue-103425.rs b/src/test/ui/parser/issue-103425.rs new file mode 100644 index 000000000000..c2f8123ca4e6 --- /dev/null +++ b/src/test/ui/parser/issue-103425.rs @@ -0,0 +1,15 @@ +fn f() -> f32 { + 3 + //~^ ERROR expected `;` + 5.0 +} + +fn k() -> f32 { + 2_u32 + //~^ ERROR expected `;` + 3_i8 + //~^ ERROR expected `;` + 5.0 +} + +fn main() {} diff --git a/src/test/ui/parser/issue-103425.stderr b/src/test/ui/parser/issue-103425.stderr new file mode 100644 index 000000000000..0efe3e3ca711 --- /dev/null +++ b/src/test/ui/parser/issue-103425.stderr @@ -0,0 +1,29 @@ +error: expected `;`, found `5.0` + --> $DIR/issue-103425.rs:2:6 + | +LL | 3 + | ^ help: add `;` here +LL | +LL | 5.0 + | --- unexpected token + +error: expected `;`, found `3_i8` + --> $DIR/issue-103425.rs:8:10 + | +LL | 2_u32 + | ^ help: add `;` here +LL | +LL | 3_i8 + | ---- unexpected token + +error: expected `;`, found `5.0` + --> $DIR/issue-103425.rs:10:9 + | +LL | 3_i8 + | ^ help: add `;` here +LL | +LL | 5.0 + | --- unexpected token + +error: aborting due to 3 previous errors + From 56735361536773aee70f44b76438d1a07b7062e3 Mon Sep 17 00:00:00 2001 From: Pointerbender <81013316+Pointerbender@users.noreply.github.com> Date: Mon, 24 Oct 2022 04:27:37 +0200 Subject: [PATCH 0231/1126] fix typos Co-authored-by: Ralf Jung --- library/core/src/cell.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 12c6f2117259..fe5dd7be8f6b 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1827,8 +1827,8 @@ impl fmt::Display for RefMut<'_, T> { /// order to avoid its interior mutability property from spreading from `T` into the `Outer` type, /// thus this can cause distortions in the type size in these cases. /// -/// Note that it is still only valid to obtain a `*mut T` pointer to the contents of a -/// _shared_ `UnsafeCell` through [`.get()`] or [`.raw_get()`]. A `&mut T` reference +/// Note that the only valid way to obtain a `*mut T` pointer to the contents of a +/// _shared_ `UnsafeCell` is through [`.get()`] or [`.raw_get()`]. A `&mut T` reference /// can be obtained by either dereferencing this pointer or by calling [`.get_mut()`] /// on an _exclusive_ `UnsafeCell`. Even though `T` and `UnsafeCell` have the /// same memory layout, the following is not allowed and undefined behavior: From 63d1a721f187faf007e8540e4d5b6b2e494231eb Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Sun, 23 Oct 2022 02:47:20 -0700 Subject: [PATCH 0232/1126] rustdoc: don't mark Box as Iterator, Read, etc Because Box has pass-through implementations, rustdoc was giving it the "Notable Traits" treatment for Iterator, Read, Write, and Future, even when the type of T was unspecified. Pin had the same problem, but just for Future. --- src/librustdoc/html/render/mod.rs | 9 +++++ ...oc-notable_trait_box_is_not_an_iterator.rs | 38 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/test/rustdoc/doc-notable_trait_box_is_not_an_iterator.rs diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index cd56d73e7d47..b2fac7228272 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1276,6 +1276,15 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { if let Some((did, ty)) = decl.output.as_return().and_then(|t| Some((t.def_id(cx.cache())?, t))) { + // Box has pass-through impls for Read, Write, Iterator, and Future when the + // boxed type implements one of those. We don't want to treat every Box return + // as being notably an Iterator (etc), though, so we exempt it. Pin has the same + // issue, with a pass-through impl for Future. + if Some(did) == cx.tcx().lang_items().owned_box() + || Some(did) == cx.tcx().lang_items().pin_type() + { + return "".to_string(); + } if let Some(impls) = cx.cache().impls.get(&did) { for i in impls { let impl_ = i.inner_impl(); diff --git a/src/test/rustdoc/doc-notable_trait_box_is_not_an_iterator.rs b/src/test/rustdoc/doc-notable_trait_box_is_not_an_iterator.rs new file mode 100644 index 000000000000..3fb00c7db841 --- /dev/null +++ b/src/test/rustdoc/doc-notable_trait_box_is_not_an_iterator.rs @@ -0,0 +1,38 @@ +#![feature(doc_notable_trait)] +#![feature(lang_items)] +#![feature(no_core)] +#![no_core] +#[lang = "owned_box"] +pub struct Box; + +impl Box { + pub fn new(x: T) -> Box { + Box + } +} + +#[doc(notable_trait)] +pub trait FakeIterator {} + +impl FakeIterator for Box {} + +#[lang = "pin"] +pub struct Pin; + +impl Pin { + pub fn new(x: T) -> Pin { + Pin + } +} + +impl FakeIterator for Pin {} + +// @!has doc_notable_trait_box_is_not_an_iterator/fn.foo.html '//*' 'Notable' +pub fn foo(x: T) -> Box { + Box::new(x) +} + +// @!has doc_notable_trait_box_is_not_an_iterator/fn.bar.html '//*' 'Notable' +pub fn bar(x: T) -> Pin { + Pin::new(x) +} From 45a9d18969894757ab3294fd95242cc9bde4be6c Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sun, 23 Oct 2022 22:16:13 -0700 Subject: [PATCH 0233/1126] rustdoc: remove no-op CSS `.source pre.rust { white-space: pre }` This rule, added in 49e6db7f3510a99ab3d3723b2430add985629c39, overrode a rule in normalize.css. https://github.com/rust-lang/rust/blob/49e6db7f3510a99ab3d3723b2430add985629c39/src/librustdoc/html/static/normalize.css#L169-L175 When normalize.css was updated, this rule went away. https://github.com/necolas/normalize.css/commit/a8edd0c5aa06b905e8e1550fd6a5c01e46375194 --- src/librustdoc/html/static/css/rustdoc.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 293c9787609b..8a9a74a0ce39 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -538,7 +538,6 @@ ul.block, .block li { } .source .content pre.rust { - white-space: pre; overflow: auto; padding-left: 0; } From 0d0a603aaba085f31d7639dcbf6b460f7d1766cf Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 24 Oct 2022 08:05:52 +0200 Subject: [PATCH 0234/1126] point to my fork of josh for now --- src/tools/miri/CONTRIBUTING.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index c9a7d708f1e0..b1e6b9c69d39 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -279,13 +279,12 @@ With this, you should now have a working development setup! See ## Advanced topic: Syncing with the rustc repo We use the [`josh` proxy](https://github.com/josh-project/josh) to transmit -changes between the rustc and Miri repositories. For now, josh needs to be built +changes between the rustc and Miri repositories. For now, a fork of josh needs to be built from source. This downloads and runs josh: ```sh -git clone https://github.com/josh-project/josh +git clone https://github.com/RalfJung/josh cd josh -git checkout @changes/master/christian.schilling.de@gmail.com/start-filter cargo run --release -p josh-proxy -- --local=$(pwd)/local --remote=https://github.com --no-background ``` From c4bdb8e516e9f927f37a324185905fb9be387f57 Mon Sep 17 00:00:00 2001 From: feniljain Date: Fri, 7 Oct 2022 00:11:02 +0530 Subject: [PATCH 0235/1126] feat: add config for inserting must_use in `generate_enum_as_method` --- crates/ide-assists/src/assist_config.rs | 1 + .../generate_enum_projection_method.rs | 27 +++++++++++++++---- crates/ide-assists/src/tests.rs | 1 + crates/rust-analyzer/src/config.rs | 4 +++ docs/user/generated_config.adoc | 6 +++++ editors/code/package.json | 5 ++++ 6 files changed, 39 insertions(+), 5 deletions(-) diff --git a/crates/ide-assists/src/assist_config.rs b/crates/ide-assists/src/assist_config.rs index 60d1588a44e5..b273ebc85a50 100644 --- a/crates/ide-assists/src/assist_config.rs +++ b/crates/ide-assists/src/assist_config.rs @@ -14,4 +14,5 @@ pub struct AssistConfig { pub allowed: Option>, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub assist_emit_must_use: bool, } diff --git a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs index 402ab1ee79d3..732ee49f66d2 100644 --- a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs +++ b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs @@ -124,6 +124,7 @@ fn generate_enum_projection_method( happy_case, sad_case, } = props; + let variant = ctx.find_node_at_offset::()?; let variant_name = variant.name()?; let parent_enum = ast::Adt::Enum(variant.parent_enum()); @@ -144,7 +145,7 @@ fn generate_enum_projection_method( ast::StructKind::Unit => return None, }; - let fn_name = format!("{}_{}", fn_name_prefix, &to_lower_snake_case(&variant_name.text())); + let fn_name = format!("{fn_name_prefix}_{}", &to_lower_snake_case(&variant_name.text())); // Return early if we've found an existing new fn let impl_def = find_struct_impl(ctx, &parent_enum, &fn_name)?; @@ -156,15 +157,31 @@ fn generate_enum_projection_method( assist_description, target, |builder| { - let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{v} ")); - let method = format!( - " {vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{ + let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); + + let field_type_syntax = field_type.syntax(); + + let method = if ctx.config.assist_emit_must_use + { + format!( + " #[must_use] + {vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type_syntax}{return_suffix} {{ if let Self::{variant_name}{pattern_suffix} = self {{ {happy_case}({bound_name}) }} else {{ {sad_case} }} - }}"); + }}") + } else { + format!( + " {vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type_syntax}{return_suffix} {{ + if let Self::{variant_name}{pattern_suffix} = self {{ + {happy_case}({bound_name}) + }} else {{ + {sad_case} + }} + }}") + }; add_method_to_adt(builder, &parent_enum, impl_def, &method); }, diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index f7f2417d0745..92ced27c78ae 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs @@ -30,6 +30,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { skip_glob_imports: true, }, prefer_no_std: false, + assist_emit_must_use: false, }; pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 577a8640a4c0..21d7538fdc1e 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -56,6 +56,9 @@ mod patch_old_style; // parsing the old name. config_data! { struct ConfigData { + /// Whether to insert must_use derive macro while generating `as_` methods + /// for enum variants. + assist_emitMustUse: bool = "false", /// Placeholder expression to use for missing expressions in assists. assist_expressionFillDefault: ExprFillDefaultDef = "\"todo\"", @@ -1227,6 +1230,7 @@ impl Config { allowed: None, insert_use: self.insert_use_config(), prefer_no_std: self.data.imports_prefer_no_std, + assist_emit_must_use: self.data.assist_emitMustUse, } } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index acf0aaea859a..82ec1d56f2b2 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -1,3 +1,9 @@ +[[rust-analyzer.assist.emitMustUse]]rust-analyzer.assist.emitMustUse (default: `false`):: ++ +-- +Whether to insert must_use derive macro while generating `as_` methods +for enum variants. +-- [[rust-analyzer.assist.expressionFillDefault]]rust-analyzer.assist.expressionFillDefault (default: `"todo"`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index 1afe2087c71a..1446a6037721 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -397,6 +397,11 @@ "type": "boolean" }, "$generated-start": {}, + "rust-analyzer.assist.emitMustUse": { + "markdownDescription": "Whether to insert must_use derive macro while generating `as_` methods\nfor enum variants.", + "default": false, + "type": "boolean" + }, "rust-analyzer.assist.expressionFillDefault": { "markdownDescription": "Placeholder expression to use for missing expressions in assists.", "default": "todo", From e3a091ad6ab9cdc8b25da1bc52093023bc90e232 Mon Sep 17 00:00:00 2001 From: Jesse Ruderman Date: Mon, 24 Oct 2022 00:52:26 -0700 Subject: [PATCH 0236/1126] Remove redundant sentence --- compiler/rustc_error_codes/src/error_codes/E0210.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0210.md b/compiler/rustc_error_codes/src/error_codes/E0210.md index dc2fd9b0ca04..41263e5e3f5a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0210.md +++ b/compiler/rustc_error_codes/src/error_codes/E0210.md @@ -76,7 +76,5 @@ Let `Ti` be the first such type. For information on the design of the orphan rules, see [RFC 2451] and [RFC 1023]. -For information on the design of the orphan rules, see [RFC 1023]. - [RFC 2451]: https://rust-lang.github.io/rfcs/2451-re-rebalancing-coherence.html [RFC 1023]: https://github.com/rust-lang/rfcs/blob/master/text/1023-rebalancing-coherence.md From c0447b489ba890c740c1e5c41f3eacca148734a9 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 24 Oct 2022 16:48:28 +0800 Subject: [PATCH 0237/1126] fix #103435, unused lint won't produce invalid code --- compiler/rustc_lint/src/unused.rs | 44 +++++++++++++------ .../lint/issue-103435-extra-parentheses.fixed | 16 +++++++ .../ui/lint/issue-103435-extra-parentheses.rs | 16 +++++++ .../issue-103435-extra-parentheses.stderr | 43 ++++++++++++++++++ 4 files changed, 106 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/lint/issue-103435-extra-parentheses.fixed create mode 100644 src/test/ui/lint/issue-103435-extra-parentheses.rs create mode 100644 src/test/ui/lint/issue-103435-extra-parentheses.stderr diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 46706e498445..1299444cd773 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -565,10 +565,26 @@ trait UnusedDelimLint { lint.set_arg("delim", Self::DELIM_STR); lint.set_arg("item", msg); if let Some((lo, hi)) = spans { - let replacement = vec![ - (lo, if keep_space.0 { " ".into() } else { "".into() }), - (hi, if keep_space.1 { " ".into() } else { "".into() }), - ]; + let sm = cx.sess().source_map(); + let lo_replace = + if keep_space.0 && + let Ok(snip) = sm.span_to_snippet(lo.with_lo(lo.lo() - BytePos(1))) && + !snip.starts_with(" ") { + " ".to_string() + } else { + "".to_string() + }; + + let hi_replace = + if keep_space.1 && + let Ok(snip) = sm.span_to_snippet(sm.next_point(hi)) && + !snip.starts_with(" ") { + " ".to_string() + } else { + "".to_string() + }; + + let replacement = vec![(lo, lo_replace), (hi, hi_replace)]; lint.multipart_suggestion( fluent::suggestion, replacement, @@ -765,6 +781,7 @@ impl UnusedParens { value: &ast::Pat, avoid_or: bool, avoid_mut: bool, + keep_space: (bool, bool), ) { use ast::{BindingAnnotation, PatKind}; @@ -789,7 +806,7 @@ impl UnusedParens { } else { None }; - self.emit_unused_delims(cx, value.span, spans, "pattern", (false, false)); + self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space); } } } @@ -798,7 +815,7 @@ impl EarlyLintPass for UnusedParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { match e.kind { ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => { - self.check_unused_parens_pat(cx, pat, false, false); + self.check_unused_parens_pat(cx, pat, false, false, (true, true)); } // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already // handle a hard error for them during AST lowering in `lower_expr_mut`, but we still @@ -842,6 +859,7 @@ impl EarlyLintPass for UnusedParens { fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) { use ast::{Mutability, PatKind::*}; + let keep_space = (false, false); match &p.kind { // Do not lint on `(..)` as that will result in the other arms being useless. Paren(_) @@ -849,33 +867,33 @@ impl EarlyLintPass for UnusedParens { | Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {}, // These are list-like patterns; parens can always be removed. TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { - self.check_unused_parens_pat(cx, p, false, false); + self.check_unused_parens_pat(cx, p, false, false, keep_space); }, Struct(_, _, fps, _) => for f in fps { - self.check_unused_parens_pat(cx, &f.pat, false, false); + self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. - Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false), + Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. - Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not), + Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), } } fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { if let StmtKind::Local(ref local) = s.kind { - self.check_unused_parens_pat(cx, &local.pat, true, false); + self.check_unused_parens_pat(cx, &local.pat, true, false, (false, false)); } ::check_stmt(self, cx, s) } fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) { - self.check_unused_parens_pat(cx, ¶m.pat, true, false); + self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false)); } fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) { - self.check_unused_parens_pat(cx, &arm.pat, false, false); + self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false)); } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { diff --git a/src/test/ui/lint/issue-103435-extra-parentheses.fixed b/src/test/ui/lint/issue-103435-extra-parentheses.fixed new file mode 100644 index 000000000000..dbbcaa441ddd --- /dev/null +++ b/src/test/ui/lint/issue-103435-extra-parentheses.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![deny(unused_parens)] + +fn main() { + if let Some(_) = Some(1) {} + //~^ ERROR unnecessary parentheses around pattern + + for _x in 1..10 {} + //~^ ERROR unnecessary parentheses around pattern + + if 2 == 1 {} + //~^ ERROR unnecessary parentheses around `if` condition + + // FIXME, auto recover from this one? + // for(_x in 1..10) {} +} diff --git a/src/test/ui/lint/issue-103435-extra-parentheses.rs b/src/test/ui/lint/issue-103435-extra-parentheses.rs new file mode 100644 index 000000000000..f5c2a6664ede --- /dev/null +++ b/src/test/ui/lint/issue-103435-extra-parentheses.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![deny(unused_parens)] + +fn main() { + if let(Some(_))= Some(1) {} + //~^ ERROR unnecessary parentheses around pattern + + for(_x)in 1..10 {} + //~^ ERROR unnecessary parentheses around pattern + + if(2 == 1){} + //~^ ERROR unnecessary parentheses around `if` condition + + // FIXME, auto recover from this one? + // for(_x in 1..10) {} +} diff --git a/src/test/ui/lint/issue-103435-extra-parentheses.stderr b/src/test/ui/lint/issue-103435-extra-parentheses.stderr new file mode 100644 index 000000000000..a3f2fbc51ab2 --- /dev/null +++ b/src/test/ui/lint/issue-103435-extra-parentheses.stderr @@ -0,0 +1,43 @@ +error: unnecessary parentheses around pattern + --> $DIR/issue-103435-extra-parentheses.rs:5:11 + | +LL | if let(Some(_))= Some(1) {} + | ^ ^ + | +note: the lint level is defined here + --> $DIR/issue-103435-extra-parentheses.rs:2:9 + | +LL | #![deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - if let(Some(_))= Some(1) {} +LL + if let Some(_) = Some(1) {} + | + +error: unnecessary parentheses around pattern + --> $DIR/issue-103435-extra-parentheses.rs:8:8 + | +LL | for(_x)in 1..10 {} + | ^ ^ + | +help: remove these parentheses + | +LL - for(_x)in 1..10 {} +LL + for _x in 1..10 {} + | + +error: unnecessary parentheses around `if` condition + --> $DIR/issue-103435-extra-parentheses.rs:11:7 + | +LL | if(2 == 1){} + | ^ ^ + | +help: remove these parentheses + | +LL - if(2 == 1){} +LL + if 2 == 1 {} + | + +error: aborting due to 3 previous errors + From 113e8dfb7293cc070214b42541781b2eeac25ae1 Mon Sep 17 00:00:00 2001 From: Charles Lew Date: Sat, 22 Oct 2022 18:48:20 +0800 Subject: [PATCH 0238/1126] Port `dead_code` lints to be translatable. --- .../locales/en-US/passes.ftl | 34 ++++ compiler/rustc_errors/src/diagnostic_impls.rs | 32 ++++ compiler/rustc_errors/src/lib.rs | 2 +- compiler/rustc_passes/src/dead.rs | 175 +++++++++--------- compiler/rustc_passes/src/errors.rs | 80 +++++++- ...lone-debug-dead-code-in-the-same-struct.rs | 2 +- ...-debug-dead-code-in-the-same-struct.stderr | 2 +- .../multiple-dead-codes-in-the-same-struct.rs | 2 +- ...tiple-dead-codes-in-the-same-struct.stderr | 2 +- .../ui/lint/dead-code/tuple-struct-field.rs | 2 +- .../lint/dead-code/tuple-struct-field.stderr | 2 +- 11 files changed, 236 insertions(+), 99 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl index 4bc6bd9fb220..a5b002fa3579 100644 --- a/compiler/rustc_error_messages/locales/en-US/passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -671,3 +671,37 @@ passes_missing_const_err = attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` .help = make the function or method const .label = attribute specified here + +passes_dead_codes = + { $multiple -> + *[true] multiple {$descr}s are + [false] { $num -> + [one] {$descr} {$name_list} is + *[other] {$descr}s {$name_list} are + } + } never {$participle} + +passes_change_fields_to_be_of_unit_type = + consider changing the { $num -> + [one] field + *[other] fields + } to be of unit type to suppress this warning + while preserving the field numbering, or remove the { $num -> + [one] field + *[other] fields + } + +passes_parent_info = + {$num -> + [one] {$descr} + *[other] {$descr}s + } in this {$parent_descr} + +passes_ignored_derived_impls = + `{$name}` has {$trait_list_len -> + [one] a derived impl + *[other] derived impls + } for the {$trait_list_len -> + [one] trait {$trait_list}, but this is + *[other] traits {$trait_list}, but these are + } intentionally ignored during dead code analysis diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 7640b2919f78..f6fe9192b45c 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -11,6 +11,7 @@ use rustc_target::abi::TargetDataLayoutErrors; use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple}; use std::borrow::Cow; use std::fmt; +use std::fmt::Write; use std::num::ParseIntError; use std::path::{Path, PathBuf}; @@ -170,6 +171,37 @@ impl IntoDiagnosticArg for Level { } } +#[derive(Clone)] +pub struct DiagnosticSymbolList(Vec); + +impl From> for DiagnosticSymbolList { + fn from(v: Vec) -> Self { + DiagnosticSymbolList(v) + } +} + +impl IntoDiagnosticArg for DiagnosticSymbolList { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + // FIXME: replace the logic here with a real list formatter + let symbols = match &self.0[..] { + [symbol] => format!("`{symbol}`"), + [symbol, last] => { + format!("`{symbol}` and `{last}`",) + } + [symbols @ .., last] => { + let mut result = String::new(); + for symbol in symbols { + write!(result, "`{symbol}`, ").unwrap(); + } + write!(result, "and `{last}`").unwrap(); + result + } + [] => unreachable!(), + }; + DiagnosticArgValue::Str(Cow::Owned(symbols)) + } +} + impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> { fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> { let mut diag; diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 0963ea71f802..2c8a70981bc7 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -376,7 +376,7 @@ pub use diagnostic::{ DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, }; pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted}; -pub use diagnostic_impls::DiagnosticArgFromDisplay; +pub use diagnostic_impls::{DiagnosticArgFromDisplay, DiagnosticSymbolList}; use std::backtrace::Backtrace; /// A handler deals with errors and other compiler output. diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 6a97ad3fe86e..9157c8279a5c 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -4,7 +4,7 @@ use itertools::Itertools; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{pluralize, Applicability, MultiSpan}; +use rustc_errors::MultiSpan; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -18,7 +18,10 @@ use rustc_session::lint; use rustc_span::symbol::{sym, Symbol}; use std::mem; -use crate::errors::UselessAssignment; +use crate::errors::{ + ChangeFieldsToBeOfUnitType, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, + UselessAssignment, +}; // Any local node that may call something in its body block should be // explored. For example, if it's a live Node::Item that is a @@ -698,99 +701,89 @@ impl<'tcx> DeadVisitor<'tcx> { parent_item: Option, is_positional: bool, ) { - if let Some(&first_id) = dead_codes.first() { - let tcx = self.tcx; - let names: Vec<_> = dead_codes - .iter() - .map(|&def_id| tcx.item_name(def_id.to_def_id()).to_string()) - .collect(); - let spans: Vec<_> = dead_codes - .iter() - .map(|&def_id| match tcx.def_ident_span(def_id) { - Some(s) => s.with_ctxt(tcx.def_span(def_id).ctxt()), - None => tcx.def_span(def_id), + let Some(&first_id) = dead_codes.first() else { + return; + }; + let tcx = self.tcx; + let names: Vec<_> = + dead_codes.iter().map(|&def_id| tcx.item_name(def_id.to_def_id())).collect(); + let spans: Vec<_> = dead_codes + .iter() + .map(|&def_id| match tcx.def_ident_span(def_id) { + Some(s) => s.with_ctxt(tcx.def_span(def_id).ctxt()), + None => tcx.def_span(def_id), + }) + .collect(); + + let descr = tcx.def_kind(first_id).descr(first_id.to_def_id()); + let num = dead_codes.len(); + let multiple = num > 6; + let name_list = names.into(); + + let lint = if is_positional { + lint::builtin::UNUSED_TUPLE_STRUCT_FIELDS + } else { + lint::builtin::DEAD_CODE + }; + + let parent_info = if let Some(parent_item) = parent_item { + let parent_descr = tcx.def_kind(parent_item).descr(parent_item.to_def_id()); + Some(ParentInfo { + num, + descr, + parent_descr, + span: tcx.def_ident_span(parent_item).unwrap(), + }) + } else { + None + }; + + let encl_def_id = parent_item.unwrap_or(first_id); + let ignored_derived_impls = + if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) { + let trait_list = ign_traits + .iter() + .map(|(trait_id, _)| self.tcx.item_name(*trait_id)) + .collect::>(); + let trait_list_len = trait_list.len(); + Some(IgnoredDerivedImpls { + name: self.tcx.item_name(encl_def_id.to_def_id()), + trait_list: trait_list.into(), + trait_list_len, }) - .collect(); - - let descr = tcx.def_kind(first_id).descr(first_id.to_def_id()); - let span_len = dead_codes.len(); - let names = match &names[..] { - _ if span_len > 6 => String::new(), - [name] => format!("`{name}` "), - [names @ .., last] => { - format!( - "{} and `{last}` ", - names.iter().map(|name| format!("`{name}`")).join(", ") - ) - } - [] => unreachable!(), + } else { + None }; - let msg = format!( - "{these}{descr}{s} {names}{are} never {participle}", - these = if span_len > 6 { "multiple " } else { "" }, - s = pluralize!(span_len), - are = pluralize!("is", span_len), - ); - tcx.struct_span_lint_hir( - if is_positional { - lint::builtin::UNUSED_TUPLE_STRUCT_FIELDS - } else { - lint::builtin::DEAD_CODE - }, - tcx.hir().local_def_id_to_hir_id(first_id), - MultiSpan::from_spans(spans.clone()), - msg, - |err| { - if is_positional { - err.multipart_suggestion( - &format!( - "consider changing the field{s} to be of unit type to \ - suppress this warning while preserving the field \ - numbering, or remove the field{s}", - s = pluralize!(span_len) - ), - spans.iter().map(|sp| (*sp, "()".to_string())).collect(), - // "HasPlaceholders" because applying this fix by itself isn't - // enough: All constructor calls have to be adjusted as well - Applicability::HasPlaceholders, - ); - } + let diag = if is_positional { + MultipleDeadCodes::UnusedTupleStructFields { + multiple, + num, + descr, + participle, + name_list, + change_fields_suggestion: ChangeFieldsToBeOfUnitType { num, spans: spans.clone() }, + parent_info, + ignored_derived_impls, + } + } else { + MultipleDeadCodes::DeadCodes { + multiple, + num, + descr, + participle, + name_list, + parent_info, + ignored_derived_impls, + } + }; - if let Some(parent_item) = parent_item { - let parent_descr = tcx.def_kind(parent_item).descr(parent_item.to_def_id()); - err.span_label( - tcx.def_ident_span(parent_item).unwrap(), - format!("{descr}{s} in this {parent_descr}", s = pluralize!(span_len)), - ); - } - - let encl_def_id = parent_item.unwrap_or(first_id); - if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) { - let traits_str = ign_traits - .iter() - .map(|(trait_id, _)| format!("`{}`", self.tcx.item_name(*trait_id))) - .collect::>() - .join(" and "); - let plural_s = pluralize!(ign_traits.len()); - let article = if ign_traits.len() > 1 { "" } else { "a " }; - let is_are = if ign_traits.len() > 1 { "these are" } else { "this is" }; - let msg = format!( - "`{}` has {}derived impl{} for the trait{} {}, but {} \ - intentionally ignored during dead code analysis", - self.tcx.item_name(encl_def_id.to_def_id()), - article, - plural_s, - plural_s, - traits_str, - is_are - ); - err.note(&msg); - } - err - }, - ); - } + self.tcx.emit_spanned_lint( + lint, + tcx.hir().local_def_id_to_hir_id(first_id), + MultiSpan::from_spans(spans.clone()), + diag, + ); } fn warn_dead_fields_and_variants( diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index adaaf5392425..d39d7629b287 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -4,12 +4,16 @@ use std::{ }; use rustc_ast::Label; -use rustc_errors::{error_code, Applicability, ErrorGuaranteed, IntoDiagnostic, MultiSpan}; +use rustc_errors::{ + error_code, Applicability, DiagnosticSymbolList, ErrorGuaranteed, IntoDiagnostic, MultiSpan, +}; use rustc_hir::{self as hir, ExprKind, Target}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{MainDefinition, Ty}; use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_errors::{pluralize, AddToDiagnostic, Diagnostic, SubdiagnosticMessage}; + use crate::lang_items::Duplicate; #[derive(LintDiagnostic)] @@ -1449,3 +1453,77 @@ pub struct MissingConstErr { #[label] pub const_span: Span, } + +#[derive(LintDiagnostic)] +pub enum MultipleDeadCodes<'tcx> { + #[diag(passes_dead_codes)] + DeadCodes { + multiple: bool, + num: usize, + descr: &'tcx str, + participle: &'tcx str, + name_list: DiagnosticSymbolList, + #[subdiagnostic] + parent_info: Option>, + #[subdiagnostic] + ignored_derived_impls: Option, + }, + #[diag(passes_dead_codes)] + UnusedTupleStructFields { + multiple: bool, + num: usize, + descr: &'tcx str, + participle: &'tcx str, + name_list: DiagnosticSymbolList, + #[subdiagnostic] + change_fields_suggestion: ChangeFieldsToBeOfUnitType, + #[subdiagnostic] + parent_info: Option>, + #[subdiagnostic] + ignored_derived_impls: Option, + }, +} + +#[derive(Subdiagnostic)] +#[label(passes_parent_info)] +pub struct ParentInfo<'tcx> { + pub num: usize, + pub descr: &'tcx str, + pub parent_descr: &'tcx str, + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +#[note(passes_ignored_derived_impls)] +pub struct IgnoredDerivedImpls { + pub name: Symbol, + pub trait_list: DiagnosticSymbolList, + pub trait_list_len: usize, +} + +pub struct ChangeFieldsToBeOfUnitType { + pub num: usize, + pub spans: Vec, +} + +// FIXME: Replace this impl with a derive. +impl AddToDiagnostic for ChangeFieldsToBeOfUnitType { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + diag.multipart_suggestion( + &format!( + "consider changing the field{s} to be of unit type to \ + suppress this warning while preserving the field \ + numbering, or remove the field{s}", + s = pluralize!(self.num) + ), + self.spans.iter().map(|sp| (*sp, "()".to_string())).collect(), + // "HasPlaceholders" because applying this fix by itself isn't + // enough: All constructor calls have to be adjusted as well + Applicability::HasPlaceholders, + ); + } +} diff --git a/src/test/ui/derives/clone-debug-dead-code-in-the-same-struct.rs b/src/test/ui/derives/clone-debug-dead-code-in-the-same-struct.rs index 15d06817577e..6ab1fb7b039b 100644 --- a/src/test/ui/derives/clone-debug-dead-code-in-the-same-struct.rs +++ b/src/test/ui/derives/clone-debug-dead-code-in-the-same-struct.rs @@ -3,7 +3,7 @@ #[derive(Debug)] pub struct Whatever { pub field0: (), - field1: (), //~ ERROR fields `field1`, `field2`, `field3` and `field4` are never read + field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read field2: (), field3: (), field4: (), diff --git a/src/test/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr b/src/test/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr index 512b870fa4b6..7f4f78cebc91 100644 --- a/src/test/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr +++ b/src/test/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr @@ -1,4 +1,4 @@ -error: fields `field1`, `field2`, `field3` and `field4` are never read +error: fields `field1`, `field2`, `field3`, and `field4` are never read --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5 | LL | pub struct Whatever { diff --git a/src/test/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.rs b/src/test/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.rs index e3935cf9149b..2003e1e293a5 100644 --- a/src/test/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.rs +++ b/src/test/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.rs @@ -7,7 +7,7 @@ struct Bar { b: usize, //~ ERROR field `b` is never read #[deny(dead_code)] c: usize, //~ ERROR fields `c` and `e` are never read - d: usize, //~ WARN fields `d`, `f` and `g` are never read + d: usize, //~ WARN fields `d`, `f`, and `g` are never read #[deny(dead_code)] e: usize, f: usize, diff --git a/src/test/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr b/src/test/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr index c0f1ed38f6de..0e5c78a71679 100644 --- a/src/test/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr +++ b/src/test/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr @@ -1,4 +1,4 @@ -warning: fields `d`, `f` and `g` are never read +warning: fields `d`, `f`, and `g` are never read --> $DIR/multiple-dead-codes-in-the-same-struct.rs:10:5 | LL | struct Bar { diff --git a/src/test/ui/lint/dead-code/tuple-struct-field.rs b/src/test/ui/lint/dead-code/tuple-struct-field.rs index b15d70636863..14fb30be949d 100644 --- a/src/test/ui/lint/dead-code/tuple-struct-field.rs +++ b/src/test/ui/lint/dead-code/tuple-struct-field.rs @@ -11,7 +11,7 @@ struct SingleUnused(i32, [u8; LEN], String); //~| HELP: consider changing the field to be of unit type struct MultipleUnused(i32, f32, String, u8); -//~^ ERROR: fields `0`, `1`, `2` and `3` are never read +//~^ ERROR: fields `0`, `1`, `2`, and `3` are never read //~| NOTE: fields in this struct //~| HELP: consider changing the fields to be of unit type diff --git a/src/test/ui/lint/dead-code/tuple-struct-field.stderr b/src/test/ui/lint/dead-code/tuple-struct-field.stderr index ca0989f5b987..b8ad5cbe4e97 100644 --- a/src/test/ui/lint/dead-code/tuple-struct-field.stderr +++ b/src/test/ui/lint/dead-code/tuple-struct-field.stderr @@ -16,7 +16,7 @@ help: consider changing the field to be of unit type to suppress this warning wh LL | struct SingleUnused(i32, (), String); | ~~ -error: fields `0`, `1`, `2` and `3` are never read +error: fields `0`, `1`, `2`, and `3` are never read --> $DIR/tuple-struct-field.rs:13:23 | LL | struct MultipleUnused(i32, f32, String, u8); From 0fca075ce8a2247b6d59cd8eaf2fd1d6b89855d8 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Wed, 12 Oct 2022 17:07:30 +0800 Subject: [PATCH 0239/1126] suggest type annotation for local statement initialed by ref expression --- compiler/rustc_hir_typeck/src/demand.rs | 19 ++++++- .../src/fn_ctxt/suggestions.rs | 47 ++++++++++++++- src/test/ui/suggestions/format-borrow.stderr | 16 ++++++ src/test/ui/suggestions/issue-102892.rs | 25 ++++++++ src/test/ui/suggestions/issue-102892.stderr | 57 +++++++++++++++++++ 5 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/suggestions/issue-102892.rs create mode 100644 src/test/ui/suggestions/issue-102892.stderr diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 2974ac97f236..be14234afe28 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -714,7 +714,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, - ) -> Option<(Span, String, String, Applicability, bool /* verbose */)> { + ) -> Option<( + Span, + String, + String, + Applicability, + bool, /* verbose */ + bool, /* suggest `&` or `&mut` type annotation */ + )> { let sess = self.sess(); let sp = expr.span; @@ -746,6 +753,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + false, )); } } @@ -760,6 +768,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "b".to_string(), Applicability::MachineApplicable, true, + false, )); } } @@ -817,6 +826,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg.2, Applicability::MachineApplicable, false, + false, )); } @@ -844,6 +854,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("{prefix}&mut {sugg_expr}"), Applicability::MachineApplicable, false, + false, ), hir::Mutability::Not => ( sp, @@ -851,6 +862,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("{prefix}&{sugg_expr}"), Applicability::MachineApplicable, false, + false, ), }); } @@ -880,6 +892,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + true )); } return None; @@ -893,6 +906,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + true, )); } } @@ -959,6 +973,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { src, applicability, true, + false, )); } } @@ -999,6 +1014,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable }, true, + false, )); } @@ -1050,6 +1066,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggestion, Applicability::MachineApplicable, true, + false, )); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index cd2e41aff0f1..4db9c56f98fe 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -327,7 +327,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) -> bool { let expr = expr.peel_blocks(); - if let Some((sp, msg, suggestion, applicability, verbose)) = + if let Some((sp, msg, suggestion, applicability, verbose, annotation)) = self.check_ref(expr, found, expected) { if verbose { @@ -335,9 +335,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.span_suggestion(sp, &msg, suggestion, applicability); } + if annotation { + let suggest_annotation = match expr.peel_drop_temps().kind { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, _) => "&", + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) => "&mut ", + _ => return true, + }; + let mut tuple_indexes = Vec::new(); + let mut expr_id = expr.hir_id; + for (parent_id, node) in self.tcx.hir().parent_iter(expr.hir_id) { + match node { + Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => { + tuple_indexes.push( + subs.iter() + .enumerate() + .find(|(_, sub_expr)| sub_expr.hir_id == expr_id) + .unwrap() + .0, + ); + expr_id = parent_id; + } + Node::Local(local) => { + if let Some(mut ty) = local.ty { + while let Some(index) = tuple_indexes.pop() { + match ty.kind { + TyKind::Tup(tys) => ty = &tys[index], + _ => return true, + } + } + let annotation_span = ty.span; + err.span_suggestion( + annotation_span.with_hi(annotation_span.lo()), + format!("alternatively, consider changing the type annotation"), + suggest_annotation, + Applicability::MaybeIncorrect, + ); + } + break; + } + _ => break, + } + } + } return true; - } else if self.suggest_else_fn_with_closure(err, expr, found, expected) - { + } else if self.suggest_else_fn_with_closure(err, expr, found, expected) { return true; } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) && let ty::FnDef(def_id, ..) = &found.kind() diff --git a/src/test/ui/suggestions/format-borrow.stderr b/src/test/ui/suggestions/format-borrow.stderr index fac6a5a5f48c..8ed2b9c9a633 100644 --- a/src/test/ui/suggestions/format-borrow.stderr +++ b/src/test/ui/suggestions/format-borrow.stderr @@ -11,6 +11,10 @@ help: consider removing the borrow LL - let a: String = &String::from("a"); LL + let a: String = String::from("a"); | +help: alternatively, consider changing the type annotation + | +LL | let a: &String = &String::from("a"); + | + error[E0308]: mismatched types --> $DIR/format-borrow.rs:4:21 @@ -25,6 +29,10 @@ help: consider removing the borrow LL - let b: String = &format!("b"); LL + let b: String = format!("b"); | +help: alternatively, consider changing the type annotation + | +LL | let b: &String = &format!("b"); + | + error[E0308]: mismatched types --> $DIR/format-borrow.rs:6:21 @@ -39,6 +47,10 @@ help: consider removing the borrow LL - let c: String = &mut format!("c"); LL + let c: String = format!("c"); | +help: alternatively, consider changing the type annotation + | +LL | let c: &mut String = &mut format!("c"); + | ++++ error[E0308]: mismatched types --> $DIR/format-borrow.rs:8:21 @@ -53,6 +65,10 @@ help: consider removing the borrow LL - let d: String = &mut (format!("d")); LL + let d: String = format!("d")); | +help: alternatively, consider changing the type annotation + | +LL | let d: &mut String = &mut (format!("d")); + | ++++ error: aborting due to 4 previous errors diff --git a/src/test/ui/suggestions/issue-102892.rs b/src/test/ui/suggestions/issue-102892.rs new file mode 100644 index 000000000000..c1a791d8d857 --- /dev/null +++ b/src/test/ui/suggestions/issue-102892.rs @@ -0,0 +1,25 @@ +#![allow(dead_code, unused_variables)] + +use std::sync::Arc; + +#[derive(Debug)] +struct A; +#[derive(Debug)] +struct B; + +fn process_without_annot(arc: &Arc<(A, B)>) { + let (a, b) = **arc; // suggests putting `&**arc` here; with that, fixed! +} + +fn process_with_annot(arc: &Arc<(A, B)>) { + let (a, b): (A, B) = &**arc; // suggests putting `&**arc` here too + //~^ ERROR mismatched types +} + +fn process_with_tuple_annot(mutation: &mut (A, B), arc: &Arc<(A, B)>) { + let (a, b): ((A, B), A) = (&mut *mutation, &(**arc).0); // suggests putting `&**arc` here too + //~^ ERROR mismatched types + //~| ERROR mismatched types +} + +fn main() {} diff --git a/src/test/ui/suggestions/issue-102892.stderr b/src/test/ui/suggestions/issue-102892.stderr new file mode 100644 index 000000000000..a3dbc7cb861f --- /dev/null +++ b/src/test/ui/suggestions/issue-102892.stderr @@ -0,0 +1,57 @@ +error[E0308]: mismatched types + --> $DIR/issue-102892.rs:15:26 + | +LL | let (a, b): (A, B) = &**arc; // suggests putting `&**arc` here too + | ------ ^^^^^^ expected tuple, found `&(A, B)` + | | + | expected due to this + | + = note: expected tuple `(A, B)` + found reference `&(A, B)` +help: consider removing the borrow + | +LL - let (a, b): (A, B) = &**arc; // suggests putting `&**arc` here too +LL + let (a, b): (A, B) = **arc; // suggests putting `&**arc` here too + | +help: alternatively, consider changing the type annotation + | +LL | let (a, b): &(A, B) = &**arc; // suggests putting `&**arc` here too + | + + +error[E0308]: mismatched types + --> $DIR/issue-102892.rs:20:32 + | +LL | let (a, b): ((A, B), A) = (&mut *mutation, &(**arc).0); // suggests putting `&**arc` here too + | ^^^^^^^^^^^^^^ expected tuple, found `&mut (A, B)` + | + = note: expected tuple `(A, B)` + found mutable reference `&mut (A, B)` +help: consider removing the borrow + | +LL - let (a, b): ((A, B), A) = (&mut *mutation, &(**arc).0); // suggests putting `&**arc` here too +LL + let (a, b): ((A, B), A) = (*mutation, &(**arc).0); // suggests putting `&**arc` here too + | +help: alternatively, consider changing the type annotation + | +LL | let (a, b): (&mut (A, B), A) = (&mut *mutation, &(**arc).0); // suggests putting `&**arc` here too + | ++++ + +error[E0308]: mismatched types + --> $DIR/issue-102892.rs:20:48 + | +LL | let (a, b): ((A, B), A) = (&mut *mutation, &(**arc).0); // suggests putting `&**arc` here too + | ^^^^^^^^^^ expected struct `A`, found `&A` + | +help: consider removing the borrow + | +LL - let (a, b): ((A, B), A) = (&mut *mutation, &(**arc).0); // suggests putting `&**arc` here too +LL + let (a, b): ((A, B), A) = (&mut *mutation, (**arc).0); // suggests putting `&**arc` here too + | +help: alternatively, consider changing the type annotation + | +LL | let (a, b): ((A, B), &A) = (&mut *mutation, &(**arc).0); // suggests putting `&**arc` here too + | + + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. From 64f56d238cc49aff79f12c31a08d22316f43d532 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Oct 2022 10:45:25 +0200 Subject: [PATCH 0240/1126] update lockfile --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index d5acab8139c9..03d450ab417f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2262,6 +2262,7 @@ dependencies = [ "rand 0.8.5", "regex", "rustc-workspace-hack", + "rustc_version", "shell-escape", "smallvec", "ui_test", From a61737ed6e169c64c851dd9a6f9bbcf5d78f70b4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Oct 2022 11:18:23 +0200 Subject: [PATCH 0241/1126] add support for testing Miri on other targets, and do some cross-testing on CI --- src/bootstrap/test.rs | 16 +++++++++++++--- .../host-x86_64/x86_64-gnu-tools/checktools.sh | 4 ++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 8d4914097787..7f63226a0612 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -461,24 +461,30 @@ impl Step for RustDemangler { pub struct Miri { stage: u32, host: TargetSelection, + target: TargetSelection, } impl Step for Miri { type Output = (); - const ONLY_HOSTS: bool = true; + const ONLY_HOSTS: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/miri") } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { stage: run.builder.top_stage, host: run.target }); + run.builder.ensure(Miri { + stage: run.builder.top_stage, + host: run.build_triple(), + target: run.target, + }); } /// Runs `cargo test` for miri. fn run(self, builder: &Builder<'_>) { let stage = self.stage; let host = self.host; + let target = self.target; let compiler = builder.compiler(stage, host); // We need the stdlib for the *next* stage, as it was built with this compiler that also built Miri. // Except if we are at stage 2, the bootstrap loop is complete and we can stick with our current stage. @@ -495,7 +501,7 @@ impl Step for Miri { builder.ensure(compile::Std::new(compiler_std, host)); let sysroot = builder.sysroot(compiler_std); - // # Run `cargo miri setup`. + // # Run `cargo miri setup` for the given target. let mut cargo = tool::prepare_tool_cargo( builder, compiler, @@ -508,6 +514,7 @@ impl Step for Miri { ); cargo.add_rustc_lib_path(builder, compiler); cargo.arg("--").arg("miri").arg("setup"); + cargo.arg("--target").arg(target.rustc_target_arg()); // Tell `cargo miri setup` where to find the sources. cargo.env("MIRI_LIB_SRC", builder.src.join("library")); @@ -565,6 +572,9 @@ impl Step for Miri { cargo.env("MIRI_BLESS", "Gesundheit"); } + // Set the target. + cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); + // Forward test filters. cargo.arg("--").args(builder.config.cmd.test_args()); let mut cargo = Command::from(cargo); diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index 3e1f39eaab5a..086d04a178a3 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -25,3 +25,7 @@ python3 "$X_PY" test --stage 2 check-tools python3 "$X_PY" test --stage 2 src/tools/clippy python3 "$X_PY" test --stage 2 src/tools/rustfmt python3 "$X_PY" test --stage 2 src/tools/miri +# We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. +# Also cover some other targets (on both of these hosts) via cross-testing. +python3 "$X_PY" test --stage 2 src/tools/miri --target i686-pc-windows-msvc +python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-apple-darwin From 84e6732d1e41ad8f928132cf9035a099a65c7010 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Oct 2022 13:59:45 +0200 Subject: [PATCH 0242/1126] also smoke-test 'cargo miri test' --- src/bootstrap/test.rs | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 7f63226a0612..ebac73d8aad2 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -563,10 +563,10 @@ impl Step for Miri { cargo.add_rustc_lib_path(builder, compiler); // miri tests need to know about the stage sysroot - cargo.env("MIRI_SYSROOT", miri_sysroot); + cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", sysroot); cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); - cargo.env("MIRI", miri); + cargo.env("MIRI", &miri); // propagate --bless if builder.config.cmd.bless() { cargo.env("MIRI_BLESS", "Gesundheit"); @@ -579,6 +579,40 @@ impl Step for Miri { let mut cargo = Command::from(cargo); builder.run(&mut cargo); + + // # Run `cargo miri test`. + // This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures + // that we get the desired output), but that is sufficient to make sure that the libtest harness + // itself executes properly under Miri. + let mut cargo = tool::prepare_tool_cargo( + builder, + compiler, + Mode::ToolRustc, + host, + "run", + "src/tools/miri/cargo-miri", + SourceType::Submodule, + &[], + ); + cargo.add_rustc_lib_path(builder, compiler); + cargo.arg("--").arg("miri").arg("test"); + cargo + .arg("--manifest-path") + .arg(builder.src.join("src/tools/miri/test-cargo-miri/Cargo.toml")); + cargo.arg("--target").arg(target.rustc_target_arg()); + cargo.arg("--tests"); // don't run doctests, they are too confused by the staging + cargo.arg("--").args(builder.config.cmd.test_args()); + + // Tell `cargo miri` where to find things. + cargo.env("MIRI_SYSROOT", &miri_sysroot); + cargo.env("MIRI_HOST_SYSROOT", sysroot); + cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); + cargo.env("MIRI", &miri); + // Debug things. + cargo.env("RUST_BACKTRACE", "1"); + + let mut cargo = Command::from(cargo); + builder.run(&mut cargo); } } From 7b83059146b6cfb367bbee7cd97370fc271e8979 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 24 Oct 2022 12:00:35 +0200 Subject: [PATCH 0243/1126] rustup --- src/tools/miri/rust-version | 2 +- src/tools/miri/src/shims/windows/foreign_items.rs | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index eb0301bee2ac..768658b1a122 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -b1ab3b738ac718da74cd4aa0bb7f362d0adbdf84 +56f132565eb31eeb9ec7e1800a6ab2ca354e710e diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 184ba997fc86..2a34a3a47bbb 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -418,13 +418,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Indicate an error. this.write_null(dest)?; } - "GetFileInformationByHandleEx" if this.frame_in_std() => { - #[allow(non_snake_case)] - let [_hFile, _FileInformationClass, _lpFileInformation, _dwBufferSize] = - this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - // Just make it fail. - this.write_null(dest)?; - } "GetFileType" if this.frame_in_std() => { #[allow(non_snake_case)] let [_hFile] = From a46af18cb1aedf7ccc5fbebd6f126c71da6cd4ee Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 24 Oct 2022 18:24:10 +0800 Subject: [PATCH 0244/1126] fix parentheses surrounding spacing issue in parser --- compiler/rustc_lint/src/unused.rs | 6 ++--- compiler/rustc_parse/src/errors.rs | 6 +++-- .../rustc_parse/src/parser/diagnostics.rs | 24 +++++++++++++++---- .../lint/issue-103435-extra-parentheses.fixed | 6 +++-- .../ui/lint/issue-103435-extra-parentheses.rs | 6 +++-- .../issue-103435-extra-parentheses.stderr | 20 +++++++++++++++- 6 files changed, 52 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 1299444cd773..d4b083de2762 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -568,8 +568,7 @@ trait UnusedDelimLint { let sm = cx.sess().source_map(); let lo_replace = if keep_space.0 && - let Ok(snip) = sm.span_to_snippet(lo.with_lo(lo.lo() - BytePos(1))) && - !snip.starts_with(" ") { + let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(" ") { " ".to_string() } else { "".to_string() @@ -577,8 +576,7 @@ trait UnusedDelimLint { let hi_replace = if keep_space.1 && - let Ok(snip) = sm.span_to_snippet(sm.next_point(hi)) && - !snip.starts_with(" ") { + let Ok(snip) = sm.span_to_next_source(hi) && !snip.starts_with(" ") { " ".to_string() } else { "".to_string() diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 9b177c5189bf..a3fbc06293f7 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1122,10 +1122,12 @@ pub(crate) struct ParenthesesInForHead { #[derive(Subdiagnostic)] #[multipart_suggestion(suggestion, applicability = "machine-applicable")] pub(crate) struct ParenthesesInForHeadSugg { - #[suggestion_part(code = "")] + #[suggestion_part(code = "{left_snippet}")] pub left: Span, - #[suggestion_part(code = "")] + pub left_snippet: String, + #[suggestion_part(code = "{right_snippet}")] pub right: Span, + pub right_snippet: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 887a4a6de33b..e9549e899985 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1641,15 +1641,29 @@ impl<'a> Parser<'a> { (token::CloseDelim(Delimiter::Parenthesis), Some(begin_par_sp)) => { self.bump(); + let sm = self.sess.source_map(); + let left = begin_par_sp; + let right = self.prev_token.span; + let left_snippet = if let Ok(snip) = sm.span_to_prev_source(left) && + !snip.ends_with(" ") { + " ".to_string() + } else { + "".to_string() + }; + + let right_snippet = if let Ok(snip) = sm.span_to_next_source(right) && + !snip.starts_with(" ") { + " ".to_string() + } else { + "".to_string() + }; + self.sess.emit_err(ParenthesesInForHead { - span: vec![begin_par_sp, self.prev_token.span], + span: vec![left, right], // With e.g. `for (x) in y)` this would replace `(x) in y)` // with `x) in y)` which is syntactically invalid. // However, this is prevented before we get here. - sugg: ParenthesesInForHeadSugg { - left: begin_par_sp, - right: self.prev_token.span, - }, + sugg: ParenthesesInForHeadSugg { left, right, left_snippet, right_snippet }, }); // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. diff --git a/src/test/ui/lint/issue-103435-extra-parentheses.fixed b/src/test/ui/lint/issue-103435-extra-parentheses.fixed index dbbcaa441ddd..2b01b414baa6 100644 --- a/src/test/ui/lint/issue-103435-extra-parentheses.fixed +++ b/src/test/ui/lint/issue-103435-extra-parentheses.fixed @@ -11,6 +11,8 @@ fn main() { if 2 == 1 {} //~^ ERROR unnecessary parentheses around `if` condition - // FIXME, auto recover from this one? - // for(_x in 1..10) {} + // reported by parser + for _x in 1..10 {} + //~^ ERROR expected one of + //~| ERROR unexpected parentheses surrounding } diff --git a/src/test/ui/lint/issue-103435-extra-parentheses.rs b/src/test/ui/lint/issue-103435-extra-parentheses.rs index f5c2a6664ede..8261610cf564 100644 --- a/src/test/ui/lint/issue-103435-extra-parentheses.rs +++ b/src/test/ui/lint/issue-103435-extra-parentheses.rs @@ -11,6 +11,8 @@ fn main() { if(2 == 1){} //~^ ERROR unnecessary parentheses around `if` condition - // FIXME, auto recover from this one? - // for(_x in 1..10) {} + // reported by parser + for(_x in 1..10){} + //~^ ERROR expected one of + //~| ERROR unexpected parentheses surrounding } diff --git a/src/test/ui/lint/issue-103435-extra-parentheses.stderr b/src/test/ui/lint/issue-103435-extra-parentheses.stderr index a3f2fbc51ab2..29c41c91050b 100644 --- a/src/test/ui/lint/issue-103435-extra-parentheses.stderr +++ b/src/test/ui/lint/issue-103435-extra-parentheses.stderr @@ -1,3 +1,21 @@ +error: expected one of `)`, `,`, `@`, or `|`, found keyword `in` + --> $DIR/issue-103435-extra-parentheses.rs:15:12 + | +LL | for(_x in 1..10){} + | ^^ expected one of `)`, `,`, `@`, or `|` + +error: unexpected parentheses surrounding `for` loop head + --> $DIR/issue-103435-extra-parentheses.rs:15:8 + | +LL | for(_x in 1..10){} + | ^ ^ + | +help: remove parentheses in `for` loop + | +LL - for(_x in 1..10){} +LL + for _x in 1..10 {} + | + error: unnecessary parentheses around pattern --> $DIR/issue-103435-extra-parentheses.rs:5:11 | @@ -39,5 +57,5 @@ LL - if(2 == 1){} LL + if 2 == 1 {} | -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors From 91c09d44f60c22e0874bcef393e2fa67e1875c01 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Mon, 24 Oct 2022 13:01:07 +0200 Subject: [PATCH 0245/1126] use the shared assets step for building std too --- src/bootstrap/doc.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 939e169ec00e..5c624bd4eb44 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -457,7 +457,8 @@ impl Step for Std { let target = self.target; let out = builder.doc_out(target); t!(fs::create_dir_all(&out)); - t!(fs::copy(builder.src.join("src/doc/rust.css"), out.join("rust.css"))); + + builder.ensure(SharedAssets { target: self.target }); let index_page = builder.src.join("src/doc/index.md").into_os_string(); let mut extra_args = vec![ From b6824ba52adda195f6279cd84e248936788188b9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 24 Oct 2022 11:58:45 +0000 Subject: [PATCH 0246/1126] Make param index generation a bit more robust --- .../src/collect/generics_of.rs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 707fd6c75278..c7777a946893 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -249,6 +249,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { // Now create the real type and const parameters. let type_start = own_start - has_self as u32 + params.len() as u32; let mut i = 0; + let mut next_index = || { + let prev = i; + i += 1; + prev as u32 + type_start + }; const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \ `struct`, `enum`, `type`, or `trait` definitions"; @@ -278,15 +283,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { let kind = ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic }; - let param_def = ty::GenericParamDef { - index: type_start + i as u32, + Some(ty::GenericParamDef { + index: next_index(), name: param.name.ident().name, def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), pure_wrt_drop: param.pure_wrt_drop, kind, - }; - i += 1; - Some(param_def) + }) } GenericParamKind::Const { default, .. } => { if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() { @@ -297,15 +300,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { ); } - let param_def = ty::GenericParamDef { - index: type_start + i as u32, + Some(ty::GenericParamDef { + index: next_index(), name: param.name.ident().name, def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), pure_wrt_drop: param.pure_wrt_drop, kind: ty::GenericParamDefKind::Const { has_default: default.is_some() }, - }; - i += 1; - Some(param_def) + }) } })); @@ -323,8 +324,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { &["", "", ""][..] }; - params.extend(dummy_args.iter().enumerate().map(|(i, &arg)| ty::GenericParamDef { - index: type_start + i as u32, + params.extend(dummy_args.iter().map(|&arg| ty::GenericParamDef { + index: next_index(), name: Symbol::intern(arg), def_id, pure_wrt_drop: false, @@ -337,7 +338,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node { params.push(ty::GenericParamDef { - index: type_start, + index: next_index(), name: Symbol::intern(""), def_id, pure_wrt_drop: false, From 6afd0f57eb97306e677caa2e072f7537a2b314a3 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Mon, 24 Oct 2022 19:38:16 +0900 Subject: [PATCH 0247/1126] Refactor: unwrap `Option` once in the beginning of closure --- crates/hir/src/source_analyzer.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 07bae2b38c79..9ec52282829a 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -487,9 +487,9 @@ impl SourceAnalyzer { let mut prefer_value_ns = false; let resolved = (|| { + let infer = self.infer.as_deref()?; if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) { let expr_id = self.expr_id(db, &path_expr.into())?; - let infer = self.infer.as_ref()?; if let Some(assoc) = infer.assoc_resolutions_for_expr(expr_id) { let assoc = match assoc { AssocItemId::FunctionId(f_in_trait) => { @@ -520,18 +520,18 @@ impl SourceAnalyzer { prefer_value_ns = true; } else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) { let pat_id = self.pat_id(&path_pat.into())?; - if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) { + if let Some(assoc) = infer.assoc_resolutions_for_pat(pat_id) { return Some(PathResolution::Def(AssocItem::from(assoc).into())); } if let Some(VariantId::EnumVariantId(variant)) = - self.infer.as_ref()?.variant_resolution_for_pat(pat_id) + infer.variant_resolution_for_pat(pat_id) { return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); } } else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) { let expr_id = self.expr_id(db, &rec_lit.into())?; if let Some(VariantId::EnumVariantId(variant)) = - self.infer.as_ref()?.variant_resolution_for_expr(expr_id) + infer.variant_resolution_for_expr(expr_id) { return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); } @@ -541,8 +541,7 @@ impl SourceAnalyzer { || parent().and_then(ast::TupleStructPat::cast).map(ast::Pat::from); if let Some(pat) = record_pat.or_else(tuple_struct_pat) { let pat_id = self.pat_id(&pat)?; - let variant_res_for_pat = - self.infer.as_ref()?.variant_resolution_for_pat(pat_id); + let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id); if let Some(VariantId::EnumVariantId(variant)) = variant_res_for_pat { return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); } From 3f19fa496baa0cad3787f4d666c1eb13c9c0c7cc Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 24 Oct 2022 14:49:18 +0200 Subject: [PATCH 0248/1126] Update LLVM submodule --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index 4b8525577211..2a2ea6b49e79 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 4b85255772114ca4946d95fe591933dae7d61991 +Subproject commit 2a2ea6b49e79325e0d10d33fac2b10ea3bebcc7c From 6a00e14c7aba6851d921db2053d4b11dcc514528 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 24 Oct 2022 14:56:58 +0200 Subject: [PATCH 0249/1126] fix: Don't respond with an error when requesting a shutdown while starting --- crates/rust-analyzer/src/main_loop.rs | 48 +++++++++++++++------------ 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 2c928a580405..7d10dc5d15b6 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -607,30 +607,34 @@ impl GlobalState { /// Handles a request. fn on_request(&mut self, req: Request) { - if self.shutdown_requested { - self.respond(lsp_server::Response::new_err( - req.id, - lsp_server::ErrorCode::InvalidRequest as i32, - "Shutdown already requested.".to_owned(), - )); - return; + let mut dispatcher = RequestDispatcher { req: Some(req), global_state: self }; + dispatcher.on_sync_mut::(|s, ()| { + s.shutdown_requested = true; + Ok(()) + }); + + if let RequestDispatcher { req: Some(req), global_state: this } = &mut dispatcher { + if this.shutdown_requested { + this.respond(lsp_server::Response::new_err( + req.id.clone(), + lsp_server::ErrorCode::InvalidRequest as i32, + "Shutdown already requested.".to_owned(), + )); + return; + } + + // Avoid flashing a bunch of unresolved references during initial load. + if this.workspaces.is_empty() && !this.is_quiescent() { + this.respond(lsp_server::Response::new_err( + req.id.clone(), + lsp_server::ErrorCode::ContentModified as i32, + "waiting for cargo metadata or cargo check".to_owned(), + )); + return; + } } - // Avoid flashing a bunch of unresolved references during initial load. - if self.workspaces.is_empty() && !self.is_quiescent() { - self.respond(lsp_server::Response::new_err( - req.id, - lsp_server::ErrorCode::ContentModified as i32, - "waiting for cargo metadata or cargo check".to_owned(), - )); - return; - } - - RequestDispatcher { req: Some(req), global_state: self } - .on_sync_mut::(|s, ()| { - s.shutdown_requested = true; - Ok(()) - }) + dispatcher .on_sync_mut::(handlers::handle_workspace_reload) .on_sync_mut::(handlers::handle_memory_usage) .on_sync_mut::(handlers::handle_shuffle_crate_graph) From 674cd6125da1277af2d864b7d4e637c0a73f142c Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Mon, 24 Oct 2022 15:01:58 +0100 Subject: [PATCH 0250/1126] Clairify Vec::capacity docs Fixes #103326 --- library/alloc/src/vec/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 6a71f08330ca..bbbdc3aa2a2d 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -868,13 +868,14 @@ impl Vec { (ptr, len, capacity, alloc) } - /// Returns the number of elements the vector can hold without + /// Returns the total number of elements the vector can hold without /// reallocating. /// /// # Examples /// /// ``` - /// let vec: Vec = Vec::with_capacity(10); + /// let mut vec: Vec = Vec::with_capacity(10); + /// vec.push(42); /// assert_eq!(vec.capacity(), 10); /// ``` #[inline] From fbae83acd0f7e6ddc7002774451e4e8df6b94286 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 24 Oct 2022 16:07:42 +0200 Subject: [PATCH 0251/1126] fix: Fix standard flycheck command not being executed in the workspace it is being invoked for --- crates/flycheck/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 73c3a48b4c5a..8a91d6066614 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -295,7 +295,9 @@ impl FlycheckActor { } => { let mut cmd = Command::new(toolchain::cargo()); cmd.arg(command); - cmd.args(&["--workspace", "--message-format=json"]); + cmd.current_dir(&self.root); + cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) + .arg(self.root.join("Cargo.toml").as_os_str()); if let Some(target) = target_triple { cmd.args(&["--target", target.as_str()]); From 15d4383053307a983ea81197ec2a313bc9a471d9 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Mon, 24 Oct 2022 23:28:53 +0900 Subject: [PATCH 0252/1126] Let `InferenceTable::unify()` relate `Zip` values --- crates/hir-ty/src/infer/unify.rs | 12 ++++++++---- crates/hir-ty/src/method_resolution.rs | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index b00e3216b2d2..12f45f00f9c4 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -340,8 +340,8 @@ impl<'a> InferenceTable<'a> { self.resolve_with_fallback(t, &|_, _, d, _| d) } - /// Unify two types and register new trait goals that arise from that. - pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { + /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. + pub(crate) fn unify>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, Err(_) => return false, @@ -350,9 +350,13 @@ impl<'a> InferenceTable<'a> { true } - /// Unify two types and return new trait goals arising from it, so the + /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the /// caller needs to deal with them. - pub(crate) fn try_unify>(&mut self, t1: &T, t2: &T) -> InferResult<()> { + pub(crate) fn try_unify>( + &mut self, + t1: &T, + t2: &T, + ) -> InferResult<()> { match self.var_unification_table.relate( Interner, &self.db, diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 5998680dcd39..6d3df34746e1 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -1214,7 +1214,7 @@ fn is_valid_fn_candidate( let expected_receiver = sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst); - check_that!(table.unify(&receiver_ty, &expected_receiver)); + check_that!(table.unify(receiver_ty, &expected_receiver)); } if let ItemContainerId::ImplId(impl_id) = container { From c2bc3bcfb04707dedfc24881746054086198b6e5 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sat, 22 Oct 2022 13:29:10 -0500 Subject: [PATCH 0253/1126] Document link to unstable book --- src/doc/rustc/src/command-line-arguments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 2d12cf382b16..2dc182b3d83e 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -302,7 +302,7 @@ _Note:_ The order of these lint level arguments is taken into account, see [lint This flag will allow you to set unstable options of rustc. In order to set multiple options, the -Z flag can be used multiple times. For example: `rustc -Z verbose -Z time-passes`. Specifying options with -Z is only available on nightly. To view all available options -run: `rustc -Z help`. +run: `rustc -Z help`, or see [The Unstable Book](../unstable-book/index.html). ## `--cap-lints`: set the most restrictive lint level From 4bf9b9b0031f64755bfa102a3cf9363aadea98c5 Mon Sep 17 00:00:00 2001 From: feniljain Date: Mon, 24 Oct 2022 21:06:32 +0530 Subject: [PATCH 0254/1126] refactor: remove repetitive string interpolation and doc changes --- .../generate_enum_projection_method.rs | 24 +++++++------------ crates/rust-analyzer/src/config.rs | 2 +- docs/user/generated_config.adoc | 2 +- editors/code/package.json | 2 +- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs index 732ee49f66d2..ad88a04ce87b 100644 --- a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs +++ b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs @@ -161,27 +161,21 @@ fn generate_enum_projection_method( let field_type_syntax = field_type.syntax(); - let method = if ctx.config.assist_emit_must_use - { - format!( - " #[must_use] - {vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type_syntax}{return_suffix} {{ - if let Self::{variant_name}{pattern_suffix} = self {{ - {happy_case}({bound_name}) - }} else {{ - {sad_case} - }} - }}") + let must_use = if ctx.config.assist_emit_must_use { + "#[must_use]\n" } else { - format!( - " {vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type_syntax}{return_suffix} {{ + "" + }; + + let method = format!( + " {must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type_syntax}{return_suffix} {{ if let Self::{variant_name}{pattern_suffix} = self {{ {happy_case}({bound_name}) }} else {{ {sad_case} }} - }}") - }; + }}" + ); add_method_to_adt(builder, &parent_enum, impl_def, &method); }, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 21d7538fdc1e..9bd0e22d8fc4 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -56,7 +56,7 @@ mod patch_old_style; // parsing the old name. config_data! { struct ConfigData { - /// Whether to insert must_use derive macro while generating `as_` methods + /// Whether to insert #[must_use] when generating `as_` methods /// for enum variants. assist_emitMustUse: bool = "false", /// Placeholder expression to use for missing expressions in assists. diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 82ec1d56f2b2..152b7c5d815b 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -1,7 +1,7 @@ [[rust-analyzer.assist.emitMustUse]]rust-analyzer.assist.emitMustUse (default: `false`):: + -- -Whether to insert must_use derive macro while generating `as_` methods +Whether to insert #[must_use] when generating `as_` methods for enum variants. -- [[rust-analyzer.assist.expressionFillDefault]]rust-analyzer.assist.expressionFillDefault (default: `"todo"`):: diff --git a/editors/code/package.json b/editors/code/package.json index 1446a6037721..a25e4313844f 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -398,7 +398,7 @@ }, "$generated-start": {}, "rust-analyzer.assist.emitMustUse": { - "markdownDescription": "Whether to insert must_use derive macro while generating `as_` methods\nfor enum variants.", + "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.", "default": false, "type": "boolean" }, From 1dff99f46c7698c4e7ca231572e4c3794b6b52b5 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 24 Oct 2022 17:44:17 +0200 Subject: [PATCH 0255/1126] Use functions in highlight-colors rustdoc GUI test --- src/test/rustdoc-gui/highlight-colors.goml | 141 +++++++++++++-------- 1 file changed, 89 insertions(+), 52 deletions(-) diff --git a/src/test/rustdoc-gui/highlight-colors.goml b/src/test/rustdoc-gui/highlight-colors.goml index dd01dbf6148d..51693314e85e 100644 --- a/src/test/rustdoc-gui/highlight-colors.goml +++ b/src/test/rustdoc-gui/highlight-colors.goml @@ -2,56 +2,93 @@ goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" show-text: true -local-storage: {"rustdoc-theme": "ayu", "rustdoc-use-system-theme": "false"} -reload: +define-function: ( + "check-colors", + ( + theme, + kw, + kw2, + prelude_ty, + prelude_val, + lifetime, + number, + string, + bool_val, + self, + attribute, + macro, + question_mark, + comment, + doc_comment, + ), + [ + ("local-storage", {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}), + ("reload"), + ("assert-css", ("pre.rust .kw", {"color": |kw|}, ALL)), + ("assert-css", ("pre.rust .kw-2", {"color": |kw2|}, ALL)), + ("assert-css", ("pre.rust .prelude-ty", {"color": |prelude_ty|}, ALL)), + ("assert-css", ("pre.rust .prelude-val", {"color": |prelude_val|}, ALL)), + ("assert-css", ("pre.rust .lifetime", {"color": |lifetime|}, ALL)), + ("assert-css", ("pre.rust .number", {"color": |number|}, ALL)), + ("assert-css", ("pre.rust .string", {"color": |string|}, ALL)), + ("assert-css", ("pre.rust .bool-val", {"color": |bool_val|}, ALL)), + ("assert-css", ("pre.rust .self", {"color": |self|}, ALL)), + ("assert-css", ("pre.rust .attribute", {"color": |attribute|}, ALL)), + ("assert-css", ("pre.rust .macro", {"color": |macro|}, ALL)), + ("assert-css", ("pre.rust .question-mark", {"color": |question_mark|}, ALL)), + ("assert-css", ("pre.rust .comment", {"color": |comment|}, ALL)), + ("assert-css", ("pre.rust .doccomment", {"color": |doc_comment|}, ALL)), + ], +) -assert-css: ("pre.rust .kw", {"color": "rgb(255, 119, 51)"}, ALL) -assert-css: ("pre.rust .kw-2", {"color": "rgb(255, 119, 51)"}, ALL) -assert-css: ("pre.rust .prelude-ty", {"color": "rgb(105, 242, 223)"}, ALL) -assert-css: ("pre.rust .prelude-val", {"color": "rgb(255, 119, 51)"}, ALL) -assert-css: ("pre.rust .lifetime", {"color": "rgb(255, 119, 51)"}, ALL) -assert-css: ("pre.rust .number", {"color": "rgb(184, 204, 82)"}, ALL) -assert-css: ("pre.rust .string", {"color": "rgb(184, 204, 82)"}, ALL) -assert-css: ("pre.rust .bool-val", {"color": "rgb(255, 119, 51)"}, ALL) -assert-css: ("pre.rust .self", {"color": "rgb(54, 163, 217)"}, ALL) -assert-css: ("pre.rust .attribute", {"color": "rgb(230, 225, 207)"}, ALL) -assert-css: ("pre.rust .macro", {"color": "rgb(163, 122, 204)"}, ALL) -assert-css: ("pre.rust .question-mark", {"color": "rgb(255, 144, 17)"}, ALL) -assert-css: ("pre.rust .comment", {"color": "rgb(120, 135, 151)"}, ALL) -assert-css: ("pre.rust .doccomment", {"color": "rgb(161, 172, 136)"}, ALL) - -local-storage: {"rustdoc-theme": "dark"} -reload: - -assert-css: ("pre.rust .kw", {"color": "rgb(171, 138, 193)"}, ALL) -assert-css: ("pre.rust .kw-2", {"color": "rgb(118, 154, 203)"}, ALL) -assert-css: ("pre.rust .prelude-ty", {"color": "rgb(118, 154, 203)"}, ALL) -assert-css: ("pre.rust .prelude-val", {"color": "rgb(238, 104, 104)"}, ALL) -assert-css: ("pre.rust .lifetime", {"color": "rgb(217, 127, 38)"}, ALL) -assert-css: ("pre.rust .number", {"color": "rgb(131, 163, 0)"}, ALL) -assert-css: ("pre.rust .string", {"color": "rgb(131, 163, 0)"}, ALL) -assert-css: ("pre.rust .bool-val", {"color": "rgb(238, 104, 104)"}, ALL) -assert-css: ("pre.rust .self", {"color": "rgb(238, 104, 104)"}, ALL) -assert-css: ("pre.rust .attribute", {"color": "rgb(238, 104, 104)"}, ALL) -assert-css: ("pre.rust .macro", {"color": "rgb(62, 153, 159)"}, ALL) -assert-css: ("pre.rust .question-mark", {"color": "rgb(255, 144, 17)"}, ALL) -assert-css: ("pre.rust .comment", {"color": "rgb(141, 141, 139)"}, ALL) -assert-css: ("pre.rust .doccomment", {"color": "rgb(140, 163, 117)"}, ALL) - -local-storage: {"rustdoc-theme": "light"} -reload: - -assert-css: ("pre.rust .kw", {"color": "rgb(137, 89, 168)"}, ALL) -assert-css: ("pre.rust .kw-2", {"color": "rgb(66, 113, 174)"}, ALL) -assert-css: ("pre.rust .prelude-ty", {"color": "rgb(66, 113, 174)"}, ALL) -assert-css: ("pre.rust .prelude-val", {"color": "rgb(200, 40, 41)"}, ALL) -assert-css: ("pre.rust .lifetime", {"color": "rgb(183, 101, 20)"}, ALL) -assert-css: ("pre.rust .number", {"color": "rgb(113, 140, 0)"}, ALL) -assert-css: ("pre.rust .string", {"color": "rgb(113, 140, 0)"}, ALL) -assert-css: ("pre.rust .bool-val", {"color": "rgb(200, 40, 41)"}, ALL) -assert-css: ("pre.rust .self", {"color": "rgb(200, 40, 41)"}, ALL) -assert-css: ("pre.rust .attribute", {"color": "rgb(200, 40, 41)"}, ALL) -assert-css: ("pre.rust .macro", {"color": "rgb(62, 153, 159)"}, ALL) -assert-css: ("pre.rust .question-mark", {"color": "rgb(255, 144, 17)"}, ALL) -assert-css: ("pre.rust .comment", {"color": "rgb(142, 144, 140)"}, ALL) -assert-css: ("pre.rust .doccomment", {"color": "rgb(77, 77, 76)"}, ALL) +call-function: ("check-colors", { + "theme": "ayu", + "kw": "rgb(255, 119, 51)", + "kw2": "rgb(255, 119, 51)", + "prelude_ty": "rgb(105, 242, 223)", + "prelude_val": "rgb(255, 119, 51)", + "lifetime": "rgb(255, 119, 51)", + "number": "rgb(184, 204, 82)", + "string": "rgb(184, 204, 82)", + "bool_val": "rgb(255, 119, 51)", + "self": "rgb(54, 163, 217)", + "attribute": "rgb(230, 225, 207)", + "macro": "rgb(163, 122, 204)", + "question_mark": "rgb(255, 144, 17)", + "comment": "rgb(120, 135, 151)", + "doc_comment": "rgb(161, 172, 136)", +}) +call-function: ("check-colors", { + "theme": "dark", + "kw": "rgb(171, 138, 193)", + "kw2": "rgb(118, 154, 203)", + "prelude_ty": "rgb(118, 154, 203)", + "prelude_val": "rgb(238, 104, 104)", + "lifetime": "rgb(217, 127, 38)", + "number": "rgb(131, 163, 0)", + "string": "rgb(131, 163, 0)", + "bool_val": "rgb(238, 104, 104)", + "self": "rgb(238, 104, 104)", + "attribute": "rgb(238, 104, 104)", + "macro": "rgb(62, 153, 159)", + "question_mark": "rgb(255, 144, 17)", + "comment": "rgb(141, 141, 139)", + "doc_comment": "rgb(140, 163, 117)", +}) +call-function: ("check-colors", { + "theme": "light", + "kw": "rgb(137, 89, 168)", + "kw2": "rgb(66, 113, 174)", + "prelude_ty": "rgb(66, 113, 174)", + "prelude_val": "rgb(200, 40, 41)", + "lifetime": "rgb(183, 101, 20)", + "number": "rgb(113, 140, 0)", + "string": "rgb(113, 140, 0)", + "bool_val": "rgb(200, 40, 41)", + "self": "rgb(200, 40, 41)", + "attribute": "rgb(200, 40, 41)", + "macro": "rgb(62, 153, 159)", + "question_mark": "rgb(255, 144, 17)", + "comment": "rgb(142, 144, 140)", + "doc_comment": "rgb(77, 77, 76)", +}) From f01608cbc41783710dd1909707d74d720c2b6cf8 Mon Sep 17 00:00:00 2001 From: Andrew Pollack Date: Mon, 24 Oct 2022 08:48:13 -0700 Subject: [PATCH 0256/1126] Update src/tools/compiletest/src/runtest.rs Co-authored-by: Mark Rousskov --- src/tools/compiletest/src/runtest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 3068f3c5b0b0..8af5f1da694b 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1184,7 +1184,7 @@ impl<'test> TestCx<'test> { // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS. let options_to_remove = ["-O".to_owned(), "-g".to_owned(), "--debuginfo".to_owned()]; - options.to_vec().into_iter().filter(|x| !options_to_remove.contains(x)).collect() + options.iter().filter(|x| !options_to_remove.contains(x)).map(|x| x.clone()).collect() } fn maybe_add_external_args(&self, cmd: &mut Command, args: &Vec) { From 4e1abcda1b195e7636e1e289c8a60e870eeba37a Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 24 Oct 2022 10:47:24 -0700 Subject: [PATCH 0257/1126] rustdoc: remove unused `.sidebar-logo` DOM on source pages --- src/librustdoc/html/static/css/noscript.css | 4 ++++ src/librustdoc/html/static/css/rustdoc.css | 4 ---- src/librustdoc/html/templates/page.html | 2 ++ src/test/rustdoc-gui/sidebar-source-code-display.goml | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 63b35f5d0df0..301f03a16427 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -18,3 +18,7 @@ nav.sub { /* The search bar and related controls don't work without JS */ display: none; } + +.source .sidebar { + display: none; +} diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 293c9787609b..8dea0547ee8d 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -402,10 +402,6 @@ img { overflow-y: hidden; } -.rustdoc.source .sidebar .sidebar-logo { - display: none; -} - .source .sidebar, #sidebar-toggle, #source-sidebar { background-color: var(--sidebar-background-color); } diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 2a111f94e507..ee8938ea6030 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -89,6 +89,7 @@ {#- -#} {%- endif -%}