From 26c0f97579763363310bc32cf6d8ef4cd6f5588f Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 2 Sep 2023 21:55:44 +0200 Subject: [PATCH 001/149] [`len_without_is_empty`]: follow type alias --- clippy_lints/src/len_zero.rs | 8 ++++++++ tests/ui/len_without_is_empty.rs | 23 +++++++++++++++++++++++ tests/ui/len_without_is_empty.stderr | 8 +++++++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index deba232bdd23e..c06b35ca0dabf 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -424,6 +424,14 @@ fn check_for_is_empty( item_name: Symbol, item_kind: &str, ) { + // Implementor may be a type alias, in which case we need to get the `DefId` of the aliased type to + // find the correct inherent impls. + let impl_ty = if let Some(adt) = cx.tcx.type_of(impl_ty).skip_binder().ty_adt_def() { + adt.did() + } else { + return; + }; + let is_empty = Symbol::intern("is_empty"); let is_empty = cx .tcx diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index ac6c3e06365c6..d623601110e27 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -436,4 +436,27 @@ impl DifferingErrors { } } +// Issue #11165 +pub struct Aliased1; +pub type Alias1 = Aliased1; + +impl Alias1 { + pub fn len(&self) -> usize { + todo!() + } + + pub fn is_empty(&self) -> bool { + todo!() + } +} + +pub struct Aliased2; +pub type Alias2 = Aliased2; +impl Alias2 { + pub fn len(&self) -> usize { + //~^ ERROR: type `Alias2` has a public `len` method, but no `is_empty` method + todo!() + } +} + fn main() {} diff --git a/tests/ui/len_without_is_empty.stderr b/tests/ui/len_without_is_empty.stderr index 1042ea2e1b389..50a8c8086b71f 100644 --- a/tests/ui/len_without_is_empty.stderr +++ b/tests/ui/len_without_is_empty.stderr @@ -139,5 +139,11 @@ error: struct `AsyncResultLenWithoutIsEmpty` has a public `len` method, but no ` LL | pub async fn len(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: type `Alias2` has a public `len` method, but no `is_empty` method + --> $DIR/len_without_is_empty.rs:456:5 + | +LL | pub fn len(&self) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors From 1c27ba931b5f5fce58363a59ed7aae94cb4ea336 Mon Sep 17 00:00:00 2001 From: Michael Watzko Date: Wed, 26 Jul 2023 22:30:29 +0200 Subject: [PATCH 002/149] Stabilize the Saturating type (saturating_int_impl, gh-87920) Also stabilizes saturating_int_assign_impl, gh-92354. And also make pub fns const where the underlying saturating_* fns became const in the meantime since the Saturating type was created. --- tests/ui/arithmetic_side_effects.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index def30f5903d74..b454c29aef4db 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -10,7 +10,7 @@ arithmetic_overflow, unconditional_panic )] -#![feature(const_mut_refs, inline_const, saturating_int_impl)] +#![feature(const_mut_refs, inline_const)] #![warn(clippy::arithmetic_side_effects)] extern crate proc_macro_derive; From e34e49f7ff8a4fee6667c3ec68a76e2a55e36ad8 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 6 Aug 2023 23:45:34 +0200 Subject: [PATCH 003/149] `useless_conversion`: don't lint if ty param has multiple bounds --- clippy_lints/src/useless_conversion.rs | 97 +++++++++++++++++++++----- tests/ui/useless_conversion.fixed | 18 +++++ tests/ui/useless_conversion.rs | 18 +++++ tests/ui/useless_conversion.stderr | 20 +++--- 4 files changed, 127 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 5ac4f0aa46c10..05acf034a95df 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,15 +1,19 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; -use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths}; +use clippy_utils::{ + get_parent_expr, is_diag_trait_item, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; @@ -61,22 +65,83 @@ impl MethodOrFunction { } } -/// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did` -fn into_iter_bound(cx: &LateContext<'_>, fn_did: DefId, into_iter_did: DefId, param_index: u32) -> Option { - cx.tcx - .predicates_of(fn_did) - .predicates - .iter() - .find_map(|&(ref pred, span)| { - if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() - && tr.def_id() == into_iter_did - && tr.self_ty().is_param(param_index) +/// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did`, +/// iff the `IntoIterator` bound is the only bound on the type parameter. +/// +/// This last part is important because it might be that the type the user is calling `.into_iter()` +/// on might not satisfy those other bounds and would result in compile errors: +/// ```ignore +/// pub fn foo(i: I) +/// where I: IntoIterator + ExactSizeIterator +/// ^^^^^^^^^^^^^^^^^ this extra bound stops us from suggesting to remove `.into_iter()` ... +/// { +/// assert_eq!(i.len(), 3); +/// } +/// +/// pub fn bar() { +/// foo([1, 2, 3].into_iter()); +/// ^^^^^^^^^^^^ ... here, because `[i32; 3]` is not `ExactSizeIterator` +/// } +/// ``` +fn exclusive_into_iter_bound( + cx: &LateContext<'_>, + fn_did: DefId, + into_iter_did: DefId, + param_index: u32, +) -> Option { + #[derive(Clone)] + struct ExplicitlyUsedTyParam<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + param_index: u32, + } + + impl<'a, 'tcx> TypeVisitor> for ExplicitlyUsedTyParam<'a, 'tcx> { + type BreakTy = (); + fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> ControlFlow { + // Ignore implicit `T: Sized` bound + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr)) = p.kind().skip_binder() + && let Some(sized_trait_did) = self.cx.tcx.lang_items().sized_trait() + && sized_trait_did == tr.def_id() { - Some(span) + return ControlFlow::Continue(()); + } + + // Ignore `::Item` projection, this use of the ty param specifically is fine + // because it's what we're already looking for + if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = p.kind().skip_binder() + && is_diag_trait_item(self.cx,proj.projection_ty.def_id, sym::IntoIterator) + { + return ControlFlow::Continue(()); + } + + p.super_visit_with(self) + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + if t.is_param(self.param_index) { + ControlFlow::Break(()) } else { - None + ControlFlow::Continue(()) } - }) + } + } + + let mut into_iter_span = None; + + for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { + if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() + && tr.def_id() == into_iter_did + && tr.self_ty().is_param(param_index) + { + into_iter_span = Some(*span); + } else if pred.visit_with(&mut ExplicitlyUsedTyParam { cx, param_index }).is_break() { + // Found another reference of the type parameter; conservatively assume + // that we can't remove the bound. + return None; + } + } + + into_iter_span } /// Extracts the receiver of a `.into_iter()` method call. @@ -175,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && let Some(arg_pos) = args.iter().position(|x| x.hir_id == e.hir_id) && let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos)) && let ty::Param(param) = into_iter_param.kind() - && let Some(span) = into_iter_bound(cx, parent_fn_did, into_iter_did, param.index) + && let Some(span) = exclusive_into_iter_bound(cx, parent_fn_did, into_iter_did, param.index) && self.expn_depth == 0 { // Get the "innermost" `.into_iter()` call, e.g. given this expression: diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 5259195990592..13463e1782dcb 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -151,6 +151,8 @@ fn main() { let _ = s3; let s4: Foo<'a'> = Foo; let _ = vec![s4, s4, s4].into_iter(); + + issue11300::bar(); } #[allow(dead_code)] @@ -196,6 +198,22 @@ fn explicit_into_iter_fn_arg() { b(macro_generated!()); } +mod issue11300 { + pub fn foo(i: I) + where + I: IntoIterator + ExactSizeIterator, + { + assert_eq!(i.len(), 3); + } + + pub fn bar() { + // This should not trigger the lint: + // `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be + // removed and is not useless. + foo([1, 2, 3].into_iter()); + } +} + #[derive(Copy, Clone)] struct Foo; diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index befb2f9a5c32c..06f3160b0f3c7 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -151,6 +151,8 @@ fn main() { let _ = Foo::<'a'>::from(s3); let s4: Foo<'a'> = Foo; let _ = vec![s4, s4, s4].into_iter().into_iter(); + + issue11300::bar(); } #[allow(dead_code)] @@ -196,6 +198,22 @@ fn explicit_into_iter_fn_arg() { b(macro_generated!()); } +mod issue11300 { + pub fn foo(i: I) + where + I: IntoIterator + ExactSizeIterator, + { + assert_eq!(i.len(), 3); + } + + pub fn bar() { + // This should not trigger the lint: + // `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be + // removed and is not useless. + foo([1, 2, 3].into_iter()); + } +} + #[derive(Copy, Clone)] struct Foo; diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 28e7bb61098f7..e7466e2fbdbe7 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -119,61 +119,61 @@ LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:183:7 + --> $DIR/useless_conversion.rs:185:7 | LL | b(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:173:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:184:7 + --> $DIR/useless_conversion.rs:186:7 | LL | c(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:174:18 + --> $DIR/useless_conversion.rs:176:18 | LL | fn c(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:185:7 + --> $DIR/useless_conversion.rs:187:7 | LL | d(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:177:12 + --> $DIR/useless_conversion.rs:179:12 | LL | T: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:188:7 + --> $DIR/useless_conversion.rs:190:7 | LL | b(vec![1, 2].into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:173:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:189:7 + --> $DIR/useless_conversion.rs:191:7 | LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:173:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ From cf10690ad4e1c4df0794fcf072f65515cf8bbadd Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 7 Aug 2023 04:04:51 +0200 Subject: [PATCH 004/149] check that the receiver type satisfies bounds --- clippy_lints/src/useless_conversion.rs | 121 ++++++++++++------------- tests/ui/useless_conversion.fixed | 32 +++++++ tests/ui/useless_conversion.rs | 32 +++++++ tests/ui/useless_conversion.stderr | 26 +++++- 4 files changed, 148 insertions(+), 63 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 05acf034a95df..5afb1f76df8c6 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,21 +1,21 @@ -use std::ops::ControlFlow; - use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; -use clippy_utils::{ - get_parent_expr, is_diag_trait_item, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths, -}; +use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; declare_clippy_lint! { /// ### What it does @@ -66,10 +66,7 @@ impl MethodOrFunction { } /// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did`, -/// iff the `IntoIterator` bound is the only bound on the type parameter. -/// -/// This last part is important because it might be that the type the user is calling `.into_iter()` -/// on might not satisfy those other bounds and would result in compile errors: +/// iff all of the bounds also hold for the type of the `.into_iter()` receiver. /// ```ignore /// pub fn foo(i: I) /// where I: IntoIterator + ExactSizeIterator @@ -83,61 +80,42 @@ impl MethodOrFunction { /// ^^^^^^^^^^^^ ... here, because `[i32; 3]` is not `ExactSizeIterator` /// } /// ``` -fn exclusive_into_iter_bound( - cx: &LateContext<'_>, +fn into_iter_bound<'tcx>( + cx: &LateContext<'tcx>, fn_did: DefId, into_iter_did: DefId, + into_iter_receiver: Ty<'tcx>, param_index: u32, + node_args: GenericArgsRef<'tcx>, ) -> Option { - #[derive(Clone)] - struct ExplicitlyUsedTyParam<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - param_index: u32, - } - - impl<'a, 'tcx> TypeVisitor> for ExplicitlyUsedTyParam<'a, 'tcx> { - type BreakTy = (); - fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> ControlFlow { - // Ignore implicit `T: Sized` bound - if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr)) = p.kind().skip_binder() - && let Some(sized_trait_did) = self.cx.tcx.lang_items().sized_trait() - && sized_trait_did == tr.def_id() - { - return ControlFlow::Continue(()); - } - - // Ignore `::Item` projection, this use of the ty param specifically is fine - // because it's what we're already looking for - if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = p.kind().skip_binder() - && is_diag_trait_item(self.cx,proj.projection_ty.def_id, sym::IntoIterator) - { - return ControlFlow::Continue(()); - } - - p.super_visit_with(self) - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - if t.is_param(self.param_index) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - } - } - + let param_env = cx.tcx.param_env(fn_did); let mut into_iter_span = None; for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { - if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() - && tr.def_id() == into_iter_did - && tr.self_ty().is_param(param_index) - { - into_iter_span = Some(*span); - } else if pred.visit_with(&mut ExplicitlyUsedTyParam { cx, param_index }).is_break() { - // Found another reference of the type parameter; conservatively assume - // that we can't remove the bound. - return None; + if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() { + if tr.def_id() == into_iter_did && tr.self_ty().is_param(param_index) { + into_iter_span = Some(*span); + } else { + // Substitute generics in the predicate and replace the IntoIterator type parameter with the + // `.into_iter()` receiver to see if the bound also holds for that type. + let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { + if i == param_index as usize { + GenericArg::from(into_iter_receiver) + } else { + arg + } + })); + let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate); + if !cx + .tcx + .infer_ctxt() + .build() + .predicate_must_hold_modulo_regions(&obligation) + { + return None; + } + } } } @@ -225,22 +203,41 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // `fn_sig` does not ICE. (see #11065) && cx.tcx.opt_def_kind(did).is_some_and(DefKind::is_fn_like) => { - Some((did, args, MethodOrFunction::Function)) + Some(( + did, + args, + cx.typeck_results().node_args(recv.hir_id), + MethodOrFunction::Function + )) } ExprKind::MethodCall(.., args, _) => { cx.typeck_results().type_dependent_def_id(parent.hir_id) - .map(|did| (did, args, MethodOrFunction::Method)) + .map(|did| { + return ( + did, + args, + cx.typeck_results().node_args(recv.hir_id), + MethodOrFunction::Method + ); + }) } _ => None, }; - if let Some((parent_fn_did, args, kind)) = parent_fn + if let Some((parent_fn_did, args, node_args, kind)) = parent_fn && let Some(into_iter_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator) && let sig = cx.tcx.fn_sig(parent_fn_did).skip_binder().skip_binder() && let Some(arg_pos) = args.iter().position(|x| x.hir_id == e.hir_id) && let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos)) && let ty::Param(param) = into_iter_param.kind() - && let Some(span) = exclusive_into_iter_bound(cx, parent_fn_did, into_iter_did, param.index) + && let Some(span) = into_iter_bound( + cx, + parent_fn_did, + into_iter_did, + cx.typeck_results().expr_ty(into_iter_recv), + param.index, + node_args + ) && self.expn_depth == 0 { // Get the "innermost" `.into_iter()` call, e.g. given this expression: diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 13463e1782dcb..f3504bc62b45e 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -206,11 +206,43 @@ mod issue11300 { assert_eq!(i.len(), 3); } + trait Helper {} + impl Helper for [i32; 3] {} + impl Helper for std::array::IntoIter {} + impl Helper<()> for std::array::IntoIter {} + + fn foo2(_: I) + where + I: IntoIterator + Helper, + { + } + + trait Helper2 {} + impl Helper2> for i32 {} + impl Helper2<[i32; 3]> for i32 {} + fn foo3(_: I) + where + I: IntoIterator, + i32: Helper2, + { + } + pub fn bar() { // This should not trigger the lint: // `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be // removed and is not useless. foo([1, 2, 3].into_iter()); + + // This should trigger the lint, receiver type [i32; 3] also implements `Helper` + foo2::([1, 2, 3]); + + // This again should *not* lint, since X = () and I = std::array::IntoIter, + // and `[i32; 3]: Helper<()>` is not true (only `std::array::IntoIter: Helper<()>` is). + foo2::<(), _>([1, 2, 3].into_iter()); + + // This should lint. Removing the `.into_iter()` means that `I` gets substituted with `[i32; 3]`, + // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. + foo3([1, 2, 3]); } } diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 06f3160b0f3c7..a4f4722927fea 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -206,11 +206,43 @@ mod issue11300 { assert_eq!(i.len(), 3); } + trait Helper {} + impl Helper for [i32; 3] {} + impl Helper for std::array::IntoIter {} + impl Helper<()> for std::array::IntoIter {} + + fn foo2(_: I) + where + I: IntoIterator + Helper, + { + } + + trait Helper2 {} + impl Helper2> for i32 {} + impl Helper2<[i32; 3]> for i32 {} + fn foo3(_: I) + where + I: IntoIterator, + i32: Helper2, + { + } + pub fn bar() { // This should not trigger the lint: // `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be // removed and is not useless. foo([1, 2, 3].into_iter()); + + // This should trigger the lint, receiver type [i32; 3] also implements `Helper` + foo2::([1, 2, 3].into_iter()); + + // This again should *not* lint, since X = () and I = std::array::IntoIter, + // and `[i32; 3]: Helper<()>` is not true (only `std::array::IntoIter: Helper<()>` is). + foo2::<(), _>([1, 2, 3].into_iter()); + + // This should lint. Removing the `.into_iter()` means that `I` gets substituted with `[i32; 3]`, + // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. + foo3([1, 2, 3].into_iter()); } } diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index e7466e2fbdbe7..81a0898bdf62a 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -178,5 +178,29 @@ note: this parameter accepts any `IntoIterator`, so you don't need to call `.int LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:237:24 + | +LL | foo2::([1, 2, 3].into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:216:12 + | +LL | I: IntoIterator + Helper, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:245:14 + | +LL | foo3([1, 2, 3].into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:225:12 + | +LL | I: IntoIterator, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 26 previous errors From 18f36897ef0844dc5e5a0d995356247cbee72ff3 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Tue, 5 Sep 2023 19:08:34 +0200 Subject: [PATCH 005/149] use the correct node args for substitution --- clippy_lints/src/useless_conversion.rs | 52 +++++++++++++++----------- tests/ui/useless_conversion.fixed | 41 ++++++++++++++++++++ tests/ui/useless_conversion.rs | 41 ++++++++++++++++++++ tests/ui/useless_conversion.stderr | 26 ++++++++++++- 4 files changed, 137 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 5afb1f76df8c6..f32e7edad6cb6 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -12,7 +12,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty}; +use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -93,27 +93,35 @@ fn into_iter_bound<'tcx>( for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() { - if tr.def_id() == into_iter_did && tr.self_ty().is_param(param_index) { - into_iter_span = Some(*span); - } else { - // Substitute generics in the predicate and replace the IntoIterator type parameter with the - // `.into_iter()` receiver to see if the bound also holds for that type. - let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { - if i == param_index as usize { - GenericArg::from(into_iter_receiver) - } else { - arg + if tr.self_ty().is_param(param_index) { + if tr.def_id() == into_iter_did { + into_iter_span = Some(*span); + } else { + let tr = cx.tcx.erase_regions(tr); + if tr.has_escaping_bound_vars() { + return None; + } + + // Substitute generics in the predicate and replace the IntoIterator type parameter with the + // `.into_iter()` receiver to see if the bound also holds for that type. + let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { + if i == param_index as usize { + GenericArg::from(into_iter_receiver) + } else { + arg + } + })); + + let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate); + if !cx + .tcx + .infer_ctxt() + .build() + .predicate_must_hold_modulo_regions(&obligation) + { + return None; } - })); - let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); - let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate); - if !cx - .tcx - .infer_ctxt() - .build() - .predicate_must_hold_modulo_regions(&obligation) - { - return None; } } } @@ -216,7 +224,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { return ( did, args, - cx.typeck_results().node_args(recv.hir_id), + cx.typeck_results().node_args(parent.hir_id), MethodOrFunction::Method ); }) diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index f3504bc62b45e..ed8387b7eb2c5 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -244,6 +244,47 @@ mod issue11300 { // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. foo3([1, 2, 3]); } + + fn ice() { + struct S1; + impl S1 { + pub fn foo(&self, _: I) {} + } + + S1.foo([1, 2]); + + // ICE that occured in itertools + trait Itertools { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized; + } + impl Itertools for I { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized, + { + } + } + let v0: Vec = vec![0, 2, 4]; + let v1: Vec = vec![1, 3, 5, 7]; + v0.into_iter().interleave_shortest(v1); + + trait TraitWithLifetime<'a> {} + impl<'a> TraitWithLifetime<'a> for std::array::IntoIter<&'a i32, 2> {} + + struct Helper; + impl<'a> Helper { + fn with_lt(&self, _: I) + where + I: IntoIterator + TraitWithLifetime<'a>, + { + } + } + Helper.with_lt([&1, &2].into_iter()); + } } #[derive(Copy, Clone)] diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index a4f4722927fea..991a7762fc64a 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -244,6 +244,47 @@ mod issue11300 { // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. foo3([1, 2, 3].into_iter()); } + + fn ice() { + struct S1; + impl S1 { + pub fn foo(&self, _: I) {} + } + + S1.foo([1, 2].into_iter()); + + // ICE that occured in itertools + trait Itertools { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized; + } + impl Itertools for I { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized, + { + } + } + let v0: Vec = vec![0, 2, 4]; + let v1: Vec = vec![1, 3, 5, 7]; + v0.into_iter().interleave_shortest(v1.into_iter()); + + trait TraitWithLifetime<'a> {} + impl<'a> TraitWithLifetime<'a> for std::array::IntoIter<&'a i32, 2> {} + + struct Helper; + impl<'a> Helper { + fn with_lt(&self, _: I) + where + I: IntoIterator + TraitWithLifetime<'a>, + { + } + } + Helper.with_lt([&1, &2].into_iter()); + } } #[derive(Copy, Clone)] diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 81a0898bdf62a..c1f8b6b4aa966 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -202,5 +202,29 @@ note: this parameter accepts any `IntoIterator`, so you don't need to call `.int LL | I: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 26 previous errors +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:254:16 + | +LL | S1.foo([1, 2].into_iter()); + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2]` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:251:27 + | +LL | pub fn foo(&self, _: I) {} + | ^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:273:44 + | +LL | v0.into_iter().interleave_shortest(v1.into_iter()); + | ^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `v1` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:260:20 + | +LL | J: IntoIterator, + | ^^^^^^^^^^^^ + +error: aborting due to 28 previous errors From 0f74faf1584b5639a214ef794026bef2d842bfa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 31 Aug 2023 22:12:47 +0200 Subject: [PATCH 006/149] Use `Freeze` for `SourceFile.lines` --- .../src/undocumented_unsafe_blocks.rs | 84 +++++++++---------- clippy_utils/src/source.rs | 2 +- 2 files changed, 39 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index a1ea3a495eb6a..da8d8ed4c0f2c 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -507,20 +507,18 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - return unsafe_line.sf.lines(|lines| { - if comment_start_line.line >= unsafe_line.line { - HasSafetyComment::No - } else { - match text_has_safety_comment( - src, - &lines[comment_start_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + return if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } - }); + }; } } HasSafetyComment::Maybe @@ -551,20 +549,18 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - return unsafe_line.sf.lines(|lines| { - if comment_start_line.line >= unsafe_line.line { - HasSafetyComment::No - } else { - match text_has_safety_comment( - src, - &lines[comment_start_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + return if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } - }); + }; } } HasSafetyComment::Maybe @@ -614,20 +610,18 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - unsafe_line.sf.lines(|lines| { - if macro_line.line < unsafe_line.line { - match text_has_safety_comment( - src, - &lines[macro_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } - } else { - HasSafetyComment::No + if macro_line.line < unsafe_line.line { + match text_has_safety_comment( + src, + &unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } - }) + } else { + HasSafetyComment::No + } } else { // Problem getting source text. Pretend a comment was found. HasSafetyComment::Maybe @@ -671,13 +665,11 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // Get the text from the start of function body to the unsafe block. // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } // ^-------------^ - unsafe_line.sf.lines(|lines| { - body_line.line < unsafe_line.line && text_has_safety_comment( - src, - &lines[body_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ).is_some() - }) + body_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ).is_some() } else { // Problem getting source text. Pretend a comment was found. true diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 03416d35ba452..600cd084c19f5 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -118,7 +118,7 @@ fn first_char_in_first_line(cx: &T, span: Span) -> Option(cx: &T, span: Span) -> Span { let span = original_sp(span, DUMMY_SP); let SourceFileAndLine { sf, line } = cx.sess().source_map().lookup_line(span.lo()).unwrap(); - let line_start = sf.lines(|lines| lines[line]); + let line_start = sf.lines()[line]; let line_start = sf.absolute_position(line_start); span.with_lo(line_start) } From cc8c0e00991ccf692b9b12bf887cefac99fffa04 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 8 Sep 2023 12:40:21 +0000 Subject: [PATCH 007/149] Ignore span's parents in `collect_ast_format_args`/`find_format_args` --- clippy_utils/src/macros.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 173f9841d4469..98724fcbe96a3 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -389,7 +389,9 @@ thread_local! { /// `FormatArgsCollector` pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) { AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args.borrow_mut().insert(span, format_args.clone()); + ast_format_args + .borrow_mut() + .insert(span.with_parent(None), format_args.clone()); }); } @@ -414,7 +416,7 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, if let Some(expr) = format_args_expr { AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args.borrow().get(&expr.span).map(callback); + ast_format_args.borrow().get(&expr.span.with_parent(None)).map(callback); }); } } From e88a556124189e3ee23841238252b3831b3af966 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 8 Sep 2023 22:39:20 +0000 Subject: [PATCH 008/149] Reuse rustdoc's doc comment handling in Clippy --- clippy_lints/Cargo.toml | 1 - clippy_lints/src/doc.rs | 185 +++++++++------------------ clippy_lints/src/lib.rs | 2 + tests/compile-test.rs | 4 +- tests/ui/doc/doc-fixable.fixed | 10 ++ tests/ui/doc/doc-fixable.rs | 10 ++ tests/ui/doc/doc-fixable.stderr | 13 +- tests/ui/doc/unbalanced_ticks.rs | 2 +- tests/ui/doc/unbalanced_ticks.stderr | 21 +-- tests/ui/doc_errors.rs | 16 +++ tests/ui/doc_errors.stderr | 2 +- tests/ui/needless_doc_main.rs | 9 +- tests/ui/needless_doc_main.stderr | 42 ++++-- 13 files changed, 160 insertions(+), 157 deletions(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index c0a9f466e7b0e..dcd9a4adcbd46 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -15,7 +15,6 @@ clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } if_chain = "1.0" itertools = "0.10.1" -pulldown-cmark = { version = "0.9", default-features = false } quine-mc_cluskey = "0.2" regex-syntax = "0.7" serde = { version = "1.0", features = ["derive"] } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index e29ab634c9790..d8f2fbcea0027 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,18 +1,16 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::source::{first_line_of_span, snippet_with_applicability}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty}; use if_chain::if_chain; -use itertools::Itertools; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; -use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind}; -use rustc_ast::token::CommentKind; +use rustc_ast::ast::{Async, Attribute, Fn, FnRetTy, ItemKind}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; @@ -26,6 +24,9 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_parse::maybe_new_parser_from_source_str; use rustc_parse::parser::ForceCollect; +use rustc_resolve::rustdoc::{ + add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, DocFragment, +}; use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::edition::Edition; @@ -450,53 +451,16 @@ fn lint_for_missing_headers( } } -/// Cleanup documentation decoration. -/// -/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or -/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we -/// need to keep track of -/// the spans but this function is inspired from the later. -#[expect(clippy::cast_possible_truncation)] -#[must_use] -pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) { - // one-line comments lose their prefix - if comment_kind == CommentKind::Line { - let mut doc = doc.to_owned(); - doc.push('\n'); - let len = doc.len(); - // +3 skips the opening delimiter - return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]); - } +#[derive(Copy, Clone)] +struct Fragments<'a> { + doc: &'a str, + fragments: &'a [DocFragment], +} - let mut sizes = vec![]; - let mut contains_initial_stars = false; - for line in doc.lines() { - let offset = line.as_ptr() as usize - doc.as_ptr() as usize; - debug_assert_eq!(offset as u32 as usize, offset); - contains_initial_stars |= line.trim_start().starts_with('*'); - // +1 adds the newline, +3 skips the opening delimiter - sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32)))); - } - if !contains_initial_stars { - return (doc.to_string(), sizes); - } - // remove the initial '*'s if any - let mut no_stars = String::with_capacity(doc.len()); - for line in doc.lines() { - let mut chars = line.chars(); - for c in &mut chars { - if c.is_whitespace() { - no_stars.push(c); - } else { - no_stars.push(if c == '*' { ' ' } else { c }); - break; - } - } - no_stars.push_str(chars.as_str()); - no_stars.push('\n'); +impl Fragments<'_> { + fn span(self, cx: &LateContext<'_>, range: Range) -> Option { + source_span_for_markdown_range(cx.tcx, &self.doc, &range, &self.fragments) } - - (no_stars, sizes) } #[derive(Copy, Clone, Default)] @@ -515,27 +479,12 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ Some(("fake".into(), "fake".into())) } + let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let mut doc = String::new(); - let mut spans = vec![]; - - for attr in attrs { - if let AttrKind::DocComment(comment_kind, comment) = attr.kind { - let (comment, current_spans) = strip_doc_comment_decoration(comment.as_str(), comment_kind, attr.span); - spans.extend_from_slice(¤t_spans); - doc.push_str(&comment); - } else if attr.has_name(sym::doc) { - // ignore mix of sugared and non-sugared doc - // don't trigger the safety or errors check - return None; - } - } - - let mut current = 0; - for &mut (ref mut offset, _) in &mut spans { - let offset_copy = *offset; - *offset = current; - current += offset_copy; + for fragment in &fragments { + add_doc_fragment(&mut doc, fragment); } + doc.pop(); if doc.is_empty() { return Some(DocHeaders::default()); @@ -543,23 +492,19 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ let mut cb = fake_broken_link_callback; - let parser = - pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter(); - // Iterate over all `Events` and combine consecutive events into one - let events = parser.coalesce(|previous, current| { - let previous_range = previous.1; - let current_range = current.1; - - match (previous.0, current.0) { - (Text(previous), Text(current)) => { - let mut previous = previous.to_string(); - previous.push_str(¤t); - Ok((Text(previous.into()), previous_range)) - }, - (previous, current) => Err(((previous, previous_range), (current, current_range))), - } - }); - Some(check_doc(cx, valid_idents, events, &spans)) + // disable smart punctuation to pick up ['link'] more easily + let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION; + let parser = pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut cb)); + + Some(check_doc( + cx, + valid_idents, + parser.into_offset_iter(), + Fragments { + fragments: &fragments, + doc: &doc, + }, + )) } const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; @@ -568,7 +513,7 @@ fn check_doc<'a, Events: Iterator, Range, valid_idents: &FxHashSet, events: Events, - spans: &[(usize, Span)], + fragments: Fragments<'_>, ) -> DocHeaders { // true if a safety header was found let mut headers = DocHeaders::default(); @@ -579,8 +524,8 @@ fn check_doc<'a, Events: Iterator, Range, Span)> = Vec::new(); - let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1; + let mut text_to_check: Vec<(CowStr<'_>, Range)> = Vec::new(); + let mut paragraph_range = 0..0; for (event, range) in events { match event { Start(CodeBlock(ref kind)) => { @@ -613,25 +558,28 @@ fn check_doc<'a, Events: Iterator, Range { if let End(Heading(_, _, _)) = event { in_heading = false; } - if ticks_unbalanced { + if ticks_unbalanced + && let Some(span) = fragments.span(cx, paragraph_range.clone()) + { span_lint_and_help( cx, DOC_MARKDOWN, - paragraph_span, + span, "backticks are unbalanced", None, "a backtick may be missing a pair", ); } else { - for (text, span) in text_to_check { - check_text(cx, valid_idents, &text, span); + for (text, range) in text_to_check { + if let Some(span) = fragments.span(cx, range) { + check_text(cx, valid_idents, &text, span); + } } } text_to_check = Vec::new(); @@ -640,8 +588,7 @@ fn check_doc<'a, Events: Iterator, Range (), // HTML is weird, just ignore it SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), FootnoteReference(text) | Text(text) => { - let (begin, span) = get_current_span(spans, range.start); - paragraph_span = paragraph_span.with_hi(span.hi()); + paragraph_range.end = range.end; ticks_unbalanced |= text.contains('`') && !in_code; if Some(&text) == in_link.as_ref() || ticks_unbalanced { // Probably a link of the form `` @@ -658,19 +605,19 @@ fn check_doc<'a, Events: Iterator, 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); +fn check_link_quotes(cx: &LateContext<'_>, trimmed_text: &str, range: Range, fragments: Fragments<'_>) { + if trimmed_text.starts_with('\'') + && trimmed_text.ends_with('\'') + && let Some(span) = fragments.span(cx, range) + { span_lint( cx, DOC_LINK_WITH_QUOTES, - span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)), + span, "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, - Err(e) => e - 1, - }; - spans[index] -} - -fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range, fragments: Fragments<'_>) { fn has_needless_main(code: String, edition: Edition) -> bool { rustc_driver::catch_fatal_errors(|| { rustc_span::create_session_globals_then(edition, || { @@ -774,12 +706,13 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { .unwrap_or_default() } + let trailing_whitespace = text.len() - text.trim_end().len(); + // Because of the global session, we need to create a new session in a different thread with // the edition we need. let text = text.to_owned(); - if thread::spawn(move || has_needless_main(text, edition)) - .join() - .expect("thread::spawn failed") + if thread::spawn(move || has_needless_main(text, edition)).join().expect("thread::spawn failed") + && let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ab71bfbdc587e..20fdf26dc521b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -21,6 +21,7 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) +extern crate pulldown_cmark; extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; @@ -37,6 +38,7 @@ extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; extern crate rustc_parse; +extern crate rustc_resolve; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; diff --git a/tests/compile-test.rs b/tests/compile-test.rs index e329a94ff6a1d..c1c3d4a868f92 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -130,7 +130,9 @@ fn base_config(test_dir: &str) -> (Config, Args) { }; let mut config = Config { - mode: Mode::Yolo { rustfix: RustfixMode::Everything }, + mode: Mode::Yolo { + rustfix: RustfixMode::Everything, + }, stderr_filters: vec![(Match::PathBackslash, b"/")], stdout_filters: vec![], output_conflict_handling: if bless { diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index f7c2f14a4824a..47b56960a00fe 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -198,6 +198,16 @@ fn pulldown_cmark_crash() {} /// [plain text][path::to::item] fn intra_doc_link() {} +/// Ignore escaped\_underscores +/// +/// \\[ +/// \\prod\_{x\\in X} p\_x +/// \\] +fn issue_2581() {} + +/// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` +fn lint_after_escaped_chars() {} + // issue #7033 - generic_const_exprs ICE struct S where [(); N.checked_next_power_of_two().unwrap()]: { diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 51961e75b84c2..4d9a4eafa5fcb 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -198,6 +198,16 @@ fn pulldown_cmark_crash() {} /// [plain text][path::to::item] fn intra_doc_link() {} +/// Ignore escaped\_underscores +/// +/// \\[ +/// \\prod\_{x\\in X} p\_x +/// \\] +fn issue_2581() {} + +/// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint +fn lint_after_escaped_chars() {} + // issue #7033 - generic_const_exprs ICE struct S where [(); N.checked_next_power_of_two().unwrap()]: { diff --git a/tests/ui/doc/doc-fixable.stderr b/tests/ui/doc/doc-fixable.stderr index a30ded81385f1..4c9ff41d91835 100644 --- a/tests/ui/doc/doc-fixable.stderr +++ b/tests/ui/doc/doc-fixable.stderr @@ -308,5 +308,16 @@ help: try LL | /// An iterator over `mycrate::Collection`'s values. | ~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 28 previous errors +error: item in documentation is missing backticks + --> $DIR/doc-fixable.rs:208:34 + | +LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` + | ~~~~~~~~~~~~~~~~~ + +error: aborting due to 29 previous errors diff --git a/tests/ui/doc/unbalanced_ticks.rs b/tests/ui/doc/unbalanced_ticks.rs index 4a1711f79a020..6f7bab72040c8 100644 --- a/tests/ui/doc/unbalanced_ticks.rs +++ b/tests/ui/doc/unbalanced_ticks.rs @@ -48,4 +48,4 @@ fn other_markdown() {} /// /// `lol` /// pub struct Struct; /// ``` -fn iss_7421() {} +fn issue_7421() {} diff --git a/tests/ui/doc/unbalanced_ticks.stderr b/tests/ui/doc/unbalanced_ticks.stderr index 92b6f8536c88b..89ad8db3916c2 100644 --- a/tests/ui/doc/unbalanced_ticks.stderr +++ b/tests/ui/doc/unbalanced_ticks.stderr @@ -1,7 +1,8 @@ error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:7:1 + --> $DIR/unbalanced_ticks.rs:7:5 | -LL | / /// This is a doc comment with `unbalanced_tick marks and several words that +LL | /// This is a doc comment with `unbalanced_tick marks and several words that + | _____^ LL | | LL | | /// should be `encompassed_by` tick marks because they `contain_underscores`. LL | | /// Because of the initial `unbalanced_tick` pair, the error message is @@ -13,10 +14,10 @@ LL | | /// very `confusing_and_misleading`. = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]` error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:14:1 + --> $DIR/unbalanced_ticks.rs:14:5 | LL | /// This paragraph has `unbalanced_tick marks and should stop_linting. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair @@ -32,10 +33,10 @@ LL | /// This paragraph is fine and `should_be` linted normally. | ~~~~~~~~~~~ error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:20:1 + --> $DIR/unbalanced_ticks.rs:20:5 | LL | /// Double unbalanced backtick from ``here to here` should lint. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair @@ -51,18 +52,18 @@ LL | /// ## `not_fine` | ~~~~~~~~~~ error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:37:1 + --> $DIR/unbalanced_ticks.rs:37:5 | LL | /// ### `unbalanced - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:40:1 + --> $DIR/unbalanced_ticks.rs:40:5 | LL | /// - This `item has unbalanced tick marks - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index 86721f61d199c..d661231c59ea8 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -85,6 +85,22 @@ impl Struct1 { async fn async_priv_method_missing_errors_header() -> Result<(), ()> { unimplemented!(); } + + /** + # Errors + A description of the errors goes here. + */ + fn block_comment() -> Result<(), ()> { + unimplemented!(); + } + + /** + * # Errors + * A description of the errors goes here. + */ + fn block_comment_leading_asterisks() -> Result<(), ()> { + unimplemented!(); + } } pub trait Trait1 { diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index 9cea864f1d867..28b2db2440b7a 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -38,7 +38,7 @@ LL | pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:92:5 + --> $DIR/doc_errors.rs:108:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index d31b9047eaaeb..fee05926ce4fc 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -10,7 +10,7 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// With an explicit return type it should lint too /// ```edition2015 /// fn main() -> () { @@ -18,7 +18,7 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// This should, too. /// ```rust /// fn main() { @@ -26,11 +26,12 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// This one too. /// ```no_run -/// fn main() { +/// // the fn is not always the first line //~^ ERROR: needless `fn main` in doctest +/// fn main() { /// unimplemented!(); /// } /// ``` diff --git a/tests/ui/needless_doc_main.stderr b/tests/ui/needless_doc_main.stderr index 8dd93bb05dd50..84256548671d1 100644 --- a/tests/ui/needless_doc_main.stderr +++ b/tests/ui/needless_doc_main.stderr @@ -1,29 +1,47 @@ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:7:4 + --> $DIR/needless_doc_main.rs:7:5 | -LL | /// fn main() { - | ^^^^^^^^^^^^ +LL | /// fn main() { + | _____^ +LL | | +LL | | +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ | = note: `-D clippy::needless-doctest-main` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_doctest_main)]` error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:16:4 + --> $DIR/needless_doc_main.rs:16:5 | -LL | /// fn main() -> () { - | ^^^^^^^^^^^^^^^^^^ +LL | /// fn main() -> () { + | _____^ +LL | | +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:24:4 + --> $DIR/needless_doc_main.rs:24:5 | -LL | /// fn main() { - | ^^^^^^^^^^^^ +LL | /// fn main() { + | _____^ +LL | | +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:32:4 + --> $DIR/needless_doc_main.rs:32:5 | -LL | /// fn main() { - | ^^^^^^^^^^^^ +LL | /// // the fn is not always the first line + | _____^ +LL | | +LL | | /// fn main() { +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ error: aborting due to 4 previous errors From 54f3ddb84a2bbdbb0b60f1d38ea3deae03286b30 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 10 Sep 2023 18:17:47 +0000 Subject: [PATCH 009/149] Call `LateLintPass::check_attribute` from `with_lint_attrs` --- tests/ui/allow_attributes.fixed | 7 +++++++ tests/ui/allow_attributes.rs | 7 +++++++ tests/ui/blanket_clippy_restriction_lints.stderr | 14 +++++++------- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/tests/ui/allow_attributes.fixed b/tests/ui/allow_attributes.fixed index 945ba83611c0d..b506a9890f5b9 100644 --- a/tests/ui/allow_attributes.fixed +++ b/tests/ui/allow_attributes.fixed @@ -22,6 +22,13 @@ struct T4; #[cfg_attr(panic = "unwind", expect(dead_code))] struct CfgT; +#[allow(clippy::allow_attributes, unused)] +struct Allowed; + +#[expect(clippy::allow_attributes)] +#[allow(unused)] +struct Expected; + fn ignore_external() { external! { #[allow(clippy::needless_borrow)] // Should not lint diff --git a/tests/ui/allow_attributes.rs b/tests/ui/allow_attributes.rs index 8afa61c7002c5..c7daa7abd9d41 100644 --- a/tests/ui/allow_attributes.rs +++ b/tests/ui/allow_attributes.rs @@ -22,6 +22,13 @@ struct T4; #[cfg_attr(panic = "unwind", allow(dead_code))] struct CfgT; +#[allow(clippy::allow_attributes, unused)] +struct Allowed; + +#[expect(clippy::allow_attributes)] +#[allow(unused)] +struct Expected; + fn ignore_external() { external! { #[allow(clippy::needless_borrow)] // Should not lint diff --git a/tests/ui/blanket_clippy_restriction_lints.stderr b/tests/ui/blanket_clippy_restriction_lints.stderr index d04ea7151c2a5..afb634f34b41a 100644 --- a/tests/ui/blanket_clippy_restriction_lints.stderr +++ b/tests/ui/blanket_clippy_restriction_lints.stderr @@ -1,10 +1,3 @@ -error: `clippy::restriction` is not meant to be enabled as a group - | - = note: because of the command line `--warn clippy::restriction` - = help: enable the restriction lints you need individually - = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]` - error: `clippy::restriction` is not meant to be enabled as a group --> $DIR/blanket_clippy_restriction_lints.rs:6:9 | @@ -12,6 +5,8 @@ LL | #![warn(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | = help: enable the restriction lints you need individually + = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]` error: `clippy::restriction` is not meant to be enabled as a group --> $DIR/blanket_clippy_restriction_lints.rs:8:9 @@ -29,5 +24,10 @@ LL | #![forbid(clippy::restriction)] | = help: enable the restriction lints you need individually +error: `clippy::restriction` is not meant to be enabled as a group + | + = note: because of the command line `--warn clippy::restriction` + = help: enable the restriction lints you need individually + error: aborting due to 4 previous errors From ab08a3d7b2143154f16b82c388bb355aff6c4b4b Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 8 Sep 2023 16:14:47 +0000 Subject: [PATCH 010/149] Update tools and fulldeps tests --- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 2 +- clippy_utils/src/ast_utils.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 23d6e2a845fd6..d10f10ef87e81 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -586,7 +586,7 @@ fn ident_difference_expr_with_base_location( | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) | (While(_, _, _), While(_, _, _)) | (If(_, _, _), If(_, _, _)) - | (Let(_, _, _), Let(_, _, _)) + | (Let(_, _, _, _), Let(_, _, _, _)) | (Type(_, _), Type(_, _)) | (Cast(_, _), Cast(_, _)) | (Lit(_), Lit(_)) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 9cf5957722985..766a548145160 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -68,7 +68,7 @@ impl EarlyLintPass for UnnestedOrPatterns { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { if self.msrv.meets(msrvs::OR_PATTERNS) { - if let ast::ExprKind::Let(pat, _, _) = &e.kind { + if let ast::ExprKind::Let(pat, _, _, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } } diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 140cfa2194fcb..a78ff02021f25 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -166,7 +166,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), (Lit(l), Lit(r)) => l == r, (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), - (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re), + (Let(lp, le, _, _), Let(rp, re, _, _)) => eq_pat(lp, rp) && eq_expr(le, re), (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt), (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => { From f598bb75d47c45b69b3fe2952d53fcccf870e8b3 Mon Sep 17 00:00:00 2001 From: Michael Krasnitski Date: Mon, 11 Sep 2023 21:43:27 -0400 Subject: [PATCH 011/149] Walk the left side of where bounds if the bounded type is not a generic parameter --- clippy_lints/src/extra_unused_type_parameters.rs | 7 ++++++- tests/ui/extra_unused_type_parameters.fixed | 15 +++++++++++++++ tests/ui/extra_unused_type_parameters.rs | 15 +++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index c18006a71c26e..0a885984abbf2 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -246,8 +246,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { { self.ty_params.remove(&def_id); } + } else { + // If the bounded type isn't a generic param, but is instead a concrete generic + // type, any params we find nested inside of it are being used as concrete types, + // and can therefore can be considered used. So, we're fine to walk the left-hand + // side of the where bound. + walk_ty(self, predicate.bounded_ty); } - // Only walk the right-hand side of where bounds for bound in predicate.bounds { walk_param_bound(self, bound); } diff --git a/tests/ui/extra_unused_type_parameters.fixed b/tests/ui/extra_unused_type_parameters.fixed index a4943344a11ef..c9bebabdf17ae 100644 --- a/tests/ui/extra_unused_type_parameters.fixed +++ b/tests/ui/extra_unused_type_parameters.fixed @@ -113,4 +113,19 @@ with_span!( } ); +mod issue11302 { + use std::fmt::Debug; + use std::marker::PhantomData; + + #[derive(Debug)] + struct Wrapper(PhantomData); + + fn store(v: &mut Vec>) + where + Wrapper: Debug, + { + v.push(Box::new(Wrapper(PhantomData))); + } +} + fn main() {} diff --git a/tests/ui/extra_unused_type_parameters.rs b/tests/ui/extra_unused_type_parameters.rs index 6d85b1ce9d4f3..1bc0047adf01f 100644 --- a/tests/ui/extra_unused_type_parameters.rs +++ b/tests/ui/extra_unused_type_parameters.rs @@ -113,4 +113,19 @@ with_span!( } ); +mod issue11302 { + use std::fmt::Debug; + use std::marker::PhantomData; + + #[derive(Debug)] + struct Wrapper(PhantomData); + + fn store(v: &mut Vec>) + where + Wrapper: Debug, + { + v.push(Box::new(Wrapper(PhantomData))); + } +} + fn main() {} From f136e1634a8d834d32deb89273717227eedaf153 Mon Sep 17 00:00:00 2001 From: jonboh Date: Sat, 26 Aug 2023 16:26:09 +0200 Subject: [PATCH 012/149] new unnecessary_map_on_constructor lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/unnecessary_map_on_constructor.rs | 93 +++++++++++++++++++ tests/ui/eta.fixed | 3 +- tests/ui/eta.rs | 3 +- tests/ui/eta.stderr | 54 +++++------ tests/ui/manual_map_option.fixed | 1 + tests/ui/manual_map_option.rs | 1 + tests/ui/manual_map_option.stderr | 42 ++++----- tests/ui/option_filter_map.fixed | 2 +- tests/ui/option_filter_map.rs | 2 +- tests/ui/result_map_unit_fn_unfixable.rs | 2 +- tests/ui/unnecessary_map_on_constructor.fixed | 56 +++++++++++ tests/ui/unnecessary_map_on_constructor.rs | 56 +++++++++++ .../ui/unnecessary_map_on_constructor.stderr | 53 +++++++++++ 16 files changed, 319 insertions(+), 53 deletions(-) create mode 100644 clippy_lints/src/unnecessary_map_on_constructor.rs create mode 100644 tests/ui/unnecessary_map_on_constructor.fixed create mode 100644 tests/ui/unnecessary_map_on_constructor.rs create mode 100644 tests/ui/unnecessary_map_on_constructor.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ae4a0ee2c09..9aca13a6cf642 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5437,6 +5437,7 @@ Released 2018-09-13 [`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap +[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a4d40df52e7bc..ecf44e05920ae 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -671,6 +671,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO, crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO, crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO, + crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 440940a44780a..f5035dcfeeb6a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -329,6 +329,7 @@ mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; mod unnecessary_box_returns; +mod unnecessary_map_on_constructor; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_struct_initialization; @@ -1102,6 +1103,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)); store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)); + store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unnecessary_map_on_constructor.rs b/clippy_lints/src/unnecessary_map_on_constructor.rs new file mode 100644 index 0000000000000..5aa057580e9d9 --- /dev/null +++ b/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -0,0 +1,93 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::get_type_diagnostic_name; +use rustc_errors::Applicability; +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 + /// Suggest removing the use of a may (or map_err) method when an Option or Result is being construted. + /// + /// ### Why is this bad? + /// It introduces unnecessary complexity. In this case the function can be used directly and + /// construct the Option or Result from the output. + /// + /// ### Example + /// ```rust + /// Some(4).map(i32::swap_bytes); + /// ``` + /// Use instead: + /// ```rust + /// Some(i32::swap_bytes(4)); + /// ``` + #[clippy::version = "1.73.0"] + pub UNNECESSARY_MAP_ON_CONSTRUCTOR, + complexity, + "using `map`/`map_err` on `Option` or `Result` constructors" +} +declare_lint_pass!(UnnecessaryMapOnConstructor => [UNNECESSARY_MAP_ON_CONSTRUCTOR]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if expr.span.from_expansion() { + return; + } + if let hir::ExprKind::MethodCall(path, recv, args, ..) = expr.kind + && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)){ + let (constructor_path, constructor_item) = + if let hir::ExprKind::Call(constructor, constructor_args) = recv.kind + && let hir::ExprKind::Path(constructor_path) = constructor.kind + && let Some(arg) = constructor_args.get(0) + { + if constructor.span.from_expansion() || arg.span.from_expansion() { + return; + } + (constructor_path, arg) + } else { + return; + }; + let constructor_symbol = match constructor_path { + hir::QPath::Resolved(_, path) => { + if let Some(path_segment) = path.segments.last() { + path_segment.ident.name + } else { + return; + } + }, + hir::QPath::TypeRelative(_, path) => path.ident.name, + hir::QPath::LangItem(_, _, _) => return, + }; + match constructor_symbol { + sym::Some | sym::Ok if path.ident.name == rustc_span::sym::map => (), + sym::Err if path.ident.name == sym!(map_err) => (), + _ => return, + } + + if let Some(map_arg) = args.get(0) + && let hir::ExprKind::Path(fun) = map_arg.kind + { + if map_arg.span.from_expansion() { + return; + } + let mut applicability = Applicability::MachineApplicable; + let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut applicability); + let constructor_snippet = + snippet_with_applicability(cx, constructor_path.span(), "_", &mut applicability); + let constructor_arg_snippet = + snippet_with_applicability(cx, constructor_item.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + UNNECESSARY_MAP_ON_CONSTRUCTOR, + expr.span, + &format!("unnecessary {} on constructor {constructor_snippet}(_)", path.ident.name), + "try", + format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"), + applicability, + ); + } + } + } +} diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index f1cac8c5fbc63..32c7499bf73f6 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -7,7 +7,8 @@ clippy::option_map_unit_fn, clippy::redundant_closure_call, clippy::uninlined_format_args, - clippy::useless_vec + clippy::useless_vec, + clippy::unnecessary_map_on_constructor )] use std::path::{Path, PathBuf}; diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index c7a470b5be6df..25b7431ba8cd7 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -7,7 +7,8 @@ clippy::option_map_unit_fn, clippy::redundant_closure_call, clippy::uninlined_format_args, - clippy::useless_vec + clippy::useless_vec, + clippy::unnecessary_map_on_constructor )] use std::path::{Path, PathBuf}; diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 7c7f179746205..951e4ac749c25 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -1,5 +1,5 @@ error: redundant closure - --> $DIR/eta.rs:28:27 + --> $DIR/eta.rs:29:27 | LL | let a = Some(1u8).map(|a| foo(a)); | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` @@ -8,31 +8,31 @@ LL | let a = Some(1u8).map(|a| foo(a)); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` error: redundant closure - --> $DIR/eta.rs:32:40 + --> $DIR/eta.rs:33:40 | LL | let _: Option> = true.then(|| vec![]); // special case vec! | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` error: redundant closure - --> $DIR/eta.rs:33:35 + --> $DIR/eta.rs:34:35 | LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2` error: redundant closure - --> $DIR/eta.rs:34:26 + --> $DIR/eta.rs:35:26 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` error: redundant closure - --> $DIR/eta.rs:41:27 + --> $DIR/eta.rs:42:27 | LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` error: redundant closure - --> $DIR/eta.rs:93:51 + --> $DIR/eta.rs:94:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` @@ -41,127 +41,127 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_for_method_calls)]` error: redundant closure - --> $DIR/eta.rs:94:51 + --> $DIR/eta.rs:95:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` error: redundant closure - --> $DIR/eta.rs:96:42 + --> $DIR/eta.rs:97:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` error: redundant closure - --> $DIR/eta.rs:100:29 + --> $DIR/eta.rs:101:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` error: redundant closure - --> $DIR/eta.rs:101:27 + --> $DIR/eta.rs:102:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` error: redundant closure - --> $DIR/eta.rs:103:65 + --> $DIR/eta.rs:104:65 | LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> $DIR/eta.rs:166:22 + --> $DIR/eta.rs:167:22 | LL | requires_fn_once(|| x()); | ^^^^^^ help: replace the closure with the function itself: `x` error: redundant closure - --> $DIR/eta.rs:173:27 + --> $DIR/eta.rs:174:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> $DIR/eta.rs:178:27 + --> $DIR/eta.rs:179:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: redundant closure - --> $DIR/eta.rs:210:28 + --> $DIR/eta.rs:211:28 | LL | x.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> $DIR/eta.rs:211:28 + --> $DIR/eta.rs:212:28 | LL | y.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> $DIR/eta.rs:212:28 + --> $DIR/eta.rs:213:28 | LL | z.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` error: redundant closure - --> $DIR/eta.rs:219:21 + --> $DIR/eta.rs:220:21 | LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> $DIR/eta.rs:223:21 + --> $DIR/eta.rs:224:21 | LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` error: redundant closure - --> $DIR/eta.rs:316:18 + --> $DIR/eta.rs:317:18 | LL | takes_fn_mut(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> $DIR/eta.rs:319:19 + --> $DIR/eta.rs:320:19 | LL | takes_fn_once(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> $DIR/eta.rs:323:26 + --> $DIR/eta.rs:324:26 | LL | move || takes_fn_mut(|| f_used_once()) | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` error: redundant closure - --> $DIR/eta.rs:335:19 + --> $DIR/eta.rs:336:19 | LL | array_opt.map(|a| a.as_slice()); | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice` error: redundant closure - --> $DIR/eta.rs:338:19 + --> $DIR/eta.rs:339:19 | LL | slice_opt.map(|s| s.len()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len` error: redundant closure - --> $DIR/eta.rs:341:17 + --> $DIR/eta.rs:342:17 | LL | ptr_opt.map(|p| p.is_null()); | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null` error: redundant closure - --> $DIR/eta.rs:345:17 + --> $DIR/eta.rs:346:17 | LL | dyn_opt.map(|d| d.method_on_dyn()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `::method_on_dyn` error: redundant closure - --> $DIR/eta.rs:388:19 + --> $DIR/eta.rs:389:19 | LL | let _ = f(&0, |x, y| f2(x, y)); | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index f6a964da418a2..16cee3fd38236 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -5,6 +5,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, + clippy::unnecessary_map_on_constructor, for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index df9dc256d3038..4655acf1406c7 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -5,6 +5,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, + clippy::unnecessary_map_on_constructor, for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index ff6ed974d4a12..3754a982cb9bc 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:13:5 + --> $DIR/manual_map_option.rs:14:5 | LL | / match Some(0) { LL | | Some(_) => Some(2), @@ -11,7 +11,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::manual_map)]` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:18:5 + --> $DIR/manual_map_option.rs:19:5 | LL | / match Some(0) { LL | | Some(x) => Some(x + 1), @@ -20,7 +20,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| x + 1)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:23:5 + --> $DIR/manual_map_option.rs:24:5 | LL | / match Some("") { LL | | Some(x) => Some(x.is_empty()), @@ -29,7 +29,7 @@ LL | | }; | |_____^ help: try: `Some("").map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:28:5 + --> $DIR/manual_map_option.rs:29:5 | LL | / if let Some(x) = Some(0) { LL | | Some(!x) @@ -39,7 +39,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| !x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:35:5 + --> $DIR/manual_map_option.rs:36:5 | LL | / match Some(0) { LL | | Some(x) => { Some(std::convert::identity(x)) } @@ -48,7 +48,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(std::convert::identity)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:40:5 + --> $DIR/manual_map_option.rs:41:5 | LL | / match Some(&String::new()) { LL | | Some(x) => Some(str::len(x)), @@ -57,7 +57,7 @@ LL | | }; | |_____^ help: try: `Some(&String::new()).map(|x| str::len(x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:50:5 + --> $DIR/manual_map_option.rs:51:5 | LL | / match &Some([0, 1]) { LL | | Some(x) => Some(x[0]), @@ -66,7 +66,7 @@ LL | | }; | |_____^ help: try: `Some([0, 1]).as_ref().map(|x| x[0])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:55:5 + --> $DIR/manual_map_option.rs:56:5 | LL | / match &Some(0) { LL | | &Some(x) => Some(x * 2), @@ -75,7 +75,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| x * 2)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:60:5 + --> $DIR/manual_map_option.rs:61:5 | LL | / match Some(String::new()) { LL | | Some(ref x) => Some(x.is_empty()), @@ -84,7 +84,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:65:5 + --> $DIR/manual_map_option.rs:66:5 | LL | / match &&Some(String::new()) { LL | | Some(x) => Some(x.len()), @@ -93,7 +93,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:70:5 + --> $DIR/manual_map_option.rs:71:5 | LL | / match &&Some(0) { LL | | &&Some(x) => Some(x + x), @@ -102,7 +102,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:83:9 + --> $DIR/manual_map_option.rs:84:9 | LL | / match &mut Some(String::new()) { LL | | Some(x) => Some(x.push_str("")), @@ -111,7 +111,7 @@ LL | | }; | |_________^ help: try: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:89:5 + --> $DIR/manual_map_option.rs:90:5 | LL | / match &mut Some(String::new()) { LL | | Some(ref x) => Some(x.len()), @@ -120,7 +120,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:94:5 + --> $DIR/manual_map_option.rs:95:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -129,7 +129,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:99:5 + --> $DIR/manual_map_option.rs:100:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -138,7 +138,7 @@ LL | | }; | |_____^ help: try: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:104:5 + --> $DIR/manual_map_option.rs:105:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -147,7 +147,7 @@ LL | | }; | |_____^ help: try: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:109:5 + --> $DIR/manual_map_option.rs:110:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), @@ -156,7 +156,7 @@ LL | | }; | |_____^ help: try: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:167:5 + --> $DIR/manual_map_option.rs:168:5 | LL | / match Some(0) { LL | | Some(x) => Some(vec![x]), @@ -165,7 +165,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| vec![x])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:172:5 + --> $DIR/manual_map_option.rs:173:5 | LL | / match option_env!("") { LL | | Some(x) => Some(String::from(x)), @@ -174,7 +174,7 @@ LL | | }; | |_____^ help: try: `option_env!("").map(String::from)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:192:12 + --> $DIR/manual_map_option.rs:193:12 | LL | } else if let Some(x) = Some(0) { | ____________^ @@ -185,7 +185,7 @@ LL | | }; | |_____^ help: try: `{ Some(0).map(|x| x + 1) }` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:200:12 + --> $DIR/manual_map_option.rs:201:12 | LL | } else if let Some(x) = Some(0) { | ____________^ diff --git a/tests/ui/option_filter_map.fixed b/tests/ui/option_filter_map.fixed index d4c04ff907b2a..ee004c0e194bb 100644 --- a/tests/ui/option_filter_map.fixed +++ b/tests/ui/option_filter_map.fixed @@ -1,5 +1,5 @@ #![warn(clippy::option_filter_map)] -#![allow(clippy::map_flatten)] +#![allow(clippy::map_flatten, clippy::unnecessary_map_on_constructor)] fn main() { let _ = Some(Some(1)).flatten(); diff --git a/tests/ui/option_filter_map.rs b/tests/ui/option_filter_map.rs index 99fb4723cab7a..eae2fa176a890 100644 --- a/tests/ui/option_filter_map.rs +++ b/tests/ui/option_filter_map.rs @@ -1,5 +1,5 @@ #![warn(clippy::option_filter_map)] -#![allow(clippy::map_flatten)] +#![allow(clippy::map_flatten, clippy::unnecessary_map_on_constructor)] fn main() { let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap); diff --git a/tests/ui/result_map_unit_fn_unfixable.rs b/tests/ui/result_map_unit_fn_unfixable.rs index a4dfc8f293d3f..62798b6d3d6f3 100644 --- a/tests/ui/result_map_unit_fn_unfixable.rs +++ b/tests/ui/result_map_unit_fn_unfixable.rs @@ -1,6 +1,6 @@ #![warn(clippy::result_map_unit_fn)] #![feature(never_type)] -#![allow(unused)] +#![allow(unused, clippy::unnecessary_map_on_constructor)] //@no-rustfix struct HasResult { field: Result, diff --git a/tests/ui/unnecessary_map_on_constructor.fixed b/tests/ui/unnecessary_map_on_constructor.fixed new file mode 100644 index 0000000000000..d0ba7ed749e4b --- /dev/null +++ b/tests/ui/unnecessary_map_on_constructor.fixed @@ -0,0 +1,56 @@ +#![allow(unused)] +#![warn(clippy::unnecessary_map_on_constructor)] + +use std::ffi::OsStr; + +fn fun(t: i32) -> i32 { + t +} + +fn notfun(e: SimpleError) -> SimpleError { + e +} +macro_rules! expands_to_fun { + () => { + fun + }; +} + +#[derive(Copy, Clone)] +struct SimpleError {} + +type SimpleResult = std::result::Result; + +fn main() { + let x: i32 = 4; + + let err = SimpleError {}; + let a = Some(x); + let b: SimpleResult = Ok(x); + let c: SimpleResult = Err(err); + + let a = Some(fun(x)); + let b: SimpleResult = Ok(fun(x)); + let c: SimpleResult = Err(notfun(err)); + + let a = Option::Some(fun(x)); + let b: SimpleResult = SimpleResult::Ok(fun(x)); + let c: SimpleResult = SimpleResult::Err(notfun(err)); + let b: std::result::Result = Ok(fun(x)); + let c: std::result::Result = Err(notfun(err)); + + let a = Some(fun(x)); + let b: SimpleResult = Ok(fun(x)); + let c: SimpleResult = Err(notfun(err)); + + // Should not trigger warning + a.map(fun); + b.map(fun); + c.map_err(notfun); + + b.map_err(notfun); // Ok(_).map_err + c.map(fun); // Err(_).map() + + option_env!("PATH").map(OsStr::new); + Some(x).map(expands_to_fun!()); +} diff --git a/tests/ui/unnecessary_map_on_constructor.rs b/tests/ui/unnecessary_map_on_constructor.rs new file mode 100644 index 0000000000000..e89e7aad4c40e --- /dev/null +++ b/tests/ui/unnecessary_map_on_constructor.rs @@ -0,0 +1,56 @@ +#![allow(unused)] +#![warn(clippy::unnecessary_map_on_constructor)] + +use std::ffi::OsStr; + +fn fun(t: i32) -> i32 { + t +} + +fn notfun(e: SimpleError) -> SimpleError { + e +} +macro_rules! expands_to_fun { + () => { + fun + }; +} + +#[derive(Copy, Clone)] +struct SimpleError {} + +type SimpleResult = std::result::Result; + +fn main() { + let x: i32 = 4; + + let err = SimpleError {}; + let a = Some(x); + let b: SimpleResult = Ok(x); + let c: SimpleResult = Err(err); + + let a = Some(x).map(fun); + let b: SimpleResult = Ok(x).map(fun); + let c: SimpleResult = Err(err).map_err(notfun); + + let a = Option::Some(x).map(fun); + let b: SimpleResult = SimpleResult::Ok(x).map(fun); + let c: SimpleResult = SimpleResult::Err(err).map_err(notfun); + let b: std::result::Result = Ok(x).map(fun); + let c: std::result::Result = Err(err).map_err(notfun); + + let a = Some(fun(x)); + let b: SimpleResult = Ok(fun(x)); + let c: SimpleResult = Err(notfun(err)); + + // Should not trigger warning + a.map(fun); + b.map(fun); + c.map_err(notfun); + + b.map_err(notfun); // Ok(_).map_err + c.map(fun); // Err(_).map() + + option_env!("PATH").map(OsStr::new); + Some(x).map(expands_to_fun!()); +} diff --git a/tests/ui/unnecessary_map_on_constructor.stderr b/tests/ui/unnecessary_map_on_constructor.stderr new file mode 100644 index 0000000000000..d522b68d8726a --- /dev/null +++ b/tests/ui/unnecessary_map_on_constructor.stderr @@ -0,0 +1,53 @@ +error: unnecessary map on constructor Some(_) + --> $DIR/unnecessary_map_on_constructor.rs:32:13 + | +LL | let a = Some(x).map(fun); + | ^^^^^^^^^^^^^^^^ help: try: `Some(fun(x))` + | + = note: `-D clippy::unnecessary-map-on-constructor` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_on_constructor)]` + +error: unnecessary map on constructor Ok(_) + --> $DIR/unnecessary_map_on_constructor.rs:33:27 + | +LL | let b: SimpleResult = Ok(x).map(fun); + | ^^^^^^^^^^^^^^ help: try: `Ok(fun(x))` + +error: unnecessary map_err on constructor Err(_) + --> $DIR/unnecessary_map_on_constructor.rs:34:27 + | +LL | let c: SimpleResult = Err(err).map_err(notfun); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Err(notfun(err))` + +error: unnecessary map on constructor Option::Some(_) + --> $DIR/unnecessary_map_on_constructor.rs:36:13 + | +LL | let a = Option::Some(x).map(fun); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option::Some(fun(x))` + +error: unnecessary map on constructor SimpleResult::Ok(_) + --> $DIR/unnecessary_map_on_constructor.rs:37:27 + | +LL | let b: SimpleResult = SimpleResult::Ok(x).map(fun); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `SimpleResult::Ok(fun(x))` + +error: unnecessary map_err on constructor SimpleResult::Err(_) + --> $DIR/unnecessary_map_on_constructor.rs:38:27 + | +LL | let c: SimpleResult = SimpleResult::Err(err).map_err(notfun); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `SimpleResult::Err(notfun(err))` + +error: unnecessary map on constructor Ok(_) + --> $DIR/unnecessary_map_on_constructor.rs:39:52 + | +LL | let b: std::result::Result = Ok(x).map(fun); + | ^^^^^^^^^^^^^^ help: try: `Ok(fun(x))` + +error: unnecessary map_err on constructor Err(_) + --> $DIR/unnecessary_map_on_constructor.rs:40:52 + | +LL | let c: std::result::Result = Err(err).map_err(notfun); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Err(notfun(err))` + +error: aborting due to 8 previous errors + From 471469d30facd25ed3072524694e369aeb72082c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 12 Sep 2023 18:13:53 +0200 Subject: [PATCH 013/149] Merge commit '98363cbf6a7c3f8b571a7d92a3c645bb4376e4a6' into clippyup --- .github/driver.sh | 2 +- .github/workflows/clippy_bors.yml | 33 +- CHANGELOG.md | 5 + Cargo.toml | 2 +- book/src/SUMMARY.md | 3 + book/src/development/emitting_lints.md | 217 ++++++++ book/src/development/trait_checking.md | 105 ++++ book/src/development/writing_tests.md | 218 ++++++++ book/src/lint_configuration.md | 24 + clippy_lints/src/declared_lints.rs | 6 +- .../src/default_union_representation.rs | 3 + clippy_lints/src/dereference.rs | 31 +- clippy_lints/src/derivable_impls.rs | 3 +- clippy_lints/src/disallowed_macros.rs | 11 +- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/if_then_some_else_none.rs | 18 +- clippy_lints/src/ignored_unit_patterns.rs | 13 +- clippy_lints/src/implied_bounds_in_impls.rs | 242 ++++++--- clippy_lints/src/lib.rs | 9 +- clippy_lints/src/loops/explicit_iter_loop.rs | 17 +- clippy_lints/src/loops/mod.rs | 10 +- clippy_lints/src/loops/never_loop.rs | 268 +++++----- clippy_lints/src/manual_range_patterns.rs | 129 +++-- .../src/methods/iter_out_of_bounds.rs | 106 ++++ .../src/methods/iter_overeager_cloned.rs | 2 +- clippy_lints/src/methods/mod.rs | 58 ++- .../src/missing_asserts_for_indexing.rs | 391 +++++++++++++++ ...orrect_impls.rs => non_canonical_impls.rs} | 45 +- .../src/operators/arithmetic_side_effects.rs | 16 +- clippy_lints/src/operators/float_cmp.rs | 2 +- clippy_lints/src/raw_strings.rs | 89 +++- clippy_lints/src/renamed_lints.rs | 4 +- .../src/slow_vector_initialization.rs | 12 +- clippy_lints/src/std_instead_of_core.rs | 26 +- .../src/undocumented_unsafe_blocks.rs | 107 ++-- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_lints/src/unwrap.rs | 111 ++++- clippy_lints/src/utils/conf.rs | 20 + clippy_utils/src/lib.rs | 4 +- clippy_utils/src/paths.rs | 2 + clippy_utils/src/source.rs | 2 +- clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/ty.rs | 7 +- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- clippy_utils/src/usage.rs | 13 +- rust-toolchain | 2 +- tests/compile-test.rs | 45 +- .../cargo_common_metadata/fail/Cargo.stderr | 1 + .../fail_publish/Cargo.stderr | 1 + .../fail_publish_true/Cargo.stderr | 1 + .../ui-cargo/duplicate_mod/fail/Cargo.stderr | 1 + tests/ui-cargo/feature_name/fail/Cargo.stderr | 2 + .../module_style/fail_mod/Cargo.stderr | 1 + .../module_style/fail_mod_remap/Cargo.stderr | 1 + .../module_style/fail_no_mod/Cargo.stderr | 1 + .../multiple_crate_versions/fail/Cargo.stderr | 1 + .../wildcard_dependencies/fail/Cargo.stderr | 1 + tests/ui-internal/check_formulation.stderr | 1 + tests/ui-internal/if_chain_style.stderr | 1 + tests/ui-internal/invalid_paths.stderr | 1 + ...unnecessary_def_path_hardcoded_path.stderr | 1 + .../disallowed_macros/auxiliary/macros.rs | 15 + tests/ui-toml/disallowed_macros/clippy.toml | 2 + .../disallowed_macros/disallowed_macros.rs | 5 +- .../disallowed_macros.stderr | 22 +- .../auxiliary/proc_macros.rs | 469 ------------------ .../excessive_nesting/excessive_nesting.rs | 4 +- .../toml_unknown_key/conf_unknown_key.stderr | 2 + .../auxiliary/proc_macro_unsafe.rs | 13 - .../undocumented_unsafe_blocks.rs | 16 +- tests/ui/bool_comparison.fixed | 2 +- tests/ui/bool_comparison.rs | 2 +- ...ze_32bit.stderr => cast_size.32bit.stderr} | 56 ++- ...ast_size.stderr => cast_size.64bit.stderr} | 34 +- tests/ui/cast_size.rs | 31 +- tests/ui/cast_size_32bit.rs | 56 --- .../ui/checked_unwrap/simple_conditionals.rs | 51 ++ .../checked_unwrap/simple_conditionals.stderr | 70 ++- tests/ui/clone_on_copy_impl.rs | 2 +- tests/ui/crashes/ice-11337.rs | 9 + tests/ui/crashes/ice-11422.fixed | 25 + tests/ui/crashes/ice-11422.rs | 25 + tests/ui/crashes/ice-11422.stderr | 16 + tests/ui/crashes/ice-360.rs | 3 +- tests/ui/crashes/ice-360.stderr | 20 +- tests/ui/derivable_impls.fixed | 13 + tests/ui/derivable_impls.rs | 13 + tests/ui/derive.rs | 6 +- tests/ui/derive.stderr | 20 +- tests/ui/derive_ord_xor_partial_ord.rs | 2 +- tests/ui/empty_loop.rs | 3 + tests/ui/empty_loop.stderr | 4 +- tests/ui/enum_clike_unportable_variant.rs | 2 +- tests/ui/enum_clike_unportable_variant.stderr | 17 +- tests/ui/explicit_auto_deref.fixed | 30 +- tests/ui/explicit_auto_deref.rs | 28 ++ tests/ui/explicit_auto_deref.stderr | 12 +- tests/ui/explicit_iter_loop.fixed | 7 +- tests/ui/explicit_iter_loop.rs | 1 + tests/ui/explicit_iter_loop.stderr | 65 +-- tests/ui/float_cmp.rs | 13 + tests/ui/float_cmp.stderr | 12 +- ...stderr => fn_to_numeric_cast.32bit.stderr} | 62 +-- ...stderr => fn_to_numeric_cast.64bit.stderr} | 44 +- tests/ui/fn_to_numeric_cast.rs | 27 +- tests/ui/fn_to_numeric_cast_32bit.rs | 80 --- tests/ui/if_then_some_else_none.rs | 12 + tests/ui/ignored_unit_patterns.fixed | 16 +- tests/ui/ignored_unit_patterns.rs | 16 +- tests/ui/ignored_unit_patterns.stderr | 10 +- tests/ui/implied_bounds_in_impls.fixed | 57 +++ tests/ui/implied_bounds_in_impls.rs | 57 +++ tests/ui/implied_bounds_in_impls.stderr | 74 ++- tests/ui/iter_out_of_bounds.rs | 71 +++ tests/ui/iter_out_of_bounds.stderr | 119 +++++ tests/ui/iter_overeager_cloned.fixed | 6 +- tests/ui/iter_overeager_cloned.rs | 2 - tests/ui/iter_overeager_cloned.stderr | 18 +- tests/ui/iter_skip_next.fixed | 1 + tests/ui/iter_skip_next.rs | 1 + tests/ui/iter_skip_next.stderr | 14 +- tests/ui/iter_skip_next_unfixable.rs | 2 +- tests/ui/iter_skip_zero.fixed | 2 +- tests/ui/iter_skip_zero.rs | 2 +- tests/ui/large_enum_variant.32bit.stderr | 280 +++++++++++ ...stderr => large_enum_variant.64bit.stderr} | 44 +- tests/ui/large_enum_variant.rs | 1 + tests/ui/manual_range_patterns.fixed | 17 +- tests/ui/manual_range_patterns.rs | 11 + tests/ui/manual_range_patterns.stderr | 66 ++- tests/ui/missing_asserts_for_indexing.fixed | 121 +++++ tests/ui/missing_asserts_for_indexing.rs | 121 +++++ tests/ui/missing_asserts_for_indexing.stderr | 253 ++++++++++ .../missing_asserts_for_indexing_unfixable.rs | 49 ++ ...sing_asserts_for_indexing_unfixable.stderr | 164 ++++++ tests/ui/needless_borrow.fixed | 13 + tests/ui/needless_borrow.rs | 13 + tests/ui/needless_raw_string.fixed | 13 +- tests/ui/needless_raw_string.rs | 13 +- tests/ui/needless_raw_string.stderr | 48 +- tests/ui/needless_raw_string_hashes.fixed | 19 +- tests/ui/needless_raw_string_hashes.rs | 19 +- tests/ui/needless_raw_string_hashes.stderr | 144 +++++- tests/ui/never_loop.rs | 55 +- tests/ui/never_loop.stderr | 27 +- ...e.fixed => non_canonical_clone_impl.fixed} | 0 ...py_type.rs => non_canonical_clone_impl.rs} | 0 ...stderr => non_canonical_clone_impl.stderr} | 19 +- ...d => non_canonical_partial_ord_impl.fixed} | 0 ...e.rs => non_canonical_partial_ord_impl.rs} | 0 ... => non_canonical_partial_ord_impl.stderr} | 11 +- ..._canonical_partial_ord_impl_fully_qual.rs} | 3 - ...onical_partial_ord_impl_fully_qual.stderr} | 14 +- tests/ui/rename.fixed | 8 +- tests/ui/rename.rs | 8 +- tests/ui/rename.stderr | 132 ++--- tests/ui/result_large_err.rs | 2 + tests/ui/result_large_err.stderr | 24 +- tests/ui/similar_names.rs | 1 + tests/ui/similar_names.stderr | 28 +- tests/ui/single_call_fn.rs | 1 + tests/ui/single_call_fn.stderr | 16 +- tests/ui/slow_vector_initialization.rs | 16 +- tests/ui/slow_vector_initialization.stderr | 12 +- tests/ui/std_instead_of_core.fixed | 62 +++ tests/ui/std_instead_of_core.rs | 1 - tests/ui/std_instead_of_core.stderr | 71 +-- tests/ui/transmute_32bit.stderr | 30 +- .../unnecessary_struct_initialization.fixed | 2 +- tests/ui/unnecessary_struct_initialization.rs | 2 +- tests/ui/vec.fixed | 1 + tests/ui/vec.rs | 1 + tests/ui/vec.stderr | 10 +- tests/ui/write_literal_2.stderr | 4 +- 174 files changed, 4682 insertions(+), 1635 deletions(-) create mode 100644 book/src/development/emitting_lints.md create mode 100644 book/src/development/trait_checking.md create mode 100644 book/src/development/writing_tests.md create mode 100644 clippy_lints/src/methods/iter_out_of_bounds.rs create mode 100644 clippy_lints/src/missing_asserts_for_indexing.rs rename clippy_lints/src/{incorrect_impls.rs => non_canonical_impls.rs} (88%) delete mode 100644 tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs delete mode 100644 tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs rename tests/ui/{cast_size_32bit.stderr => cast_size.32bit.stderr} (78%) rename tests/ui/{cast_size.stderr => cast_size.64bit.stderr} (94%) delete mode 100644 tests/ui/cast_size_32bit.rs create mode 100644 tests/ui/crashes/ice-11337.rs create mode 100644 tests/ui/crashes/ice-11422.fixed create mode 100644 tests/ui/crashes/ice-11422.rs create mode 100644 tests/ui/crashes/ice-11422.stderr rename tests/ui/{fn_to_numeric_cast_32bit.stderr => fn_to_numeric_cast.32bit.stderr} (70%) rename tests/ui/{fn_to_numeric_cast.stderr => fn_to_numeric_cast.64bit.stderr} (85%) delete mode 100644 tests/ui/fn_to_numeric_cast_32bit.rs create mode 100644 tests/ui/iter_out_of_bounds.rs create mode 100644 tests/ui/iter_out_of_bounds.stderr create mode 100644 tests/ui/large_enum_variant.32bit.stderr rename tests/ui/{large_enum_variant.stderr => large_enum_variant.64bit.stderr} (91%) create mode 100644 tests/ui/missing_asserts_for_indexing.fixed create mode 100644 tests/ui/missing_asserts_for_indexing.rs create mode 100644 tests/ui/missing_asserts_for_indexing.stderr create mode 100644 tests/ui/missing_asserts_for_indexing_unfixable.rs create mode 100644 tests/ui/missing_asserts_for_indexing_unfixable.stderr rename tests/ui/{incorrect_clone_impl_on_copy_type.fixed => non_canonical_clone_impl.fixed} (100%) rename tests/ui/{incorrect_clone_impl_on_copy_type.rs => non_canonical_clone_impl.rs} (100%) rename tests/ui/{incorrect_clone_impl_on_copy_type.stderr => non_canonical_clone_impl.stderr} (54%) rename tests/ui/{incorrect_partial_ord_impl_on_ord_type.fixed => non_canonical_partial_ord_impl.fixed} (100%) rename tests/ui/{incorrect_partial_ord_impl_on_ord_type.rs => non_canonical_partial_ord_impl.rs} (100%) rename tests/ui/{incorrect_partial_ord_impl_on_ord_type.stderr => non_canonical_partial_ord_impl.stderr} (63%) rename tests/ui/{incorrect_partial_ord_impl_on_ord_type_fully_qual.rs => non_canonical_partial_ord_impl_fully_qual.rs} (82%) rename tests/ui/{incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr => non_canonical_partial_ord_impl_fully_qual.stderr} (67%) create mode 100644 tests/ui/std_instead_of_core.fixed diff --git a/.github/driver.sh b/.github/driver.sh index 798782340ee7d..c05c6ecc1151c 100644 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -30,7 +30,7 @@ unset CARGO_MANIFEST_DIR # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 # FIXME: How to match the clippy invocation in compile-test.rs? ./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1 -sed -e "s,tests/ui,\$DIR," -e "/= help/d" double_neg.stderr >normalized.stderr +sed -e "s,tests/ui,\$DIR," -e "/= help: for/d" double_neg.stderr > normalized.stderr diff -u normalized.stderr tests/ui/double_neg.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 5c69714bc1e99..9b96f8dc25338 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -52,24 +52,14 @@ jobs: needs: changelog strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc] - exclude: + include: - os: ubuntu-latest - host: x86_64-apple-darwin - - os: ubuntu-latest - host: x86_64-pc-windows-msvc - - os: macos-latest - host: x86_64-unknown-linux-gnu - - os: macos-latest - host: i686-unknown-linux-gnu - - os: macos-latest - host: x86_64-pc-windows-msvc - - os: windows-latest host: x86_64-unknown-linux-gnu - - os: windows-latest + - os: ubuntu-latest host: i686-unknown-linux-gnu - os: windows-latest + host: x86_64-pc-windows-msvc + - os: macos-latest host: x86_64-apple-darwin runs-on: ${{ matrix.os }} @@ -84,8 +74,17 @@ jobs: - name: Checkout uses: actions/checkout@v3 + - name: Install i686 dependencies + if: matrix.host == 'i686-unknown-linux-gnu' + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install gcc-multilib zlib1g-dev:i386 + - name: Install toolchain - run: rustup show active-toolchain + run: | + rustup set default-host ${{ matrix.host }} + rustup show active-toolchain # Run - name: Set LD_LIBRARY_PATH (Linux) @@ -109,11 +108,11 @@ jobs: run: cargo build --tests --features deny-warnings,internal - name: Test - if: runner.os == 'Linux' + if: matrix.host == 'x86_64-unknown-linux-gnu' run: cargo test --features deny-warnings,internal - name: Test - if: runner.os != 'Linux' + if: matrix.host != 'x86_64-unknown-linux-gnu' run: cargo test --features deny-warnings,internal -- --skip dogfood - name: Test clippy_lints diff --git a/CHANGELOG.md b/CHANGELOG.md index 92cc19edb0505..a7ae4a0ee2c09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5031,6 +5031,7 @@ Released 2018-09-13 [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero [`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections [`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items +[`iter_out_of_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_out_of_bounds [`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next [`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero @@ -5131,6 +5132,7 @@ Released 2018-09-13 [`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op [`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message +[`missing_asserts_for_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_asserts_for_indexing [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames @@ -5204,6 +5206,8 @@ Released 2018-09-13 [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding [`no_mangle_with_rust_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_mangle_with_rust_abi [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal +[`non_canonical_clone_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_canonical_clone_impl +[`non_canonical_partial_ord_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_canonical_partial_ord_impl [`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty @@ -5570,4 +5574,5 @@ Released 2018-09-13 [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings [`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments [`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates +[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow diff --git a/Cargo.toml b/Cargo.toml index 7c78beb5ddef9..2d8b590dbe311 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ tempfile = { version = "3.2", optional = true } termize = "0.1" [dev-dependencies] -ui_test = "0.18.1" +ui_test = "0.20" tester = "0.9" regex = "1.5" toml = "0.7.3" diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index daaefd06a97f3..b02457307d743 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -14,8 +14,11 @@ - [Basics](development/basics.md) - [Adding Lints](development/adding_lints.md) - [Defining Lints](development/defining_lints.md) + - [Writing tests](development/writing_tests.md) - [Lint Passes](development/lint_passes.md) + - [Emitting lints](development/emitting_lints.md) - [Type Checking](development/type_checking.md) + - [Trait Checking](development/trait_checking.md) - [Method Checking](development/method_checking.md) - [Macro Expansions](development/macro_expansions.md) - [Common Tools](development/common_tools_writing_lints.md) diff --git a/book/src/development/emitting_lints.md b/book/src/development/emitting_lints.md new file mode 100644 index 0000000000000..a12f6aa91b307 --- /dev/null +++ b/book/src/development/emitting_lints.md @@ -0,0 +1,217 @@ +# Emitting a lint + +Once we have [defined a lint](defining_lints.md), written [UI +tests](writing_tests.md) and chosen [the lint pass](lint_passes.md) for the lint, +we can begin the implementation of the lint logic so that we can emit it and +gradually work towards a lint that behaves as expected. + +Note that we will not go into concrete implementation of a lint logic in this +chapter. We will go into details in later chapters as well as in two examples of +real Clippy lints. + +To emit a lint, we must implement a pass (see [Lint Passes](lint_passes.md)) for +the lint that we have declared. In this example we'll implement a "late" lint, +so take a look at the [LateLintPass][late_lint_pass] documentation, which +provides an abundance of methods that we can implement for our lint. + +```rust +pub trait LateLintPass<'tcx>: LintPass { + // Trait methods +} +``` + +By far the most common method used for Clippy lints is [`check_expr` +method][late_check_expr], this is because Rust is an expression language and, +more often than not, the lint we want to work on must examine expressions. + +> _Note:_ If you don't fully understand what expressions are in Rust, take a +> look at the official documentation on [expressions][rust_expressions] + +Other common ones include the [`check_fn` method][late_check_fn] and the +[`check_item` method][late_check_item]. + +### Emitting a lint + +Inside the trait method that we implement, we can write down the lint logic and +emit the lint with suggestions. + +Clippy's [diagnostics] provides quite a few diagnostic functions that we can use +to emit lints. Take a look at the documentation to pick one that suits your +lint's needs the best. Some common ones you will encounter in the Clippy +repository includes: + +- [`span_lint`]: Emits a lint without providing any other information +- [`span_lint_and_note`]: Emits a lint and adds a note +- [`span_lint_and_help`]: Emits a lint and provides a helpful message +- [`span_lint_and_sugg`]: Emits a lint and provides a suggestion to fix the code +- [`span_lint_and_then`]: Like `span_lint`, but allows for a lot of output + customization. + +```rust +impl<'tcx> LateLintPass<'tcx> for LintName { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint + if some_lint_expr_logic(expr) { + span_lint_and_help( + cx, // < The context + LINT_NAME, // < The name of the lint in ALL CAPS + expr.span, // < The span to lint + "message on why the lint is emitted", + None, // < An optional help span (to highlight something in the lint) + "message that provides a helpful suggestion", + ); + } + } +} +``` + +> Note: The message should be matter of fact and avoid capitalization and +> punctuation. If multiple sentences are needed, the messages should probably be +> split up into an error + a help / note / suggestion message. + +## Suggestions: Automatic fixes + +Some lints know what to change in order to fix the code. For example, the lint +[`range_plus_one`][range_plus_one] warns for ranges where the user wrote `x..y + +1` instead of using an [inclusive range][inclusive_range] (`x..=y`). The fix to +this code would be changing the `x..y + 1` expression to `x..=y`. **This is +where suggestions come in**. + +A suggestion is a change that the lint provides to fix the issue it is linting. +The output looks something like this (from the example earlier): + +```text +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:37:14 + | +LL | for _ in 1..1 + 1 {} + | ^^^^^^^^ help: use: `1..=1` +``` + +**Not all suggestions are always right**, some of them require human +supervision, that's why we have [Applicability][applicability]. + +Applicability indicates confidence in the correctness of the suggestion, some +are always right (`Applicability::MachineApplicable`), but we use +`Applicability::MaybeIncorrect` and others when talking about a suggestion that +may be incorrect. + +### Example + +The same lint `LINT_NAME` but that emits a suggestion would look something like this: + +```rust +impl<'tcx> LateLintPass<'tcx> for LintName { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint + if some_lint_expr_logic(expr) { + span_lint_and_sugg( // < Note this change + cx, + LINT_NAME, + span, + "message on why the lint is emitted", + "use", + format!("foo + {} * bar", snippet(cx, expr.span, "")), // < Suggestion + Applicability::MachineApplicable, + ); + } + } +} +``` + +Suggestions generally use the [`format!`][format_macro] macro to interpolate the +old values with the new ones. To get code snippets, use one of the `snippet*` +functions from `clippy_utils::source`. + +## How to choose between notes, help messages and suggestions + +Notes are presented separately from the main lint message, they provide useful +information that the user needs to understand why the lint was activated. They +are the most helpful when attached to a span. + +Examples: + +### Notes + +```text +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/drop_forget_ref.rs:10:5 + | +10 | forget(&SomeStruct); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::forget-ref` implied by `-D warnings` +note: argument has type &SomeStruct + --> $DIR/drop_forget_ref.rs:10:12 + | +10 | forget(&SomeStruct); + | ^^^^^^^^^^^ +``` + +### Help Messages + +Help messages are specifically to help the user. These are used in situation +where you can't provide a specific machine applicable suggestion. They can also +be attached to a span. + +Example: + +```text +error: constant division of 0.0 with 0.0 will always result in NaN + --> $DIR/zero_div_zero.rs:6:25 + | +6 | let other_f64_nan = 0.0f64 / 0.0; + | ^^^^^^^^^^^^ + | + = help: consider using `f64::NAN` if you would like a constant representing NaN +``` + +### Suggestions + +Suggestions are the most helpful, they are changes to the source code to fix the +error. The magic in suggestions is that tools like `rustfix` can detect them and +automatically fix your code. + +Example: + +```text +error: This `.fold` can be more succinctly expressed as `.any` +--> $DIR/methods.rs:390:13 + | +390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)` + | +``` + +### Snippets + +Snippets are pieces of the source code (as a string), they are extracted +generally using the [`snippet`][snippet_fn] function. + +For example, if you want to know how an item looks (and you know the item's +span), you could use `snippet(cx, span, "..")`. + +## Final: Run UI Tests to Emit the Lint + +Now, if we run our [UI test](writing_tests.md), we should see that Clippy now +produces output that contains the lint message we designed. + +The next step is to implement the logic properly, which is a detail that we will +cover in the next chapters. + +[diagnostics]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/index.html +[late_check_expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_expr +[late_check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_fn +[late_check_item]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_item +[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html +[rust_expressions]: https://doc.rust-lang.org/reference/expressions.html +[`span_lint`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint.html +[`span_lint_and_note`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_note.html +[`span_lint_and_help`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_help.html +[`span_lint_and_sugg`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html +[`span_lint_and_then`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_then.html +[range_plus_one]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one +[inclusive_range]: https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html +[applicability]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_errors/enum.Applicability.html +[snippet_fn]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/source/fn.snippet.html +[format_macro]: https://doc.rust-lang.org/std/macro.format.html diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md new file mode 100644 index 0000000000000..fb263922cf88e --- /dev/null +++ b/book/src/development/trait_checking.md @@ -0,0 +1,105 @@ +# Trait Checking + +Besides [type checking](type_checking.md), we might want to examine if +a specific type `Ty` implements certain trait when implementing a lint. +There are three approaches to achieve this, depending on if the target trait +that we want to examine has a [diagnostic item][diagnostic_items], +[lang item][lang_items], or neither. + +## Using Diagnostic Items + +As explained in the [Rust Compiler Development Guide][rustc_dev_guide], diagnostic items +are introduced for identifying types via [Symbols][symbol]. + +For instance, if we want to examine whether an expression implements +the `Iterator` trait, we could simply write the following code, +providing the `LateContext` (`cx`), our expression at hand, and +the symbol of the trait in question: + +```rust +use clippy_utils::is_trait_method; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_span::symbol::sym; + +impl LateLintPass<'_> for CheckIteratorTraitLint { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let implements_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { + implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[]) + }); + if implements_iterator { + // [...] + } + + } +} +``` + +> **Note**: Refer to [this index][symbol_index] for all the defined `Symbol`s. + +## Using Lang Items + +Besides diagnostic items, we can also use [`lang_items`][lang_items]. +Take a look at the documentation to find that `LanguageItems` contains +all language items defined in the compiler. + +Using one of its `*_trait` method, we could obtain the [DefId] of any +specific item, such as `Clone`, `Copy`, `Drop`, `Eq`, which are familiar +to many Rustaceans. + +For instance, if we want to examine whether an expression `expr` implements +`Drop` trait, we could access `LanguageItems` via our `LateContext`'s +[TyCtxt], which provides a `lang_items` method that will return the id of +`Drop` trait to us. Then, by calling Clippy utils function `implements_trait` +we can check that the `Ty` of the `expr` implements the trait: + +```rust +use clippy_utils::implements_trait; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; + +impl LateLintPass<'_> for CheckDropTraitLint { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let ty = cx.typeck_results().expr_ty(expr); + if cx.tcx.lang_items() + .drop_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) { + println!("`expr` implements `Drop` trait!"); + } + } +} +``` + +## Using Type Path + +If neither diagnostic item nor a language item is available, we can use +[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait +implementation. + +> **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item. + +Below, we check if the given `expr` implements the `Iterator`'s trait method `cloned` : + +```rust +use clippy_utils::{match_trait_method, paths}; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; + +impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if match_trait_method(cx, expr, &paths::CORE_ITER_CLONED) { + println!("`expr` implements `CORE_ITER_CLONED` trait!"); + } + } +} +``` + +[DefId]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html +[diagnostic_items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html +[lang_items]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/lang_items/struct.LanguageItems.html +[paths]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/paths.rs +[rustc_dev_guide]: https://rustc-dev-guide.rust-lang.org/ +[symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html +[symbol_index]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/sym/index.html +[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html +[rust]: https://github.com/rust-lang/rust diff --git a/book/src/development/writing_tests.md b/book/src/development/writing_tests.md new file mode 100644 index 0000000000000..8937e0d8e9469 --- /dev/null +++ b/book/src/development/writing_tests.md @@ -0,0 +1,218 @@ +# Testing + +Developing lints for Clippy is a Test-Driven Development (TDD) process because +our first task before implementing any logic for a new lint is to write some test cases. + +## Develop Lints with Tests + +When we develop Clippy, we enter a complex and chaotic realm full of +programmatic issues, stylistic errors, illogical code and non-adherence to convention. +Tests are the first layer of order we can leverage to define when and where +we want a new lint to trigger or not. + +Moreover, writing tests first help Clippy developers to find a balance for +the first iteration of and further enhancements for a lint. +With test cases on our side, we will not have to worry about over-engineering +a lint on its first version nor missing out some obvious edge cases of the lint. +This approach empowers us to iteratively enhance each lint. + +## Clippy UI Tests + +We use **UI tests** for testing in Clippy. These UI tests check that the output +of Clippy is exactly as we expect it to be. Each test is just a plain Rust file +that contains the code we want to check. + +The output of Clippy is compared against a `.stderr` file. Note that you don't +have to create this file yourself. We'll get to generating the `.stderr` files +with the command [`cargo bless`](#cargo-bless) (seen later on). + +### Write Test Cases + +Let us now think about some tests for our imaginary `foo_functions` lint. We +start by opening the test file `tests/ui/foo_functions.rs` that was created by +`cargo dev new_lint`. + +Update the file with some examples to get started: + +```rust +#![warn(clippy::foo_functions)] // < Add this, so the lint is guaranteed to be enabled in this file + +// Impl methods +struct A; +impl A { + pub fn fo(&self) {} + pub fn foo(&self) {} //~ ERROR: function called "foo" + pub fn food(&self) {} +} + +// Default trait methods +trait B { + fn fo(&self) {} + fn foo(&self) {} //~ ERROR: function called "foo" + fn food(&self) {} +} + +// Plain functions +fn fo() {} +fn foo() {} //~ ERROR: function called "foo" +fn food() {} + +fn main() { + // We also don't want to lint method calls + foo(); + let a = A; + a.foo(); +} +``` + +Without actual lint logic to emit the lint when we see a `foo` function name, +this test will just pass, because no lint will be emitted. However, we can now +run the test with the following command: + +```sh +$ TESTNAME=foo_functions cargo uitest +``` + +Clippy will compile and it will conclude with an `ok` for the tests: + +``` +...Clippy warnings and test outputs... +test compile_test ... ok +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s +``` + +This is normal. After all, we wrote a bunch of Rust code but we haven't really +implemented any logic for Clippy to detect `foo` functions and emit a lint. + +As we gradually implement our lint logic, we will keep running this UI test command. +Clippy will begin outputting information that allows us to check if the output is +turning into what we want it to be. + +### Example output + +As our `foo_functions` lint is tested, the output would look something like this: + +``` +failures: +---- compile_test stdout ---- +normalized stderr: +error: function called "foo" + --> $DIR/foo_functions.rs:6:12 + | +LL | pub fn foo(&self) {} + | ^^^ + | + = note: `-D clippy::foo-functions` implied by `-D warnings` +error: function called "foo" + --> $DIR/foo_functions.rs:13:8 + | +LL | fn foo(&self) {} + | ^^^ +error: function called "foo" + --> $DIR/foo_functions.rs:19:4 + | +LL | fn foo() {} + | ^^^ +error: aborting due to 3 previous errors +``` + +Note the *failures* label at the top of the fragment, we'll get rid of it +(saving this output) in the next section. + +> _Note:_ You can run multiple test files by specifying a comma separated list: +> `TESTNAME=foo_functions,bar_methods,baz_structs`. + +### `cargo bless` + +Once we are satisfied with the output, we need to run this command to +generate or update the `.stderr` file for our lint: + +```sh +$ TESTNAME=foo_functions cargo uibless +``` + +This writes the emitted lint suggestions and fixes to the `.stderr` file, with +the reason for the lint, suggested fixes, and line numbers, etc. + +Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit +our lint, we need to commit the generated `.stderr` files, too. + +In general, you should only commit files changed by `cargo bless` for the +specific lint you are creating/editing. + +> _Note:_ If the generated `.stderr`, and `.fixed` files are empty, +> they should be removed. + +## `toml` Tests + +Some lints can be configured through a `clippy.toml` file. Those configuration +values are tested in `tests/ui-toml`. + +To add a new test there, create a new directory and add the files: + +- `clippy.toml`: Put here the configuration value you want to test. +- `lint_name.rs`: A test file where you put the testing code, that should see a + different lint behavior according to the configuration set in the + `clippy.toml` file. + +The potential `.stderr` and `.fixed` files can again be generated with `cargo +bless`. + +## Cargo Lints + +The process of testing is different for Cargo lints in that now we are +interested in the `Cargo.toml` manifest file. In this case, we also need a +minimal crate associated with that manifest. Those tests are generated in +`tests/ui-cargo`. + +Imagine we have a new example lint that is named `foo_categories`, we can run: + +```sh +$ cargo dev new_lint --name=foo_categories --pass=late --category=cargo +``` + +After running `cargo dev new_lint` we will find by default two new crates, +each with its manifest file: + +* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the + new lint to raise an error. +* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger + the lint. + +If you need more cases, you can copy one of those crates (under +`foo_categories`) and rename it. + +The process of generating the `.stderr` file is the same as for other lints +and prepending the `TESTNAME` variable to `cargo uitest` works for Cargo lints too. + +## Rustfix Tests + +If the lint you are working on is making use of structured suggestions, +[`rustfix`] will apply the suggestions from the lint to the test file code and +compare that to the contents of a `.fixed` file. + +Structured suggestions tell a user how to fix or re-write certain code that has +been linted with [`span_lint_and_sugg`]. + +Should `span_lint_and_sugg` be used to generate a suggestion, but not all +suggestions lead to valid code, you can use the `//@no-rustfix` comment on top +of the test file, to not run `rustfix` on that file. + +We'll talk about suggestions more in depth in a [later chapter](emitting_lints.md). + +Use `cargo bless` to automatically generate the `.fixed` file after running +the tests. + +[`rustfix`]: https://github.com/rust-lang/rustfix +[`span_lint_and_sugg`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html + +## Testing Manually + +Manually testing against an example file can be useful if you have added some +`println!`s and the test suite output becomes unreadable. + +To try Clippy with your local modifications, run from the working copy root. + +```sh +$ cargo dev lint input.rs +``` diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index caaad6d11736f..52c795e04fe95 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -751,3 +751,27 @@ Which crates to allow absolute paths from * [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths) +## `enforce-iter-loop-reborrow` +#### Example +``` +let mut vec = vec![1, 2, 3]; +let rmvec = &mut vec; +for _ in rmvec.iter() {} +for _ in rmvec.iter_mut() {} +``` + +Use instead: +``` +let mut vec = vec![1, 2, 3]; +let rmvec = &mut vec; +for _ in &*rmvec {} +for _ in &mut *rmvec {} +``` + +**Default Value:** `false` (`bool`) + +--- +**Affected lints:** +* [`explicit_iter_loop`](https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop) + + diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index f73f1ed18f8b6..a4d40df52e7bc 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -211,8 +211,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO, crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, - crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO, - crate::incorrect_impls::INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE_INFO, crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO, crate::indexing_slicing::INDEXING_SLICING_INFO, crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO, @@ -365,6 +363,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::ITER_NTH_ZERO_INFO, crate::methods::ITER_ON_EMPTY_COLLECTIONS_INFO, crate::methods::ITER_ON_SINGLE_ITEMS_INFO, + crate::methods::ITER_OUT_OF_BOUNDS_INFO, crate::methods::ITER_OVEREAGER_CLONED_INFO, crate::methods::ITER_SKIP_NEXT_INFO, crate::methods::ITER_SKIP_ZERO_INFO, @@ -456,6 +455,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::misc_early::ZERO_PREFIXED_LITERAL_INFO, crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO, crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO, + crate::missing_asserts_for_indexing::MISSING_ASSERTS_FOR_INDEXING_INFO, crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO, crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO, crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO, @@ -496,6 +496,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO, crate::no_effect::UNNECESSARY_OPERATION_INFO, crate::no_mangle_with_rust_abi::NO_MANGLE_WITH_RUST_ABI_INFO, + crate::non_canonical_impls::NON_CANONICAL_CLONE_IMPL_INFO, + crate::non_canonical_impls::NON_CANONICAL_PARTIAL_ORD_IMPL_INFO, crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO, crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO, crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO, diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 03b5a2d6d0829..bbce6e1dd8f2b 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -69,6 +69,9 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { } /// Returns true if the given item is a union with at least two non-ZST fields. +/// (ZST fields having an arbitrary offset is completely inconsequential, and +/// if there is only one field left after ignoring ZST fields then the offset +/// of that field does not matter either.) fn is_union_with_two_non_zst_fields(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if let ItemKind::Union(data, _) = &item.kind { data.fields().iter().filter(|f| !is_zst(cx, f.ty)).count() >= 2 diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 58c2785500013..8090f821d1ff2 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -3,7 +3,7 @@ use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exact use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{is_copy, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, is_copy, peel_mid_ty_refs}; use clippy_utils::{ expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, }; @@ -33,7 +33,6 @@ use rustc_middle::ty::{ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{Obligation, ObligationCause}; use std::collections::VecDeque; @@ -452,13 +451,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // Trait methods taking `self` arg_ty } && impl_ty.is_ref() - && cx.tcx.infer_ctxt().build() - .type_implements_trait( - trait_id, - [impl_ty.into()].into_iter().chain(args.iter().copied()), - cx.param_env, - ) - .must_apply_modulo_regions() + && implements_trait( + cx, + impl_ty, + trait_id, + &args[..cx.tcx.generics_of(trait_id).params.len() - 1], + ) { false } else { @@ -609,12 +607,14 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { adjusted_ty, }, )); - } else if stability.is_deref_stable() { + } else if stability.is_deref_stable() + && let Some(parent) = get_parent_expr(cx, expr) + { self.state = Some(( State::ExplicitDeref { mutability: None }, StateData { - span: expr.span, - hir_id: expr.hir_id, + span: parent.span, + hir_id: parent.hir_id, adjusted_ty, }, )); @@ -1399,6 +1399,13 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data return; } + if let ExprKind::Field(parent_expr, _) = expr.kind + && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(parent_expr).kind() + && adt.is_union() + { + // Auto deref does not apply on union field + return; + } span_lint_hir_and_then( cx, EXPLICIT_AUTO_DEREF, diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 9a85cc4ce2dc0..d2bfc4f8e273d 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -217,8 +217,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind(); if let attrs = cx.tcx.hir().attrs(item.hir_id()); if !attrs.iter().any(|attr| attr.doc_str().is_some()); - if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); - if !child_attrs.iter().any(|attr| attr.doc_str().is_some()); + if cx.tcx.hir().attrs(impl_item_hir).is_empty(); then { if adt_def.is_struct() { diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 1971cab64ef38..7469f813ef87f 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::macro_backtrace; +use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefIdMap; -use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; +use rustc_hir::{Expr, ExprKind, 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}; @@ -111,6 +112,10 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { self.check(cx, expr.span); + // `$t + $t` can have the context of $t, check also the span of the binary operator + if let ExprKind::Binary(op, ..) = expr.kind { + self.check(cx, op.span); + } } fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { @@ -147,4 +152,8 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) { self.check(cx, path.span); } + + fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { + self.check(cx, attr.span); + } } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index d8f2fbcea0027..4498e9ccdec25 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -472,7 +472,7 @@ struct DocHeaders { fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[Attribute]) -> Option { /// We don't want the parser to choke on intra doc links. Since we don't - /// actually care about rendering them, just pretend that all broken links are + /// actually care about rendering them, just pretend that all broken links /// point to a fake address. #[expect(clippy::unnecessary_wraps)] // we're following a type signature fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> { diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index ab6ad3f3b3ab0..e2d19e2455700 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -6,7 +6,7 @@ use clippy_utils::sugg::Sugg; use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && then_expr.span.ctxt() == ctxt && 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) + && !contains_return(then_block.stmts) { let mut app = Applicability::Unspecified; let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app).maybe_par().to_string(); @@ -116,17 +116,3 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { extract_msrv_attr!(LateContext); } - -fn stmts_contains_early_return(stmts: &[Stmt<'_>]) -> bool { - stmts.iter().any(|stmt| { - let Stmt { - kind: StmtKind::Semi(e), - .. - } = stmt - else { - return false; - }; - - contains_return(e) - }) -} diff --git a/clippy_lints/src/ignored_unit_patterns.rs b/clippy_lints/src/ignored_unit_patterns.rs index c635120b88245..d8ead1c9d9f68 100644 --- a/clippy_lints/src/ignored_unit_patterns.rs +++ b/clippy_lints/src/ignored_unit_patterns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use hir::PatKind; +use hir::{Node, PatKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -37,6 +37,17 @@ declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]); impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) { + match cx.tcx.hir().get_parent(pat.hir_id) { + Node::Param(param) if matches!(cx.tcx.hir().get_parent(param.hir_id), Node::Item(_)) => { + // Ignore function parameters + return; + }, + Node::Local(local) if local.ty.is_some() => { + // Ignore let bindings with explicit type + return; + }, + _ => {}, + } if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).is_unit() { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs index c6d1acad3a0f1..ec9044bba5cc3 100644 --- a/clippy_lints/src/implied_bounds_in_impls.rs +++ b/clippy_lints/src/implied_bounds_in_impls.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_errors::{Applicability, SuggestionStyle}; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem, @@ -9,7 +9,7 @@ use rustc_hir::{ }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, ClauseKind, TyCtxt}; +use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -45,52 +45,169 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.73.0"] pub IMPLIED_BOUNDS_IN_IMPLS, - complexity, + nursery, "specifying bounds that are implied by other bounds in `impl Trait` type" } declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]); -/// This function tries to, for all type parameters in a supertype predicate `GenericTrait`, -/// check if the substituted type in the implied-by bound matches with what's subtituted in the -/// implied type. +#[allow(clippy::too_many_arguments)] +fn emit_lint( + cx: &LateContext<'_>, + poly_trait: &rustc_hir::PolyTraitRef<'_>, + opaque_ty: &rustc_hir::OpaqueTy<'_>, + index: usize, + // The bindings that were implied + implied_bindings: &[rustc_hir::TypeBinding<'_>], + // The original bindings that `implied_bindings` are implied from + implied_by_bindings: &[rustc_hir::TypeBinding<'_>], + implied_by_args: &[GenericArg<'_>], + implied_by_span: Span, +) { + let implied_by = snippet(cx, implied_by_span, ".."); + + span_lint_and_then( + cx, + IMPLIED_BOUNDS_IN_IMPLS, + poly_trait.span, + &format!("this bound is already specified as the supertrait of `{implied_by}`"), + |diag| { + // If we suggest removing a bound, we may also need to extend the span + // to include the `+` token that is ahead or behind, + // so we don't end up with something like `impl + B` or `impl A + ` + + let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) { + poly_trait.span.to(next_bound.span().shrink_to_lo()) + } else if index > 0 + && let Some(prev_bound) = opaque_ty.bounds.get(index - 1) + { + prev_bound.span().shrink_to_hi().to(poly_trait.span.shrink_to_hi()) + } else { + poly_trait.span + }; + + let mut sugg = vec![(implied_span_extended, String::new())]; + + // We also might need to include associated type binding that were specified in the implied bound, + // but omitted in the implied-by bound: + // `fn f() -> impl Deref + DerefMut` + // If we're going to suggest removing `Deref<..>`, we'll need to put `` on `DerefMut` + let omitted_assoc_tys: Vec<_> = implied_bindings + .iter() + .filter(|binding| !implied_by_bindings.iter().any(|b| b.ident == binding.ident)) + .collect(); + + if !omitted_assoc_tys.is_empty() { + // `<>` needs to be added if there aren't yet any generic arguments or bindings + let needs_angle_brackets = implied_by_args.is_empty() && implied_by_bindings.is_empty(); + let insert_span = match (implied_by_args, implied_by_bindings) { + ([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(), + ([.., arg], []) => arg.span().shrink_to_hi(), + ([], [.., binding]) => binding.span.shrink_to_hi(), + ([], []) => implied_by_span.shrink_to_hi(), + }; + + let mut associated_tys_sugg = if needs_angle_brackets { + "<".to_owned() + } else { + // If angle brackets aren't needed (i.e., there are already generic arguments or bindings), + // we need to add a comma: + // `impl A` + // ^ if we insert `Assoc=i32` without a comma here, that'd be invalid syntax: + // `impl A` + ", ".to_owned() + }; + + for (index, binding) in omitted_assoc_tys.into_iter().enumerate() { + if index > 0 { + associated_tys_sugg += ", "; + } + associated_tys_sugg += &snippet(cx, binding.span, ".."); + } + if needs_angle_brackets { + associated_tys_sugg += ">"; + } + sugg.push((insert_span, associated_tys_sugg)); + } + + diag.multipart_suggestion_with_style( + "try removing this bound", + sugg, + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + }, + ); +} + +/// Tries to "resolve" a type. +/// The index passed to this function must start with `Self=0`, i.e. it must be a valid +/// type parameter index. +/// If the index is out of bounds, it means that the generic parameter has a default type. +fn try_resolve_type<'tcx>( + tcx: TyCtxt<'tcx>, + args: &'tcx [GenericArg<'tcx>], + generics: &'tcx Generics, + index: usize, +) -> Option> { + match args.get(index - 1) { + Some(GenericArg::Type(ty)) => Some(hir_ty_to_ty(tcx, ty)), + Some(_) => None, + None => Some(tcx.type_of(generics.params[index].def_id).skip_binder()), + } +} + +/// This function tries to, for all generic type parameters in a supertrait predicate `trait ...: +/// GenericTrait`, check if the substituted type in the implied-by bound matches with what's +/// subtituted in the implied bound. /// /// Consider this example. /// ```rust,ignore /// trait GenericTrait {} /// trait GenericSubTrait: GenericTrait {} -/// ^ trait_predicate_args: [Self#0, U#2] +/// ^^^^^^^^^^^^^^^ trait_predicate_args: [Self#0, U#2] +/// (the Self#0 is implicit: `>`) /// impl GenericTrait for () {} /// impl GenericSubTrait<(), i32, ()> for () {} -/// impl GenericSubTrait<(), [u8; 8], ()> for () {} +/// impl GenericSubTrait<(), i64, ()> for () {} /// -/// fn f() -> impl GenericTrait + GenericSubTrait<(), [u8; 8], ()> { -/// ^^^ implied_args ^^^^^^^^^^^^^^^ implied_by_args -/// (we are interested in `[u8; 8]` specifically, as that -/// is what `U` in `GenericTrait` is substituted with) -/// () +/// fn f() -> impl GenericTrait + GenericSubTrait<(), i64, ()> { +/// ^^^ implied_args ^^^^^^^^^^^ implied_by_args +/// (we are interested in `i64` specifically, as that +/// is what `U` in `GenericTrait` is substituted with) /// } /// ``` -/// Here i32 != [u8; 8], so this will return false. -fn is_same_generics( - tcx: TyCtxt<'_>, - trait_predicate_args: &[ty::GenericArg<'_>], - implied_by_args: &[GenericArg<'_>], - implied_args: &[GenericArg<'_>], +/// Here i32 != i64, so this will return false. +fn is_same_generics<'tcx>( + tcx: TyCtxt<'tcx>, + trait_predicate_args: &'tcx [ty::GenericArg<'tcx>], + implied_by_args: &'tcx [GenericArg<'tcx>], + implied_args: &'tcx [GenericArg<'tcx>], + implied_by_def_id: DefId, + implied_def_id: DefId, ) -> bool { + // Get the generics of the two traits to be able to get default generic parameter. + let implied_by_generics = tcx.generics_of(implied_by_def_id); + let implied_generics = tcx.generics_of(implied_def_id); + trait_predicate_args .iter() .enumerate() .skip(1) // skip `Self` implicit arg .all(|(arg_index, arg)| { - if let Some(ty) = arg.as_type() - && let &ty::Param(ty::ParamTy{ index, .. }) = ty.kind() - // Since `trait_predicate_args` and type params in traits start with `Self=0` - // and generic argument lists `GenericTrait` don't have `Self`, - // we need to subtract 1 from the index. - && let GenericArg::Type(ty_a) = implied_by_args[index as usize - 1] - && let GenericArg::Type(ty_b) = implied_args[arg_index - 1] - { - hir_ty_to_ty(tcx, ty_a) == hir_ty_to_ty(tcx, ty_b) + if let Some(ty) = arg.as_type() { + if let &ty::Param(ty::ParamTy { index, .. }) = ty.kind() + // `index == 0` means that it's referring to `Self`, + // in which case we don't try to substitute it + && index != 0 + && let Some(ty_a) = try_resolve_type(tcx, implied_by_args, implied_by_generics, index as usize) + && let Some(ty_b) = try_resolve_type(tcx, implied_args, implied_generics, arg_index) + { + ty_a == ty_b + } else if let Some(ty_b) = try_resolve_type(tcx, implied_args, implied_generics, arg_index) { + ty == ty_b + } else { + false + } } else { false } @@ -121,7 +238,7 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) { && let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates && !predicates.is_empty() // If the trait has no supertrait, there is nothing to add. { - Some((bound.span(), path.args.map_or([].as_slice(), |a| a.args), predicates)) + Some((bound.span(), path, predicates, trait_def_id)) } else { None } @@ -134,43 +251,42 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) { if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound && let [.., path] = poly_trait.trait_ref.path.segments && let implied_args = path.args.map_or([].as_slice(), |a| a.args) + && let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings) && let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id() - && let Some(implied_by_span) = implied_bounds.iter().find_map(|&(span, implied_by_args, preds)| { - preds.iter().find_map(|(clause, _)| { - if let ClauseKind::Trait(tr) = clause.kind().skip_binder() - && tr.def_id() == def_id - && is_same_generics(cx.tcx, tr.trait_ref.args, implied_by_args, implied_args) - { - Some(span) - } else { - None - } + && let Some((implied_by_span, implied_by_args, implied_by_bindings)) = implied_bounds + .iter() + .find_map(|&(span, implied_by_path, preds, implied_by_def_id)| { + let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args); + let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings); + + preds.iter().find_map(|(clause, _)| { + if let ClauseKind::Trait(tr) = clause.kind().skip_binder() + && tr.def_id() == def_id + && is_same_generics( + cx.tcx, + tr.trait_ref.args, + implied_by_args, + implied_args, + implied_by_def_id, + def_id, + ) + { + Some((span, implied_by_args, implied_by_bindings)) + } else { + None + } + }) }) - }) { - let implied_by = snippet(cx, implied_by_span, ".."); - span_lint_and_then( - cx, IMPLIED_BOUNDS_IN_IMPLS, - poly_trait.span, - &format!("this bound is already specified as the supertrait of `{implied_by}`"), - |diag| { - // If we suggest removing a bound, we may also need extend the span - // to include the `+` token, so we don't end up with something like `impl + B` - - let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) { - poly_trait.span.to(next_bound.span().shrink_to_lo()) - } else { - poly_trait.span - }; - - diag.span_suggestion_with_style( - implied_span_extended, - "try removing this bound", - "", - Applicability::MachineApplicable, - SuggestionStyle::ShowAlways - ); - } + emit_lint( + cx, + poly_trait, + opaque_ty, + index, + implied_bindings, + implied_by_bindings, + implied_by_args, + implied_by_span ); } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 20fdf26dc521b..f52614b62088f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -156,7 +156,6 @@ mod implicit_saturating_add; mod implicit_saturating_sub; mod implied_bounds_in_impls; mod inconsistent_struct_constructor; -mod incorrect_impls; mod index_refutable_slice; mod indexing_slicing; mod infinite_iter; @@ -212,6 +211,7 @@ mod misc; mod misc_early; mod mismatching_type_param_order; mod missing_assert_message; +mod missing_asserts_for_indexing; mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; @@ -245,6 +245,7 @@ mod neg_multiply; mod new_without_default; mod no_effect; mod no_mangle_with_rust_abi; +mod non_canonical_impls; mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; @@ -697,7 +698,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); - store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv()))); + let enforce_iter_loop_reborrow = conf.enforce_iter_loop_reborrow; + store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv(), enforce_iter_loop_reborrow))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); store.register_late_pass(|_| Box::new(entry::HashMapPass)); @@ -1070,7 +1072,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: avoid_breaking_exported_api, )) }); - store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls)); + store.register_late_pass(|_| Box::new(non_canonical_impls::NonCanonicalImpls)); store.register_late_pass(move |_| { Box::new(single_call_fn::SingleCallFn { avoid_breaking_exported_api, @@ -1101,6 +1103,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)); + store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 7b8c88235a97d..6ab256ef0010b 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -13,8 +13,14 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMut use rustc_middle::ty::{self, EarlyBinder, Ty, TypeAndMut}; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>, msrv: &Msrv) { - let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr) else { +pub(super) fn check( + cx: &LateContext<'_>, + self_arg: &Expr<'_>, + call_expr: &Expr<'_>, + msrv: &Msrv, + enforce_iter_loop_reborrow: bool, +) { + let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow) else { return; }; if let ty::Array(_, count) = *ty.peel_refs().kind() { @@ -102,6 +108,7 @@ fn is_ref_iterable<'tcx>( cx: &LateContext<'tcx>, self_arg: &Expr<'_>, call_expr: &Expr<'_>, + enforce_iter_loop_reborrow: bool, ) -> Option<(AdjustKind, Ty<'tcx>)> { let typeck = cx.typeck_results(); if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) @@ -142,7 +149,8 @@ fn is_ref_iterable<'tcx>( { return Some((AdjustKind::None, self_ty)); } - } else if let ty::Ref(region, ty, Mutability::Mut) = *self_ty.kind() + } else if enforce_iter_loop_reborrow + && let ty::Ref(region, ty, Mutability::Mut) = *self_ty.kind() && let Some(mutbl) = mutbl { // Attempt to reborrow the mutable reference @@ -186,7 +194,8 @@ fn is_ref_iterable<'tcx>( }, .. ] => { - if target != self_ty + if enforce_iter_loop_reborrow + && target != self_ty && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target]) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index ffd29ab763027..1fb16adad7a19 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -609,10 +609,14 @@ declare_clippy_lint! { pub struct Loops { msrv: Msrv, + enforce_iter_loop_reborrow: bool, } impl Loops { - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(msrv: Msrv, enforce_iter_loop_reborrow: bool) -> Self { + Self { + msrv, + enforce_iter_loop_reborrow, + } } } impl_lint_pass!(Loops => [ @@ -719,7 +723,7 @@ impl Loops { if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind { match method.ident.as_str() { "iter" | "iter_mut" => { - explicit_iter_loop::check(cx, self_arg, arg, &self.msrv); + explicit_iter_loop::check(cx, self_arg, arg, &self.msrv, self.enforce_iter_loop_reborrow); }, "into_iter" => { explicit_into_iter_loop::check(cx, self_arg, arg); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index cc19ac55e5e44..3d8a4cd948afc 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -1,13 +1,13 @@ use super::utils::make_iterator_snippet; use super::NEVER_LOOP; -use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; +use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{sym, Span}; use std::iter::{once, Iterator}; pub(super) fn check<'tcx>( @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( for_loop: Option<&ForLoop<'_>>, ) { match never_loop_block(cx, block, &mut Vec::new(), loop_id) { - NeverLoopResult::AlwaysBreak => { + NeverLoopResult::Diverging => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { arg: iterator, @@ -39,67 +39,76 @@ pub(super) fn check<'tcx>( } }); }, - NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (), - NeverLoopResult::IgnoreUntilEnd(_) => unreachable!(), + NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Normal => (), } } +/// The `never_loop` analysis keeps track of three things: +/// +/// * Has any (reachable) code path hit a `continue` of the main loop? +/// * Is the current code path diverging (that is, the next expression is not reachable) +/// * For each block label `'a` inside the main loop, has any (reachable) code path encountered a +/// `break 'a`? +/// +/// The first two bits of information are in this enum, and the last part is in the +/// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by +/// scope. #[derive(Copy, Clone)] enum NeverLoopResult { - // A break/return always get triggered but not necessarily for the main loop. - AlwaysBreak, - // A continue may occur for the main loop. + /// A continue may occur for the main loop. MayContinueMainLoop, - // Ignore everything until the end of the block with this id - IgnoreUntilEnd(HirId), - Otherwise, + /// We have not encountered any main loop continue, + /// but we are diverging (subsequent control flow is not reachable) + Diverging, + /// We have not encountered any main loop continue, + /// and subsequent control flow is (possibly) reachable + Normal, } #[must_use] fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult { match arg { - NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise, + NeverLoopResult::Diverging | NeverLoopResult::Normal => NeverLoopResult::Normal, NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, - NeverLoopResult::IgnoreUntilEnd(id) => NeverLoopResult::IgnoreUntilEnd(id), } } // Combine two results for parts that are called in order. #[must_use] -fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult { +fn combine_seq(first: NeverLoopResult, second: impl FnOnce() -> NeverLoopResult) -> NeverLoopResult { match first { - NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop | NeverLoopResult::IgnoreUntilEnd(_) => { - first - }, - NeverLoopResult::Otherwise => second, + NeverLoopResult::Diverging | NeverLoopResult::MayContinueMainLoop => first, + NeverLoopResult::Normal => second(), + } +} + +// Combine an iterator of results for parts that are called in order. +#[must_use] +fn combine_seq_many(iter: impl IntoIterator) -> NeverLoopResult { + for e in iter { + if let NeverLoopResult::Diverging | NeverLoopResult::MayContinueMainLoop = e { + return e; + } } + NeverLoopResult::Normal } // Combine two results where only one of the part may have been executed. #[must_use] -fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult, ignore_ids: &[HirId]) -> NeverLoopResult { +fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult { match (b1, b2) { - (NeverLoopResult::IgnoreUntilEnd(a), NeverLoopResult::IgnoreUntilEnd(b)) => { - if ignore_ids.iter().find(|&e| e == &a || e == &b).unwrap() == &a { - NeverLoopResult::IgnoreUntilEnd(b) - } else { - NeverLoopResult::IgnoreUntilEnd(a) - } - }, - (i @ NeverLoopResult::IgnoreUntilEnd(_), NeverLoopResult::AlwaysBreak) - | (NeverLoopResult::AlwaysBreak, i @ NeverLoopResult::IgnoreUntilEnd(_)) => i, - (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak, (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => { NeverLoopResult::MayContinueMainLoop }, - (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise, + (NeverLoopResult::Normal, _) | (_, NeverLoopResult::Normal) => NeverLoopResult::Normal, + (NeverLoopResult::Diverging, NeverLoopResult::Diverging) => NeverLoopResult::Diverging, } } fn never_loop_block<'tcx>( cx: &LateContext<'tcx>, block: &Block<'tcx>, - ignore_ids: &mut Vec, + local_labels: &mut Vec<(HirId, bool)>, main_loop_id: HirId, ) -> NeverLoopResult { let iter = block @@ -107,15 +116,21 @@ fn never_loop_block<'tcx>( .iter() .filter_map(stmt_to_expr) .chain(block.expr.map(|expr| (expr, None))); - - iter.map(|(e, els)| { - let e = never_loop_expr(cx, e, ignore_ids, main_loop_id); + combine_seq_many(iter.map(|(e, els)| { + let e = never_loop_expr(cx, e, local_labels, main_loop_id); // els is an else block in a let...else binding els.map_or(e, |els| { - combine_branches(e, never_loop_block(cx, els, ignore_ids, main_loop_id), ignore_ids) + combine_seq(e, || match never_loop_block(cx, els, local_labels, main_loop_id) { + // Returning MayContinueMainLoop here means that + // we will not evaluate the rest of the body + NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, + // An else block always diverges, so the Normal case should not happen, + // but the analysis is approximate so it might return Normal anyway. + // Returning Normal here says that nothing more happens on the main path + NeverLoopResult::Diverging | NeverLoopResult::Normal => NeverLoopResult::Normal, + }) }) - }) - .fold(NeverLoopResult::Otherwise, combine_seq) + })) } fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> { @@ -131,76 +146,69 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t fn never_loop_expr<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, - ignore_ids: &mut Vec, + local_labels: &mut Vec<(HirId, bool)>, main_loop_id: HirId, ) -> NeverLoopResult { - match expr.kind { + let result = match expr.kind { ExprKind::Unary(_, e) | ExprKind::Cast(e, _) | ExprKind::Type(e, _) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Repeat(e, _) - | ExprKind::DropTemps(e) => never_loop_expr(cx, e, ignore_ids, main_loop_id), - ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, ignore_ids, main_loop_id), - ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, &mut es.iter(), ignore_ids, main_loop_id), + | ExprKind::DropTemps(e) => never_loop_expr(cx, e, local_labels, main_loop_id), + ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, local_labels, main_loop_id), + ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, es.iter(), local_labels, main_loop_id), ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all( cx, - &mut std::iter::once(receiver).chain(es.iter()), - ignore_ids, + std::iter::once(receiver).chain(es.iter()), + local_labels, main_loop_id, ), ExprKind::Struct(_, fields, base) => { - let fields = never_loop_expr_all(cx, &mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id); + let fields = never_loop_expr_all(cx, fields.iter().map(|f| f.expr), local_labels, main_loop_id); if let Some(base) = base { - combine_seq(fields, never_loop_expr(cx, base, ignore_ids, main_loop_id)) + combine_seq(fields, || never_loop_expr(cx, base, local_labels, main_loop_id)) } else { fields } }, - ExprKind::Call(e, es) => never_loop_expr_all(cx, &mut once(e).chain(es.iter()), ignore_ids, main_loop_id), + ExprKind::Call(e, es) => never_loop_expr_all(cx, once(e).chain(es.iter()), local_labels, main_loop_id), ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) - | ExprKind::Index(e1, e2, _) => { - never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id) - }, + | ExprKind::Index(e1, e2, _) => never_loop_expr_all(cx, [e1, e2].iter().copied(), local_labels, main_loop_id), ExprKind::Loop(b, _, _, _) => { - // Break can come from the inner loop so remove them. - absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id)) + // We don't attempt to track reachability after a loop, + // just assume there may have been a break somewhere + absorb_break(never_loop_block(cx, b, local_labels, main_loop_id)) }, ExprKind::If(e, e2, e3) => { - let e1 = never_loop_expr(cx, e, ignore_ids, main_loop_id); - let e2 = never_loop_expr(cx, e2, ignore_ids, main_loop_id); - // If we know the `if` condition evaluates to `true`, don't check everything past it; it - // should just return whatever's evaluated for `e1` and `e2` since `e3` is unreachable - if let Some(Constant::Bool(true)) = constant(cx, cx.typeck_results(), e) { - return combine_seq(e1, e2); - } - let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| { - never_loop_expr(cx, e, ignore_ids, main_loop_id) - }); - combine_seq(e1, combine_branches(e2, e3, ignore_ids)) + let e1 = never_loop_expr(cx, e, local_labels, main_loop_id); + combine_seq(e1, || { + let e2 = never_loop_expr(cx, e2, local_labels, main_loop_id); + let e3 = e3.as_ref().map_or(NeverLoopResult::Normal, |e| { + never_loop_expr(cx, e, local_labels, main_loop_id) + }); + combine_branches(e2, e3) + }) }, ExprKind::Match(e, arms, _) => { - let e = never_loop_expr(cx, e, ignore_ids, main_loop_id); - if arms.is_empty() { - e - } else { - let arms = never_loop_expr_branch(cx, &mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id); - combine_seq(e, arms) - } + let e = never_loop_expr(cx, e, local_labels, main_loop_id); + combine_seq(e, || { + arms.iter().fold(NeverLoopResult::Diverging, |a, b| { + combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)) + }) + }) }, ExprKind::Block(b, l) => { if l.is_some() { - ignore_ids.push(b.hir_id); - } - let ret = never_loop_block(cx, b, ignore_ids, main_loop_id); - if l.is_some() { - ignore_ids.pop(); + local_labels.push((b.hir_id, false)); } + let ret = never_loop_block(cx, b, local_labels, main_loop_id); + let jumped_to = l.is_some() && local_labels.pop().unwrap().1; match ret { - NeverLoopResult::IgnoreUntilEnd(a) if a == b.hir_id => NeverLoopResult::Otherwise, + NeverLoopResult::Diverging if jumped_to => NeverLoopResult::Normal, _ => ret, } }, @@ -211,74 +219,78 @@ fn never_loop_expr<'tcx>( if id == main_loop_id { NeverLoopResult::MayContinueMainLoop } else { - NeverLoopResult::AlwaysBreak + NeverLoopResult::Diverging } }, - // checks if break targets a block instead of a loop - ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e - .map_or(NeverLoopResult::IgnoreUntilEnd(t), |e| { - never_loop_expr(cx, e, ignore_ids, main_loop_id) - }), - ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { - combine_seq( - never_loop_expr(cx, e, ignore_ids, main_loop_id), - NeverLoopResult::AlwaysBreak, - ) - }), - ExprKind::Become(e) => combine_seq( - never_loop_expr(cx, e, ignore_ids, main_loop_id), - NeverLoopResult::AlwaysBreak, - ), - ExprKind::InlineAsm(asm) => asm - .operands - .iter() - .map(|(o, _)| match o { - InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { - never_loop_expr(cx, expr, ignore_ids, main_loop_id) - }, - InlineAsmOperand::Out { expr, .. } => { - never_loop_expr_all(cx, &mut expr.iter().copied(), ignore_ids, main_loop_id) - }, - InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all( - cx, - &mut once(*in_expr).chain(out_expr.iter().copied()), - ignore_ids, - main_loop_id, - ), - InlineAsmOperand::Const { .. } - | InlineAsmOperand::SymFn { .. } - | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise, + ExprKind::Break(_, e) | ExprKind::Ret(e) => { + let first = e.as_ref().map_or(NeverLoopResult::Normal, |e| { + never_loop_expr(cx, e, local_labels, main_loop_id) + }); + combine_seq(first, || { + // checks if break targets a block instead of a loop + if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind { + if let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) { + *reachable = true; + } + } + NeverLoopResult::Diverging }) - .fold(NeverLoopResult::Otherwise, combine_seq), + }, + ExprKind::Become(e) => combine_seq(never_loop_expr(cx, e, local_labels, main_loop_id), || { + NeverLoopResult::Diverging + }), + ExprKind::InlineAsm(asm) => combine_seq_many(asm.operands.iter().map(|(o, _)| match o { + InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { + never_loop_expr(cx, expr, local_labels, main_loop_id) + }, + InlineAsmOperand::Out { expr, .. } => { + never_loop_expr_all(cx, expr.iter().copied(), local_labels, main_loop_id) + }, + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all( + cx, + once(*in_expr).chain(out_expr.iter().copied()), + local_labels, + main_loop_id, + ), + InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => { + NeverLoopResult::Normal + }, + })), ExprKind::OffsetOf(_, _) | ExprKind::Yield(_, _) | ExprKind::Closure { .. } | ExprKind::Path(_) | ExprKind::ConstBlock(_) | ExprKind::Lit(_) - | ExprKind::Err(_) => NeverLoopResult::Otherwise, + | ExprKind::Err(_) => NeverLoopResult::Normal, + }; + let result = combine_seq(result, || { + if cx.typeck_results().expr_ty(expr).is_never() { + NeverLoopResult::Diverging + } else { + NeverLoopResult::Normal + } + }); + if let NeverLoopResult::Diverging = result && + let Some(macro_call) = root_macro_call_first_node(cx, expr) && + let Some(sym::todo_macro) = cx.tcx.get_diagnostic_name(macro_call.def_id) + { + // We return MayContinueMainLoop here because we treat `todo!()` + // as potentially containing any code, including a continue of the main loop. + // This effectively silences the lint whenever a loop contains this macro anywhere. + NeverLoopResult::MayContinueMainLoop + } else { + result } } fn never_loop_expr_all<'tcx, T: Iterator>>( cx: &LateContext<'tcx>, - es: &mut T, - ignore_ids: &mut Vec, - main_loop_id: HirId, -) -> NeverLoopResult { - es.map(|e| never_loop_expr(cx, e, ignore_ids, main_loop_id)) - .fold(NeverLoopResult::Otherwise, combine_seq) -} - -fn never_loop_expr_branch<'tcx, T: Iterator>>( - cx: &LateContext<'tcx>, - e: &mut T, - ignore_ids: &mut Vec, + es: T, + local_labels: &mut Vec<(HirId, bool)>, main_loop_id: HirId, ) -> NeverLoopResult { - e.fold(NeverLoopResult::AlwaysBreak, |a, b| { - combine_branches(a, never_loop_expr(cx, b, ignore_ids, main_loop_id), ignore_ids) - }) + combine_seq_many(es.map(|e| never_loop_expr(cx, e, local_labels, main_loop_id))) } fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String { diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs index 39d8b20d38d02..90557b5556085 100644 --- a/clippy_lints/src/manual_range_patterns.rs +++ b/clippy_lints/src/manual_range_patterns.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -6,6 +7,7 @@ use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp}; 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::{Span, DUMMY_SP}; declare_clippy_lint! { /// ### What it does @@ -49,6 +51,29 @@ fn expr_as_i128(expr: &Expr<'_>) -> Option { } } +#[derive(Copy, Clone)] +struct Num { + val: i128, + span: Span, +} + +impl Num { + fn new(expr: &Expr<'_>) -> Option { + Some(Self { + val: expr_as_i128(expr)?, + span: expr.span, + }) + } + + fn dummy(val: i128) -> Self { + Self { val, span: DUMMY_SP } + } + + fn min(self, other: Self) -> Self { + if self.val < other.val { self } else { other } + } +} + impl LateLintPass<'_> for ManualRangePatterns { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) { if in_external_macro(cx.sess(), pat.span) { @@ -56,71 +81,83 @@ impl LateLintPass<'_> for ManualRangePatterns { } // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives + // or at least one range if let PatKind::Or(pats) = pat.kind - && pats.len() >= 3 + && (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..)))) { - let mut min = i128::MAX; - let mut max = i128::MIN; + let mut min = Num::dummy(i128::MAX); + let mut max = Num::dummy(i128::MIN); + let mut range_kind = RangeEnd::Included; let mut numbers_found = FxHashSet::default(); let mut ranges_found = Vec::new(); for pat in pats { if let PatKind::Lit(lit) = pat.kind - && let Some(num) = expr_as_i128(lit) + && let Some(num) = Num::new(lit) { - numbers_found.insert(num); + numbers_found.insert(num.val); min = min.min(num); - max = max.max(num); + if num.val >= max.val { + max = num; + range_kind = RangeEnd::Included; + } } else if let PatKind::Range(Some(left), Some(right), end) = pat.kind - && let Some(left) = expr_as_i128(left) - && let Some(right) = expr_as_i128(right) - && right >= left + && let Some(left) = Num::new(left) + && let Some(mut right) = Num::new(right) { + if let RangeEnd::Excluded = end { + right.val -= 1; + } + min = min.min(left); - max = max.max(right); - ranges_found.push(left..=match end { - RangeEnd::Included => right, - RangeEnd::Excluded => right - 1, - }); + if right.val > max.val { + max = right; + range_kind = end; + } + ranges_found.push(left.val..=right.val); } else { return; } } - let contains_whole_range = 'contains: { - let mut num = min; - while num <= max { - if numbers_found.contains(&num) { - num += 1; - } - // Given a list of (potentially overlapping) ranges like: - // 1..=5, 3..=7, 6..=10 - // We want to find the range with the highest end that still contains the current number - else if let Some(range) = ranges_found - .iter() - .filter(|range| range.contains(&num)) - .max_by_key(|range| range.end()) - { - num = range.end() + 1; - } else { - break 'contains false; - } + let mut num = min.val; + while num <= max.val { + if numbers_found.contains(&num) { + num += 1; + } + // Given a list of (potentially overlapping) ranges like: + // 1..=5, 3..=7, 6..=10 + // We want to find the range with the highest end that still contains the current number + else if let Some(range) = ranges_found + .iter() + .filter(|range| range.contains(&num)) + .max_by_key(|range| range.end()) + { + num = range.end() + 1; + } else { + return; } - break 'contains true; - }; - - if contains_whole_range { - span_lint_and_sugg( - cx, - MANUAL_RANGE_PATTERNS, - pat.span, - "this OR pattern can be rewritten using a range", - "try", - format!("{min}..={max}"), - Applicability::MachineApplicable, - ); } + + span_lint_and_then( + cx, + MANUAL_RANGE_PATTERNS, + pat.span, + "this OR pattern can be rewritten using a range", + |diag| { + if let Some(min) = snippet_opt(cx, min.span) + && let Some(max) = snippet_opt(cx, max.span) + { + diag.span_suggestion( + pat.span, + "try", + format!("{min}{range_kind}{max}"), + Applicability::MachineApplicable, + ); + } + }, + ); } } } diff --git a/clippy_lints/src/methods/iter_out_of_bounds.rs b/clippy_lints/src/methods/iter_out_of_bounds.rs new file mode 100644 index 0000000000000..79c6d63254b28 --- /dev/null +++ b/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -0,0 +1,106 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::higher::VecArgs; +use clippy_utils::{expr_or_init, is_trait_method, match_def_path, paths}; +use rustc_ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self}; +use rustc_span::sym; + +use super::ITER_OUT_OF_BOUNDS; + +fn expr_as_u128(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { + if let ExprKind::Lit(lit) = expr_or_init(cx, e).kind + && let LitKind::Int(n, _) = lit.node + { + Some(n) + } else { + None + } +} + +/// Attempts to extract the length out of an iterator expression. +fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) -> Option { + let ty::Adt(adt, substs) = cx.typeck_results().expr_ty(iter).kind() else { + return None; + }; + let did = adt.did(); + + if match_def_path(cx, did, &paths::ARRAY_INTO_ITER) { + // For array::IntoIter, the length is the second generic + // parameter. + substs + .const_at(1) + .try_eval_target_usize(cx.tcx, cx.param_env) + .map(u128::from) + } else if match_def_path(cx, did, &paths::SLICE_ITER) + && let ExprKind::MethodCall(_, recv, ..) = iter.kind + { + if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() { + // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..) + len.try_eval_target_usize(cx.tcx, cx.param_env).map(u128::from) + } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) { + match args { + VecArgs::Vec(vec) => vec.len().try_into().ok(), + VecArgs::Repeat(_, len) => expr_as_u128(cx, len), + } + } else { + None + } + } else if match_def_path(cx, did, &paths::ITER_EMPTY) { + Some(0) + } else if match_def_path(cx, did, &paths::ITER_ONCE) { + Some(1) + } else { + None + } +} + +fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, + message: &'static str, + note: &'static str, +) { + if is_trait_method(cx, expr, sym::Iterator) + && let Some(len) = get_iterator_length(cx, recv) + && let Some(skipped) = expr_as_u128(cx, arg) + && skipped > len + { + span_lint_and_note(cx, ITER_OUT_OF_BOUNDS, expr.span, message, None, note); + } +} + +pub(super) fn check_skip<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, +) { + check( + cx, + expr, + recv, + arg, + "this `.skip()` call skips more items than the iterator will produce", + "this operation is useless and will create an empty iterator", + ); +} + +pub(super) fn check_take<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, +) { + check( + cx, + expr, + recv, + arg, + "this `.take()` call takes more items than the iterator will produce", + "this operation is useless and the returned iterator will simply yield the same items", + ); +} diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index ee405a3e30c3c..a49dd98db8713 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -21,7 +21,7 @@ pub(super) enum Op<'a> { RmCloned, // rm `.cloned()` - // e.g. `map` `for_each` + // e.g. `map` `for_each` `all` `any` NeedlessMove(&'a str, &'a Expr<'a>), // later `.cloned()` diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5075137caa0cb..81223fa8d954f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -43,6 +43,7 @@ mod iter_next_slice; mod iter_nth; mod iter_nth_zero; mod iter_on_single_or_empty_collections; +mod iter_out_of_bounds; mod iter_overeager_cloned; mod iter_skip_next; mod iter_skip_zero; @@ -3054,12 +3055,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// vec!(1, 2, 3, 4, 5).resize(0, 5) + /// vec![1, 2, 3, 4, 5].resize(0, 5) /// ``` /// /// Use instead: /// ```rust - /// vec!(1, 2, 3, 4, 5).clear() + /// vec![1, 2, 3, 4, 5].clear() /// ``` #[clippy::version = "1.46.0"] pub VEC_RESIZE_TO_ZERO, @@ -3538,6 +3539,30 @@ declare_clippy_lint! { "acquiring a write lock when a read lock would work" } +declare_clippy_lint! { + /// ### What it does + /// Looks for iterator combinator calls such as `.take(x)` or `.skip(x)` + /// where `x` is greater than the amount of items that an iterator will produce. + /// + /// ### Why is this bad? + /// Taking or skipping more items than there are in an iterator either creates an iterator + /// with all items from the original iterator or an iterator with no items at all. + /// This is most likely not what the user intended to do. + /// + /// ### Example + /// ```rust + /// for _ in [1, 2, 3].iter().take(4) {} + /// ``` + /// Use instead: + /// ```rust + /// for _ in [1, 2, 3].iter() {} + /// ``` + #[clippy::version = "1.74.0"] + pub ITER_OUT_OF_BOUNDS, + suspicious, + "calls to `.take()` or `.skip()` that are out of bounds" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3676,7 +3701,8 @@ impl_lint_pass!(Methods => [ STRING_LIT_CHARS_ANY, ITER_SKIP_ZERO, FILTER_MAP_BOOL_THEN, - READONLY_WRITE_LOCK + READONLY_WRITE_LOCK, + ITER_OUT_OF_BOUNDS, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3873,6 +3899,12 @@ impl Methods { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); }, + ("all", [arg]) => { + if let Some(("cloned", recv2, [], _, _)) = method_call(recv) { + iter_overeager_cloned::check(cx, expr, recv, recv2, + iter_overeager_cloned::Op::NeedlessMove(name, arg), false); + } + } ("and_then", [arg]) => { let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); @@ -3880,12 +3912,16 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, - ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind - && let body = cx.tcx.hir().body(arg.body) - && let [param] = body.params - && let Some(("chars", recv, _, _, _)) = method_call(recv) => - { - string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + ("any", [arg]) => { + match method_call(recv) { + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, arg), false), + Some(("chars", recv, _, _, _)) if let ExprKind::Closure(arg) = arg.kind + && let body = cx.tcx.hir().body(arg.body) + && let [param] = body.params => { + string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + } + _ => {} + } } ("arg", [arg]) => { suspicious_command_arg_space::check(cx, recv, arg, span); @@ -4136,6 +4172,7 @@ impl Methods { }, ("skip", [arg]) => { iter_skip_zero::check(cx, expr, arg); + iter_out_of_bounds::check_skip(cx, expr, recv, arg); if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check(cx, expr, recv, recv2, @@ -4163,7 +4200,8 @@ impl Methods { } }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), - ("take", [_arg]) => { + ("take", [arg]) => { + iter_out_of_bounds::check_take(cx, expr, recv, arg); if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::LaterCloned, false); diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs new file mode 100644 index 0000000000000..08fec2b8ec820 --- /dev/null +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -0,0 +1,391 @@ +use std::mem; +use std::ops::ControlFlow; + +use clippy_utils::comparisons::{normalize_comparison, Rel}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{eq_expr_value, hash_expr, higher}; +use rustc_ast::{LitKind, RangeLimits}; +use rustc_data_structures::unhash::UnhashMap; +use rustc_errors::{Applicability, Diagnostic}; +use rustc_hir::{BinOp, Block, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for repeated slice indexing without asserting beforehand that the length + /// is greater than the largest index used to index into the slice. + /// + /// ### Why is this bad? + /// In the general case where the compiler does not have a lot of information + /// about the length of a slice, indexing it repeatedly will generate a bounds check + /// for every single index. + /// + /// Asserting that the length of the slice is at least as large as the largest value + /// to index beforehand gives the compiler enough information to elide the bounds checks, + /// effectively reducing the number of bounds checks from however many times + /// the slice was indexed to just one (the assert). + /// + /// ### Drawbacks + /// False positives. It is, in general, very difficult to predict how well + /// the optimizer will be able to elide bounds checks and it very much depends on + /// the surrounding code. For example, indexing into the slice yielded by the + /// [`slice::chunks_exact`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.chunks_exact) + /// iterator will likely have all of the bounds checks elided even without an assert + /// if the `chunk_size` is a constant. + /// + /// Asserts are not tracked across function calls. Asserting the length of a slice + /// in a different function likely gives the optimizer enough information + /// about the length of a slice, but this lint will not detect that. + /// + /// ### Example + /// ```rust + /// fn sum(v: &[u8]) -> u8 { + /// // 4 bounds checks + /// v[0] + v[1] + v[2] + v[3] + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn sum(v: &[u8]) -> u8 { + /// assert!(v.len() > 4); + /// // no bounds checks + /// v[0] + v[1] + v[2] + v[3] + /// } + /// ``` + #[clippy::version = "1.70.0"] + pub MISSING_ASSERTS_FOR_INDEXING, + restriction, + "indexing into a slice multiple times without an `assert`" +} +declare_lint_pass!(MissingAssertsForIndexing => [MISSING_ASSERTS_FOR_INDEXING]); + +fn report_lint(cx: &LateContext<'_>, full_span: Span, msg: &str, indexes: &[Span], f: F) +where + F: FnOnce(&mut Diagnostic), +{ + span_lint_and_then(cx, MISSING_ASSERTS_FOR_INDEXING, full_span, msg, |diag| { + f(diag); + for span in indexes { + diag.span_note(*span, "slice indexed here"); + } + diag.note("asserting the length before indexing will elide bounds checks"); + }); +} + +#[derive(Copy, Clone, Debug)] +enum LengthComparison { + /// `v.len() < 5` + LengthLessThanInt, + /// `5 < v.len()` + IntLessThanLength, + /// `v.len() <= 5` + LengthLessThanOrEqualInt, + /// `5 <= v.len()` + IntLessThanOrEqualLength, +} + +/// Extracts parts out of a length comparison expression. +/// +/// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, `v.len()`))` +fn len_comparison<'hir>( + bin_op: BinOp, + left: &'hir Expr<'hir>, + right: &'hir Expr<'hir>, +) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { + macro_rules! int_lit_pat { + ($id:ident) => { + ExprKind::Lit(Spanned { + node: LitKind::Int($id, _), + .. + }) + }; + } + + // normalize comparison, `v.len() > 4` becomes `4 < v.len()` + // this simplifies the logic a bit + let (op, left, right) = normalize_comparison(bin_op.node, left, right)?; + match (op, &left.kind, &right.kind) { + (Rel::Lt, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanLength, *left as usize, right)), + (Rel::Lt, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanInt, *right as usize, left)), + (Rel::Le, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanOrEqualLength, *left as usize, right)), + (Rel::Le, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanOrEqualInt, *right as usize, left)), + _ => None, + } +} + +/// Attempts to extract parts out of an `assert!`-like expression +/// in the form `assert!(some_slice.len() > 5)`. +/// +/// `assert!` has expanded to an if expression at the HIR, so this +/// actually works not just with `assert!` specifically, but anything +/// that has a never type expression in the `then` block (e.g. `panic!`). +fn assert_len_expr<'hir>( + cx: &LateContext<'_>, + expr: &'hir Expr<'hir>, +) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { + if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) + && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind + && let ExprKind::Binary(bin_op, left, right) = &condition.kind + + && let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right) + && let ExprKind::MethodCall(method, recv, ..) = &slice_len.kind + && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() + && method.ident.name == sym::len + + // check if `then` block has a never type expression + && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind + && cx.typeck_results().expr_ty(then_expr).is_never() + { + Some((cmp, asserted_len, recv)) + } else { + None + } +} + +#[derive(Debug)] +enum IndexEntry<'hir> { + /// `assert!` without any indexing (so far) + StrayAssert { + asserted_len: usize, + comparison: LengthComparison, + assert_span: Span, + slice: &'hir Expr<'hir>, + }, + /// `assert!` with indexing + /// + /// We also store the highest index to be able to check + /// if the `assert!` asserts the right length. + AssertWithIndex { + highest_index: usize, + asserted_len: usize, + assert_span: Span, + slice: &'hir Expr<'hir>, + indexes: Vec, + comparison: LengthComparison, + }, + /// Indexing without an `assert!` + IndexWithoutAssert { + highest_index: usize, + indexes: Vec, + slice: &'hir Expr<'hir>, + }, +} + +impl<'hir> IndexEntry<'hir> { + pub fn slice(&self) -> &'hir Expr<'hir> { + match self { + IndexEntry::StrayAssert { slice, .. } + | IndexEntry::AssertWithIndex { slice, .. } + | IndexEntry::IndexWithoutAssert { slice, .. } => slice, + } + } + + pub fn index_spans(&self) -> Option<&[Span]> { + match self { + IndexEntry::StrayAssert { .. } => None, + IndexEntry::AssertWithIndex { indexes, .. } | IndexEntry::IndexWithoutAssert { indexes, .. } => { + Some(indexes) + }, + } + } +} + +/// Extracts the upper index of a slice indexing expression. +/// +/// E.g. for `5` this returns `Some(5)`, for `..5` this returns `Some(4)`, +/// for `..=5` this returns `Some(5)` +fn upper_index_expr(expr: &Expr<'_>) -> Option { + if let ExprKind::Lit(lit) = &expr.kind && let LitKind::Int(index, _) = lit.node { + Some(index as usize) + } else if let Some(higher::Range { end: Some(end), limits, .. }) = higher::Range::hir(expr) + && let ExprKind::Lit(lit) = &end.kind + && let LitKind::Int(index @ 1.., _) = lit.node + { + match limits { + RangeLimits::HalfOpen => Some(index as usize - 1), + RangeLimits::Closed => Some(index as usize), + } + } else { + None + } +} + +/// Checks if the expression is an index into a slice and adds it to `indexes` +fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap>>) { + if let ExprKind::Index(slice, index_lit, _) = expr.kind + && cx.typeck_results().expr_ty_adjusted(slice).peel_refs().is_slice() + && let Some(index) = upper_index_expr(index_lit) + { + let hash = hash_expr(cx, slice); + + let indexes = map.entry(hash).or_default(); + let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice)); + + if let Some(entry) = entry { + match entry { + IndexEntry::StrayAssert { asserted_len, comparison, assert_span, slice } => { + *entry = IndexEntry::AssertWithIndex { + highest_index: index, + asserted_len: *asserted_len, + assert_span: *assert_span, + slice, + indexes: vec![expr.span], + comparison: *comparison, + }; + }, + IndexEntry::IndexWithoutAssert { highest_index, indexes, .. } + | IndexEntry::AssertWithIndex { highest_index, indexes, .. } => { + indexes.push(expr.span); + *highest_index = (*highest_index).max(index); + }, + } + } else { + indexes.push(IndexEntry::IndexWithoutAssert { + highest_index: index, + indexes: vec![expr.span], + slice, + }); + } + } +} + +/// Checks if the expression is an `assert!` expression and adds it to `asserts` +fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap>>) { + if let Some((comparison, asserted_len, slice)) = assert_len_expr(cx, expr) { + let hash = hash_expr(cx, slice); + let indexes = map.entry(hash).or_default(); + + let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice)); + + if let Some(entry) = entry { + if let IndexEntry::IndexWithoutAssert { + highest_index, + indexes, + slice, + } = entry + { + *entry = IndexEntry::AssertWithIndex { + highest_index: *highest_index, + indexes: mem::take(indexes), + slice, + assert_span: expr.span, + comparison, + asserted_len, + }; + } + } else { + indexes.push(IndexEntry::StrayAssert { + asserted_len, + comparison, + assert_span: expr.span, + slice, + }); + } + } +} + +/// Inspects indexes and reports lints. +/// +/// Called at the end of this lint after all indexing and `assert!` expressions have been collected. +fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap>>) { + for bucket in map.values() { + for entry in bucket { + let Some(full_span) = entry + .index_spans() + .and_then(|spans| spans.first().zip(spans.last())) + .map(|(low, &high)| low.to(high)) + else { + continue; + }; + + match entry { + IndexEntry::AssertWithIndex { + highest_index, + asserted_len, + indexes, + comparison, + assert_span, + slice, + } if indexes.len() > 1 => { + // if we have found an `assert!`, let's also check that it's actually right + // and if it convers the highest index and if not, suggest the correct length + let sugg = match comparison { + // `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks. + // The user probably meant `v.len() > 5` + LengthComparison::LengthLessThanInt | LengthComparison::LengthLessThanOrEqualInt => Some( + format!("assert!({}.len() > {highest_index})", snippet(cx, slice.span, "..")), + ), + // `5 < v.len()` == `v.len() > 5` + LengthComparison::IntLessThanLength if asserted_len < highest_index => Some(format!( + "assert!({}.len() > {highest_index})", + snippet(cx, slice.span, "..") + )), + // `5 <= v.len() == `v.len() >= 5` + LengthComparison::IntLessThanOrEqualLength if asserted_len <= highest_index => Some(format!( + "assert!({}.len() > {highest_index})", + snippet(cx, slice.span, "..") + )), + _ => None, + }; + + if let Some(sugg) = sugg { + report_lint( + cx, + full_span, + "indexing into a slice multiple times with an `assert` that does not cover the highest index", + indexes, + |diag| { + diag.span_suggestion( + *assert_span, + "provide the highest index that is indexed with", + sugg, + Applicability::MachineApplicable, + ); + }, + ); + } + }, + IndexEntry::IndexWithoutAssert { + indexes, + highest_index, + slice, + } if indexes.len() > 1 => { + // if there was no `assert!` but more than one index, suggest + // adding an `assert!` that covers the highest index + report_lint( + cx, + full_span, + "indexing into a slice multiple times without an `assert`", + indexes, + |diag| { + diag.help(format!( + "consider asserting the length before indexing: `assert!({}.len() > {highest_index});`", + snippet(cx, slice.span, "..") + )); + }, + ); + }, + _ => {}, + } + } + } +} + +impl LateLintPass<'_> for MissingAssertsForIndexing { + fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) { + let mut map = UnhashMap::default(); + + for_each_expr(block, |expr| { + check_index(cx, expr, &mut map); + check_assert(cx, expr, &mut map); + ControlFlow::::Continue(()) + }); + + report_indexes(cx, &map); + } +} diff --git a/clippy_lints/src/incorrect_impls.rs b/clippy_lints/src/non_canonical_impls.rs similarity index 88% rename from clippy_lints/src/incorrect_impls.rs rename to clippy_lints/src/non_canonical_impls.rs index 3c59b839a39c6..4b24f059afdf5 100644 --- a/clippy_lints/src/incorrect_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -13,11 +13,12 @@ use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does - /// Checks for manual implementations of `Clone` when `Copy` is already implemented. + /// Checks for non-canonical implementations of `Clone` when `Copy` is already implemented. /// /// ### Why is this bad? - /// If both `Clone` and `Copy` are implemented, they must agree. This is done by dereferencing - /// `self` in `Clone`'s implementation. Anything else is incorrect. + /// If both `Clone` and `Copy` are implemented, they must agree. This can done by dereferencing + /// `self` in `Clone`'s implementation, which will avoid any possibility of the implementations + /// becoming out of sync. /// /// ### Example /// ```rust,ignore @@ -46,14 +47,13 @@ declare_clippy_lint! { /// impl Copy for A {} /// ``` #[clippy::version = "1.72.0"] - pub INCORRECT_CLONE_IMPL_ON_COPY_TYPE, - correctness, - "manual implementation of `Clone` on a `Copy` type" + pub NON_CANONICAL_CLONE_IMPL, + suspicious, + "non-canonical implementation of `Clone` on a `Copy` type" } declare_clippy_lint! { /// ### What it does - /// Checks for manual implementations of both `PartialOrd` and `Ord` when only `Ord` is - /// necessary. + /// Checks for non-canonical implementations of `PartialOrd` when `Ord` is already implemented. /// /// ### Why is this bad? /// If both `PartialOrd` and `Ord` are implemented, they must agree. This is commonly done by @@ -61,11 +61,8 @@ declare_clippy_lint! { /// introduce an error upon refactoring. /// /// ### Known issues - /// Code that calls the `.into()` method instead will be flagged as incorrect, despite `.into()` - /// wrapping it in `Some`. - /// - /// ### Limitations - /// Will not lint if `Self` and `Rhs` do not have the same type. + /// Code that calls the `.into()` method instead will be flagged, despite `.into()` wrapping it + /// in `Some`. /// /// ### Example /// ```rust @@ -107,13 +104,13 @@ declare_clippy_lint! { /// } /// ``` #[clippy::version = "1.72.0"] - pub INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE, - correctness, - "manual implementation of `PartialOrd` when `Ord` is already implemented" + pub NON_CANONICAL_PARTIAL_ORD_IMPL, + suspicious, + "non-canonical implementation of `PartialOrd` on an `Ord` type" } -declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE, INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE]); +declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); -impl LateLintPass<'_> for IncorrectImpls { +impl LateLintPass<'_> for NonCanonicalImpls { #[expect(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { let Some(Node::Item(item)) = get_parent_node(cx.tcx, impl_item.hir_id()) else { @@ -154,9 +151,9 @@ impl LateLintPass<'_> for IncorrectImpls { {} else { span_lint_and_sugg( cx, - INCORRECT_CLONE_IMPL_ON_COPY_TYPE, + NON_CANONICAL_CLONE_IMPL, block.span, - "incorrect implementation of `clone` on a `Copy` type", + "non-canonical implementation of `clone` on a `Copy` type", "change this to", "{ *self }".to_owned(), Applicability::MaybeIncorrect, @@ -169,9 +166,9 @@ impl LateLintPass<'_> for IncorrectImpls { if impl_item.ident.name == sym::clone_from { span_lint_and_sugg( cx, - INCORRECT_CLONE_IMPL_ON_COPY_TYPE, + NON_CANONICAL_CLONE_IMPL, impl_item.span, - "incorrect implementation of `clone_from` on a `Copy` type", + "unnecessary implementation of `clone_from` on a `Copy` type", "remove it", String::new(), Applicability::MaybeIncorrect, @@ -222,9 +219,9 @@ impl LateLintPass<'_> for IncorrectImpls { span_lint_and_then( cx, - INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE, + NON_CANONICAL_PARTIAL_ORD_IMPL, item.span, - "incorrect implementation of `partial_cmp` on an `Ord` type", + "non-canonical implementation of `partial_cmp` on an `Ord` type", |diag| { let [_, other] = body.params else { return; diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index a687c7d29b56e..8072aded8513b 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -14,7 +14,12 @@ use {rustc_ast as ast, rustc_hir as hir}; const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]]; const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; -const INTEGER_METHODS: &[Symbol] = &[sym::saturating_div, sym::wrapping_div, sym::wrapping_rem, sym::wrapping_rem_euclid]; +const INTEGER_METHODS: &[Symbol] = &[ + sym::saturating_div, + sym::wrapping_div, + sym::wrapping_rem, + sym::wrapping_rem_euclid, +]; #[derive(Debug)] pub struct ArithmeticSideEffects { @@ -93,7 +98,14 @@ impl ArithmeticSideEffects { let is_non_zero_u = |symbol: Option| { matches!( symbol, - Some(sym::NonZeroU128 | sym::NonZeroU16 | sym::NonZeroU32 | sym::NonZeroU64 | sym::NonZeroU8 | sym::NonZeroUsize) + Some( + sym::NonZeroU128 + | sym::NonZeroU16 + | sym::NonZeroU32 + | sym::NonZeroU64 + | sym::NonZeroU8 + | sym::NonZeroUsize + ) ) }; let is_sat_or_wrap = |ty: Ty<'_>| { diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index f3e0c58a78711..bce6bdcaf61cd 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -17,7 +17,7 @@ pub(crate) fn check<'tcx>( left: &'tcx Expr<'_>, right: &'tcx Expr<'_>, ) { - if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { + if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index ccabb577cb722..e8018462d75f6 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -1,7 +1,7 @@ use std::iter::once; use std::ops::ControlFlow; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::LitKind; @@ -9,6 +9,7 @@ use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{BytePos, Pos, Span}; declare_clippy_lint! { /// ### What it does @@ -76,14 +77,33 @@ impl EarlyLintPass for RawStrings { } if !str.contains(['\\', '"']) { - span_lint_and_sugg( + span_lint_and_then( cx, NEEDLESS_RAW_STRINGS, expr.span, "unnecessary raw string literal", - "try", - format!("{}\"{}\"", prefix.replace('r', ""), lit.symbol), - Applicability::MachineApplicable, + |diag| { + let (start, end) = hash_spans(expr.span, prefix, 0, max); + + // BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text + let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1); + let start = start.with_lo(r_pos); + + if end.is_empty() { + diag.span_suggestion( + start, + "use a string literal instead", + format!("\"{}\"", str), + Applicability::MachineApplicable, + ); + } else { + diag.multipart_suggestion( + "try", + vec![(start, String::new()), (end, String::new())], + Applicability::MachineApplicable, + ); + } + }, ); return; @@ -96,13 +116,6 @@ impl EarlyLintPass for RawStrings { let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| { match b { b'"' if !following_quote => (following_quote, req) = (true, 1), - // I'm a bit surprised the compiler didn't optimize this out, there's no - // branch but it still ends up doing an unnecessary comparison, it's: - // - cmp r9b,1h - // - sbb cl,-1h - // which will add 1 if it's true. With this change, it becomes: - // - add cl,r9b - // isn't that so much nicer? b'#' => req += u8::from(following_quote), _ => { if following_quote { @@ -126,18 +139,58 @@ impl EarlyLintPass for RawStrings { }; if req < max { - let hashes = "#".repeat(req as usize); - - span_lint_and_sugg( + span_lint_and_then( cx, NEEDLESS_RAW_STRING_HASHES, expr.span, "unnecessary hashes around raw string literal", - "try", - format!(r#"{prefix}{hashes}"{}"{hashes}"#, lit.symbol), - Applicability::MachineApplicable, + |diag| { + let (start, end) = hash_spans(expr.span, prefix, req, max); + + let message = match max - req { + _ if req == 0 => "remove all the hashes around the literal".to_string(), + 1 => "remove one hash from both sides of the literal".to_string(), + n => format!("remove {n} hashes from both sides of the literal"), + }; + + diag.multipart_suggestion( + message, + vec![(start, String::new()), (end, String::new())], + Applicability::MachineApplicable, + ); + }, ); } } } } + +/// Returns spans pointing at the unneeded hashes, e.g. for a `req` of `1` and `max` of `3`: +/// +/// ```ignore +/// r###".."### +/// ^^ ^^ +/// ``` +fn hash_spans(literal_span: Span, prefix: &str, req: u8, max: u8) -> (Span, Span) { + let literal_span = literal_span.data(); + + // BytePos: we checked prefix appears literally in the source text + let hash_start = literal_span.lo + BytePos::from_usize(prefix.len()); + let hash_end = literal_span.hi; + + // BytePos: req/max are counts of the ASCII character # + let start = Span::new( + hash_start + BytePos(req.into()), + hash_start + BytePos(max.into()), + literal_span.ctxt, + None, + ); + let end = Span::new( + hash_end - BytePos(req.into()), + hash_end - BytePos(max.into()), + literal_span.ctxt, + None, + ); + + (start, end) +} diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index fc1fabcc0ae59..613f1ecc6fbee 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -15,6 +15,8 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), ("clippy::identity_conversion", "clippy::useless_conversion"), ("clippy::if_let_some_result", "clippy::match_result_ok"), + ("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"), + ("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"), ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), ("clippy::new_without_default_derive", "clippy::new_without_default"), @@ -38,12 +40,12 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::drop_bounds", "drop_bounds"), ("clippy::drop_copy", "dropping_copy_types"), ("clippy::drop_ref", "dropping_references"), + ("clippy::fn_null_check", "useless_ptr_null_checks"), ("clippy::for_loop_over_option", "for_loops_over_fallibles"), ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), ("clippy::forget_copy", "forgetting_copy_types"), ("clippy::forget_ref", "forgetting_references"), - ("clippy::fn_null_check", "useless_ptr_null_checks"), ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index c9ab622ad25d3..9db18c2976c22 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local, @@ -148,6 +149,15 @@ impl SlowVectorInit { /// - `Some(InitializedSize::Uninitialized)` for `Vec::new()` /// - `None` for other, unrelated kinds of expressions fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option> { + // Generally don't warn if the vec initializer comes from an expansion, except for the vec! macro. + // This lets us still warn on `vec![]`, while ignoring other kinds of macros that may output an + // empty vec + if expr.span.from_expansion() + && root_macro_call(expr.span).map(|m| m.def_id) != cx.tcx.get_diagnostic_item(sym::vec_macro) + { + return None; + } + if let ExprKind::Call(func, [len_expr]) = expr.kind && is_expr_path_def_path(cx, func, &paths::VEC_WITH_CAPACITY) { @@ -205,7 +215,7 @@ impl SlowVectorInit { span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { diag.span_suggestion( - vec_alloc.allocation_expr.span, + vec_alloc.allocation_expr.span.source_callsite(), "consider replacing this with", format!("vec![0; {len_expr}]"), Applicability::Unspecified, diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index f239165276ffe..5f54a10d1c41d 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, Path, PathSegment}; @@ -99,17 +100,17 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { && let Some(first_segment) = get_first_segment(path) && is_stable(cx, def_id) { - let (lint, msg, help) = match first_segment.ident.name { + let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { sym::core => ( STD_INSTEAD_OF_CORE, - "used import from `std` instead of `core`", - "consider importing the item from `core`", + "std", + "core", ), sym::alloc => ( STD_INSTEAD_OF_ALLOC, - "used import from `std` instead of `alloc`", - "consider importing the item from `alloc`", + "std", + "alloc", ), _ => { self.prev_span = path.span; @@ -120,8 +121,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { if cx.tcx.crate_name(def_id.krate) == sym::core { ( ALLOC_INSTEAD_OF_CORE, - "used import from `alloc` instead of `core`", - "consider importing the item from `core`", + "alloc", + "core", ) } else { self.prev_span = path.span; @@ -131,7 +132,14 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { _ => return, }; if path.span != self.prev_span { - span_lint_and_help(cx, lint, path.span, msg, None, help); + span_lint_and_sugg( + cx, + lint, + first_segment.ident.span, + &format!("used import from `{used_mod}` instead of `{replace_with}`"), + &format!("consider importing the item from `{replace_with}`"), + replace_with.to_string(), + Applicability::MachineApplicable); self.prev_span = path.span; } } diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index da8d8ed4c0f2c..6193fdeb433a2 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -341,44 +341,21 @@ fn block_parents_have_safety_comment( id: hir::HirId, ) -> bool { if let Some(node) = get_parent_node(cx.tcx, id) { - return match node { - Node::Expr(expr) => { - if let Some( - Node::Local(hir::Local { span, .. }) - | Node::Item(hir::Item { - kind: hir::ItemKind::Const(..) | ItemKind::Static(..), - span, - .. - }), - ) = get_parent_node(cx.tcx, expr.hir_id) - { - let hir_id = match get_parent_node(cx.tcx, expr.hir_id) { - Some(Node::Local(hir::Local { hir_id, .. })) => *hir_id, - Some(Node::Item(hir::Item { owner_id, .. })) => { - cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id) - }, - _ => unreachable!(), - }; - - // if unsafe block is part of a let/const/static statement, - // and accept_comment_above_statement is set to true - // we accept the safety comment in the line the precedes this statement. - accept_comment_above_statement - && span_with_attrs_in_body_has_safety_comment( - cx, - *span, - hir_id, - accept_comment_above_attributes, - ) - } else { - !is_branchy(expr) - && span_with_attrs_in_body_has_safety_comment( - cx, - expr.span, - expr.hir_id, - accept_comment_above_attributes, - ) - } + let (span, hir_id) = match node { + Node::Expr(expr) => match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Local(hir::Local { span, hir_id, .. })) => (*span, *hir_id), + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Const(..) | ItemKind::Static(..), + span, + owner_id, + .. + })) => (*span, cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id)), + _ => { + if is_branchy(expr) { + return false; + } + (expr.span, expr.hir_id) + }, }, Node::Stmt(hir::Stmt { kind: @@ -387,28 +364,27 @@ fn block_parents_have_safety_comment( | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }), .. }) - | Node::Local(hir::Local { span, hir_id, .. }) => { - span_with_attrs_in_body_has_safety_comment(cx, *span, *hir_id, accept_comment_above_attributes) - }, + | Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id), Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), span, owner_id, .. - }) => span_with_attrs_in_body_has_safety_comment( - cx, - *span, - cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id), - accept_comment_above_attributes, - ), - _ => false, + }) => (*span, cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id)), + _ => return false, }; + // if unsafe block is part of a let/const/static statement, + // and accept_comment_above_statement is set to true + // we accept the safety comment in the line the precedes this statement. + accept_comment_above_statement + && span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes) + } else { + false } - false } /// Extends `span` to also include its attributes, then checks if that span has a safety comment. -fn span_with_attrs_in_body_has_safety_comment( +fn span_with_attrs_has_safety_comment( cx: &LateContext<'_>, span: Span, hir_id: HirId, @@ -420,7 +396,7 @@ fn span_with_attrs_in_body_has_safety_comment( span }; - span_in_body_has_safety_comment(cx, span) + span_has_safety_comment(cx, span) } /// Checks if an expression is "branchy", e.g. loop, match/if/etc. @@ -444,7 +420,7 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { matches!( span_from_macro_expansion_has_safety_comment(cx, span), HasSafetyComment::Yes(_) - ) || span_in_body_has_safety_comment(cx, span) + ) || span_has_safety_comment(cx, span) } fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span { @@ -633,29 +609,36 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option { let body = cx.enclosing_body?; let map = cx.tcx.hir(); let mut span = map.body(body).value.span; + let mut maybe_global_var = false; for (_, node) in map.parent_iter(body.hir_id) { match node { Node::Expr(e) => span = e.span, - Node::Block(_) - | Node::Arm(_) - | Node::Stmt(_) - | Node::Local(_) - | Node::Item(hir::Item { + Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (), + Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), .. - }) => (), + }) => maybe_global_var = true, + Node::Item(hir::Item { + kind: hir::ItemKind::Mod(_), + span: item_span, + .. + }) => { + span = *item_span; + break; + }, + Node::Crate(mod_) if maybe_global_var => { + span = mod_.spans.inner_span; + }, _ => break, } } Some(span) } -fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); - if ctxt == SyntaxContext::root() - && let Some(search_span) = get_body_search_span(cx) - { + if ctxt.is_root() && let Some(search_span) = get_body_search_span(cx) { if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) && let Some(body_span) = walk_span_to_context(search_span, SyntaxContext::root()) && let Ok(body_line) = source_map.lookup_line(body_span.lo()) diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index dd829ded0d0d6..de4b8738e35b9 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// let mut twins = vec!((1, 1), (2, 2)); + /// let mut twins = vec![(1, 1), (2, 2)]; /// twins.sort_by_key(|x| { x.1; }); /// ``` #[clippy::version = "1.47.0"] diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index c99b0290c0c0e..9a0d83d83f1f7 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,15 +1,18 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::usage::is_potentially_mutated; +use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{higher, path_to_local}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; -use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, PathSegment, UnOp}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::Ty; +use rustc_middle::mir::FakeReadCause; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -192,6 +195,55 @@ fn collect_unwrap_info<'tcx>( Vec::new() } +/// A HIR visitor delegate that checks if a local variable of type `Option<_>` is mutated, +/// *except* for if `Option::as_mut` is called. +/// The reason for why we allow that one specifically is that `.as_mut()` cannot change +/// the option to `None`, and that is important because this lint relies on the fact that +/// `is_some` + `unwrap` is equivalent to `if let Some(..) = ..`, which it would not be if +/// the option is changed to None between `is_some` and `unwrap`. +/// (And also `.as_mut()` is a somewhat common method that is still worth linting on.) +struct MutationVisitor<'tcx> { + is_mutated: bool, + local_id: HirId, + tcx: TyCtxt<'tcx>, +} + +/// Checks if the parent of the expression pointed at by the given `HirId` is a call to +/// `Option::as_mut`. +/// +/// Used by the mutation visitor to specifically allow `.as_mut()` calls. +/// In particular, the `HirId` that the visitor receives is the id of the local expression +/// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent +/// expression: that will be where the actual method call is. +fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { + if let Node::Expr(mutating_expr) = tcx.hir().get_parent(expr_id) + && let ExprKind::MethodCall(path, ..) = mutating_expr.kind + { + path.ident.name.as_str() == "as_mut" + } else { + false + } +} + +impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { + fn borrow(&mut self, cat: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { + if let ty::BorrowKind::MutBorrow = bk + && is_potentially_local_place(self.local_id, &cat.place) + && !is_option_as_mut_use(self.tcx, diag_expr_id) + { + self.is_mutated = true; + } + } + + fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) { + self.is_mutated = true; + } + + fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} +} + impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { fn visit_branch( &mut self, @@ -202,10 +254,26 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { ) { let prev_len = self.unwrappables.len(); for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) { - if is_potentially_mutated(unwrap_info.local_id, cond, self.cx) - || is_potentially_mutated(unwrap_info.local_id, branch, self.cx) - { - // if the variable is mutated, we don't know whether it can be unwrapped: + let mut delegate = MutationVisitor { + tcx: self.cx.tcx, + is_mutated: false, + local_id: unwrap_info.local_id, + }; + + let infcx = self.cx.tcx.infer_ctxt().build(); + let mut vis = ExprUseVisitor::new( + &mut delegate, + &infcx, + cond.hir_id.owner.def_id, + self.cx.param_env, + self.cx.typeck_results(), + ); + vis.walk_expr(cond); + vis.walk_expr(branch); + + if delegate.is_mutated { + // if the variable is mutated, we don't know whether it can be unwrapped. + // it might have been changed to `None` in between `is_some` + `unwrap`. continue; } self.unwrappables.push(unwrap_info); @@ -215,6 +283,27 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { } } +enum AsRefKind { + AsRef, + AsMut, +} + +/// Checks if the expression is a method call to `as_{ref,mut}` and returns the receiver of it. +/// If it isn't, the expression itself is returned. +fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option) { + if let ExprKind::MethodCall(path, recv, ..) = expr.kind { + if path.ident.name == sym::as_ref { + (recv, Some(AsRefKind::AsRef)) + } else if path.ident.name.as_str() == "as_mut" { + (recv, Some(AsRefKind::AsMut)) + } else { + (expr, None) + } + } else { + (expr, None) + } +} + impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; @@ -233,6 +322,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { // find `unwrap[_err]()` calls: if_chain! { if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind; + let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg); if let Some(id) = path_to_local(self_arg); if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name); let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name); @@ -268,7 +358,12 @@ 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} = {borrow_prefix}{unwrappable_variable_name}", + borrow_prefix = match as_ref_kind { + Some(AsRefKind::AsRef) => "&", + Some(AsRefKind::AsMut) => "&mut ", + None => "", + }, ), // 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/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 58ae0656db73e..2688947552253 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -561,6 +561,26 @@ define_Conf! { /// Which crates to allow absolute paths from (absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet = rustc_data_structures::fx::FxHashSet::default()), + /// Lint: EXPLICIT_ITER_LOOP + /// + /// Whether to recommend using implicit into iter for reborrowed values. + /// + /// #### Example + /// ``` + /// let mut vec = vec![1, 2, 3]; + /// let rmvec = &mut vec; + /// for _ in rmvec.iter() {} + /// for _ in rmvec.iter_mut() {} + /// ``` + /// + /// Use instead: + /// ``` + /// let mut vec = vec![1, 2, 3]; + /// let rmvec = &mut vec; + /// for _ in &*rmvec {} + /// for _ in &mut *rmvec {} + /// ``` + (enforce_iter_loop_reborrow: bool = false), } /// Search for the configuration file. diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 6c4cec5952471..4ef3ec19647e9 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -5,6 +5,7 @@ #![feature(lint_reasons)] #![feature(never_type)] #![feature(rustc_private)] +#![feature(assert_matches)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] @@ -110,6 +111,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{sym, Span}; use rustc_target::abi::Integer; +use visitors::Visitable; use crate::consts::{constant, miri_to_const, Constant}; use crate::higher::Range; @@ -1286,7 +1288,7 @@ pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext< } /// Returns `true` if `expr` contains a return expression -pub fn contains_return(expr: &hir::Expr<'_>) -> bool { +pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool { for_each_expr(expr, |e| { if matches!(e.kind, hir::ExprKind::Ret(..)) { ControlFlow::Break(()) diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index a567c5cbdc92a..2fb24b5c7ed9d 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -49,6 +49,7 @@ pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; +pub const ITER_ONCE: [&str; 5] = ["core", "iter", "sources", "once", "Once"]; pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; @@ -163,3 +164,4 @@ pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"]; pub const ORD_CMP: [&str; 4] = ["core", "cmp", "Ord", "cmp"]; #[expect(clippy::invalid_paths)] // not sure why it thinks this, it works so pub const BOOL_THEN: [&str; 4] = ["core", "bool", "", "then"]; +pub const ARRAY_INTO_ITER: [&str; 4] = ["core", "array", "iter", "IntoIter"]; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 600cd084c19f5..31cb421095ede 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -362,7 +362,7 @@ pub fn snippet_block_with_context<'a>( } /// Same as `snippet_with_applicability`, but first walks the span up to the given context. This -/// will result in the macro call, rather then the expansion, if the span is from a child context. +/// will result in the macro call, rather than the expansion, if the span is from a child context. /// If the span is not from a child context, it will be used directly instead. /// /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index ee5a49a207331..ae8ee371ffae3 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -88,7 +88,7 @@ impl<'a> Sugg<'a> { } /// Same as `hir`, but first walks the span up to the given context. This will result in the - /// macro call, rather then the expansion, if the span is from a child context. If the span is + /// macro call, rather than the expansion, if the span is from a child context. If the span is /// not from a child context, it will be used directly instead. /// /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a05f682aa8cda..f0b4ede35fbc5 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -27,6 +27,7 @@ use rustc_target::abi::{Size, VariantIdx}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Obligation, ObligationCause}; +use std::assert_matches::debug_assert_matches; use std::iter; use crate::{match_def_path, path_res, paths}; @@ -259,7 +260,11 @@ pub fn implements_trait_with_env_from_iter<'tcx>( })), ); - debug_assert_eq!(tcx.def_kind(trait_id), DefKind::Trait); + debug_assert_matches!( + tcx.def_kind(trait_id), + DefKind::Trait | DefKind::TraitAlias, + "`DefId` must belong to a trait or trait alias" + ); #[cfg(debug_assertions)] assert_generic_args_match(tcx, trait_id, trait_ref.args); diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 06fd952904405..dc7756533acb0 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -150,7 +150,7 @@ fn generic_args_certainty(cx: &LateContext<'_>, args: &GenericArgs<'_>) -> Certa } /// Tries to tell whether a `QPath` resolves to something certain, e.g., whether all of its path -/// segments generic arguments are are instantiated. +/// segments generic arguments are instantiated. /// /// `qpath` could refer to either a type or a value. The heuristic never needs the `DefId` of a /// value. So `DefId`s are retained only when `resolves_to_type` is true. diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 39ef76348d751..ec131c7f6a318 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -4,7 +4,7 @@ use core::ops::ControlFlow; use hir::def::Res; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir, Expr, ExprKind, HirId, HirIdSet}; -use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, Place, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -37,6 +37,17 @@ pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: & mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable)) } +pub fn is_potentially_local_place(local_id: HirId, place: &Place<'_>) -> bool { + match place.base { + PlaceBase::Local(id) => id == local_id, + PlaceBase::Upvar(_) => { + // Conservatively assume yes. + true + }, + _ => false, + } +} + struct MutVarsDelegate { used_mutably: HirIdSet, skip: bool, diff --git a/rust-toolchain b/rust-toolchain index 19c09b58b9862..9f5116eb73bb2 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-08-24" +channel = "nightly-2023-09-07" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c1c3d4a868f92..9fcc269dbf8c2 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -4,7 +4,7 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] -use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling, RustfixMode}; +use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling}; use std::collections::BTreeMap; use std::env::{self, set_var, var_os}; @@ -114,36 +114,26 @@ fn canonicalize(path: impl AsRef) -> PathBuf { } fn base_config(test_dir: &str) -> (Config, Args) { - let bless = var_os("RUSTC_BLESS").is_some_and(|v| v != "0") || env::args().any(|arg| arg == "--bless"); - - let args = Args { - filters: env::var("TESTNAME") - .map(|filters| filters.split(',').map(str::to_string).collect()) - .unwrap_or_default(), - quiet: false, - check: !bless, - threads: match std::env::var_os("RUST_TEST_THREADS") { - Some(n) => n.to_str().unwrap().parse().unwrap(), - None => std::thread::available_parallelism().unwrap(), - }, - skip: Vec::new(), - }; + let mut args = Args::test().unwrap(); + args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); let mut config = Config { mode: Mode::Yolo { - rustfix: RustfixMode::Everything, + rustfix: ui_test::RustfixMode::Everything, }, stderr_filters: vec![(Match::PathBackslash, b"/")], stdout_filters: vec![], - output_conflict_handling: if bless { - OutputConflictHandling::Bless - } else { - OutputConflictHandling::Error("cargo uibless".into()) - }, + filter_files: env::var("TESTNAME") + .map(|filters| filters.split(',').map(str::to_string).collect()) + .unwrap_or_default(), target: None, out_dir: canonicalize(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())).join("ui_test"), ..Config::rustc(Path::new("tests").join(test_dir)) }; + config.with_args(&args, /* bless by default */ false); + if let OutputConflictHandling::Error(err) = &mut config.output_conflict_handling { + *err = "cargo uibless".into(); + } let current_exe_path = env::current_exe().unwrap(); let deps_path = current_exe_path.parent().unwrap(); let profile_path = deps_path.parent().unwrap(); @@ -186,7 +176,6 @@ fn run_ui() { ui_test::run_tests_generic( vec![config], - args, ui_test::default_file_filter, ui_test::default_per_file_config, if quiet { @@ -211,7 +200,6 @@ fn run_internal_tests() { ui_test::run_tests_generic( vec![config], - args, ui_test::default_file_filter, ui_test::default_per_file_config, if quiet { @@ -245,7 +233,6 @@ fn run_ui_toml() { ui_test::run_tests_generic( vec![config], - args, ui_test::default_file_filter, |config, path, _file_contents| { config @@ -302,10 +289,16 @@ fn run_ui_cargo() { let quiet = args.quiet; + let ignored_32bit = |path: &Path| { + // FIXME: for some reason the modules are linted in a different order for this test + cfg!(target_pointer_width = "32") && path.ends_with("tests/ui-cargo/module_style/fail_mod/Cargo.toml") + }; + ui_test::run_tests_generic( vec![config], - args, - |path, args, _config| path.ends_with("Cargo.toml") && ui_test::default_filter_by_arg(path, args), + |path, config| { + path.ends_with("Cargo.toml") && ui_test::default_any_file_filter(path, config) && !ignored_32bit(path) + }, |config, path, _file_contents| { config.out_dir = canonicalize( std::env::current_dir() diff --git a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr index e161507b53395..4d97d54963d3d 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr @@ -1,6 +1,7 @@ error: package `cargo_common_metadata_fail` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]` error: package `cargo_common_metadata_fail` is missing `either package.license or package.license_file` metadata diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr b/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr index dbf494cc342d3..9eb884f08900b 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr @@ -1,6 +1,7 @@ error: package `cargo_common_metadata_fail_publish` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]` error: package `cargo_common_metadata_fail_publish` is missing `either package.license or package.license_file` metadata diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr index ae5967406f62f..f9685a784a33e 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr @@ -1,6 +1,7 @@ error: package `cargo_common_metadata_fail_publish_true` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]` error: package `cargo_common_metadata_fail_publish_true` is missing `either package.license or package.license_file` metadata diff --git a/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr b/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr index fde3a1e659915..912ea9bb4d111 100644 --- a/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr +++ b/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr @@ -9,6 +9,7 @@ error: file is loaded as a module multiple times: `src/b.rs` | = help: replace all but one `mod` item with `use` items = note: `-D clippy::duplicate-mod` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::duplicate_mod)]` error: file is loaded as a module multiple times: `src/c.rs` --> src/main.rs:9:1 diff --git a/tests/ui-cargo/feature_name/fail/Cargo.stderr b/tests/ui-cargo/feature_name/fail/Cargo.stderr index da2db45d3b836..388f49fb21351 100644 --- a/tests/ui-cargo/feature_name/fail/Cargo.stderr +++ b/tests/ui-cargo/feature_name/fail/Cargo.stderr @@ -2,6 +2,7 @@ error: the "no-" prefix in the feature name "no-qaq" is negative | = help: consider renaming the feature to "qaq", but make sure the feature adds functionality = note: `-D clippy::negative-feature-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::negative_feature_names)]` error: the "no_" prefix in the feature name "no_qaq" is negative | @@ -19,6 +20,7 @@ error: the "-support" suffix in the feature name "qvq-support" is redundant | = help: consider renaming the feature to "qvq" = note: `-D clippy::redundant-feature-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_feature_names)]` error: the "_support" suffix in the feature name "qvq_support" is redundant | diff --git a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr index c2907f319e643..902330e178530 100644 --- a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr +++ b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr @@ -6,6 +6,7 @@ error: `mod.rs` files are required, found `src/bad/inner.rs` | = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs` = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` error: `mod.rs` files are required, found `src/bad/inner/stuff.rs` --> src/bad/inner/stuff.rs:1:1 diff --git a/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr b/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr index fcf1a3c5e66c6..d776feb7f2e69 100644 --- a/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr +++ b/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr @@ -6,5 +6,6 @@ error: `mod.rs` files are required, found `src/bad.rs` | = help: move `src/bad.rs` to `src/bad/mod.rs` = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` error: could not compile `fail-mod-remap` (bin "fail-mod-remap") due to previous error diff --git a/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr b/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr index f61642ca2efd7..22558bc4ce89d 100644 --- a/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr +++ b/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr @@ -6,5 +6,6 @@ error: `mod.rs` files are not allowed, found `src/bad/mod.rs` | = help: move `src/bad/mod.rs` to `src/bad.rs` = note: `-D clippy::mod-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mod_module_files)]` error: could not compile `fail-no-mod` (bin "fail-no-mod") due to previous error diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr index 5bcce920455cf..4beedc10830a1 100644 --- a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr @@ -1,5 +1,6 @@ error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9 | = note: `-D clippy::multiple-crate-versions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::multiple_crate_versions)]` error: could not compile `multiple_crate_versions` (bin "multiple_crate_versions") due to previous error diff --git a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr index b1578c9f32490..65a19bb0718da 100644 --- a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr +++ b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr @@ -1,5 +1,6 @@ error: wildcard dependency for `regex` | = note: `-D clippy::wildcard-dependencies` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::wildcard_dependencies)]` error: could not compile `wildcard_dependencies` (bin "wildcard_dependencies") due to previous error diff --git a/tests/ui-internal/check_formulation.stderr b/tests/ui-internal/check_formulation.stderr index 10eabca4b9d74..96fa617601ff4 100644 --- a/tests/ui-internal/check_formulation.stderr +++ b/tests/ui-internal/check_formulation.stderr @@ -6,6 +6,7 @@ LL | /// Check for lint formulations that are correct | = help: try using `Checks for` instead = note: `-D clippy::almost-standard-lint-formulation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]` error: non-standard lint formulation --> $DIR/check_formulation.rs:33:5 diff --git a/tests/ui-internal/if_chain_style.stderr b/tests/ui-internal/if_chain_style.stderr index b12df2786520e..ea04955323d18 100644 --- a/tests/ui-internal/if_chain_style.stderr +++ b/tests/ui-internal/if_chain_style.stderr @@ -16,6 +16,7 @@ help: this `let` statement can also be in the `if_chain!` LL | let x = ""; | ^^^^^^^^^^^ = note: `-D clippy::if-chain-style` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::if_chain_style)]` error: `if a && b;` should be `if a; if b;` --> $DIR/if_chain_style.rs:24:12 diff --git a/tests/ui-internal/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr index 0e85088691765..988d32d5259e2 100644 --- a/tests/ui-internal/invalid_paths.stderr +++ b/tests/ui-internal/invalid_paths.stderr @@ -5,6 +5,7 @@ LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute" | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::invalid-paths` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]` error: invalid path --> $DIR/invalid_paths.rs:18:5 diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index 3ca45404e44bb..58b1fd92b5dc9 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -6,6 +6,7 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; | = help: convert all references to use `sym::Deref` = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` error: hardcoded path to a language item --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 diff --git a/tests/ui-toml/disallowed_macros/auxiliary/macros.rs b/tests/ui-toml/disallowed_macros/auxiliary/macros.rs index fcaeace0e9890..f4166b227fc68 100644 --- a/tests/ui-toml/disallowed_macros/auxiliary/macros.rs +++ b/tests/ui-toml/disallowed_macros/auxiliary/macros.rs @@ -30,3 +30,18 @@ macro_rules! item { const ITEM: usize = 1; }; } + +#[macro_export] +macro_rules! binop { + ($t:tt) => { + $t + $t + }; +} + +#[macro_export] +macro_rules! attr { + ($i:item) => { + #[repr(C)] + $i + }; +} diff --git a/tests/ui-toml/disallowed_macros/clippy.toml b/tests/ui-toml/disallowed_macros/clippy.toml index c8fe8be9a7704..85f1b71eb66b9 100644 --- a/tests/ui-toml/disallowed_macros/clippy.toml +++ b/tests/ui-toml/disallowed_macros/clippy.toml @@ -8,4 +8,6 @@ disallowed-macros = [ "macros::ty", "macros::pat", "macros::item", + "macros::binop", + "macros::attr", ] diff --git a/tests/ui-toml/disallowed_macros/disallowed_macros.rs b/tests/ui-toml/disallowed_macros/disallowed_macros.rs index ba919b4878854..4a3d55e13c9dc 100644 --- a/tests/ui-toml/disallowed_macros/disallowed_macros.rs +++ b/tests/ui-toml/disallowed_macros/disallowed_macros.rs @@ -20,11 +20,14 @@ fn main() { let macros::pat!() = 1; let _: macros::ty!() = ""; macros::item!(); + let _ = macros::binop!(1); eprintln!("allowed"); } -struct S; +macros::attr! { + struct S; +} impl S { macros::item!(); diff --git a/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr index 0eebd587a5f10..3c6f59b16e7c3 100644 --- a/tests/ui-toml/disallowed_macros/disallowed_macros.stderr +++ b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr @@ -63,23 +63,37 @@ error: use of a disallowed macro `macros::item` LL | macros::item!(); | ^^^^^^^^^^^^^^^ +error: use of a disallowed macro `macros::binop` + --> $DIR/disallowed_macros.rs:23:13 + | +LL | let _ = macros::binop!(1); + | ^^^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::attr` + --> $DIR/disallowed_macros.rs:28:1 + | +LL | / macros::attr! { +LL | | struct S; +LL | | } + | |_^ + error: use of a disallowed macro `macros::item` - --> $DIR/disallowed_macros.rs:30:5 + --> $DIR/disallowed_macros.rs:33:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ error: use of a disallowed macro `macros::item` - --> $DIR/disallowed_macros.rs:34:5 + --> $DIR/disallowed_macros.rs:37:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ error: use of a disallowed macro `macros::item` - --> $DIR/disallowed_macros.rs:38:5 + --> $DIR/disallowed_macros.rs:41:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs b/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs deleted file mode 100644 index 60fbaaea3d340..0000000000000 --- a/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs +++ /dev/null @@ -1,469 +0,0 @@ -// NOTE: Copied from `ui/auxiliary/proc_macros.rs`, couldn't get `../` to work for some reason - -#![feature(let_chains)] -#![feature(proc_macro_span)] -#![allow(clippy::excessive_nesting, dead_code)] - -extern crate proc_macro; - -use core::mem; -use proc_macro::token_stream::IntoIter; -use proc_macro::Delimiter::{self, Brace, Parenthesis}; -use proc_macro::Spacing::{self, Alone, Joint}; -use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT}; - -type Result = core::result::Result; - -/// Make a `compile_error!` pointing to the given span. -fn make_error(msg: &str, span: Span) -> TokenStream { - TokenStream::from_iter([ - TT::Ident(Ident::new("compile_error", span)), - TT::Punct(punct_with_span('!', Alone, span)), - TT::Group({ - let mut msg = Literal::string(msg); - msg.set_span(span); - group_with_span(Parenthesis, TokenStream::from_iter([TT::Literal(msg)]), span) - }), - ]) -} - -fn expect_tt(tt: Option, f: impl FnOnce(TT) -> Option, expected: &str, span: Span) -> Result { - match tt { - None => Err(make_error( - &format!("unexpected end of input, expected {expected}"), - span, - )), - Some(tt) => { - let span = tt.span(); - match f(tt) { - Some(x) => Ok(x), - None => Err(make_error(&format!("unexpected token, expected {expected}"), span)), - } - }, - } -} - -fn punct_with_span(c: char, spacing: Spacing, span: Span) -> Punct { - let mut p = Punct::new(c, spacing); - p.set_span(span); - p -} - -fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Group { - let mut g = Group::new(delimiter, stream); - g.set_span(span); - g -} - -/// Token used to escape the following token from the macro's span rules. -const ESCAPE_CHAR: char = '$'; - -/// Takes a single token followed by a sequence of tokens. Returns the sequence of tokens with their -/// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`. -#[proc_macro] -pub fn with_span(input: TokenStream) -> TokenStream { - let mut iter = input.into_iter(); - let span = iter.next().unwrap().span(); - let mut res = TokenStream::new(); - if let Err(e) = write_with_span(span, iter, &mut res) { - e - } else { - res - } -} - -/// Takes a sequence of tokens and return the tokens with the span set such that they appear to be -/// from an external macro. Tokens may be escaped with either `#ident` or `#(tokens)`. -#[proc_macro] -pub fn external(input: TokenStream) -> TokenStream { - let mut res = TokenStream::new(); - if let Err(e) = write_with_span(Span::mixed_site(), input.into_iter(), &mut res) { - e - } else { - res - } -} - -/// Copies all the tokens, replacing all their spans with the given span. Tokens can be escaped -/// either by `#ident` or `#(tokens)`. -fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Result<()> { - while let Some(tt) = input.next() { - match tt { - TT::Punct(p) if p.as_char() == ESCAPE_CHAR => { - expect_tt( - input.next(), - |tt| match tt { - tt @ (TT::Ident(_) | TT::Literal(_)) => { - out.extend([tt]); - Some(()) - }, - TT::Punct(mut p) if p.as_char() == ESCAPE_CHAR => { - p.set_span(s); - out.extend([TT::Punct(p)]); - Some(()) - }, - TT::Group(g) if g.delimiter() == Parenthesis => { - out.extend([TT::Group(group_with_span(Delimiter::None, g.stream(), g.span()))]); - Some(()) - }, - _ => None, - }, - "an ident, a literal, or parenthesized tokens", - p.span(), - )?; - }, - TT::Group(g) => { - let mut stream = TokenStream::new(); - write_with_span(s, g.stream().into_iter(), &mut stream)?; - out.extend([TT::Group(group_with_span(g.delimiter(), stream, s))]); - }, - mut tt => { - tt.set_span(s); - out.extend([tt]); - }, - } - } - Ok(()) -} - -/// Within the item this attribute is attached to, an `inline!` macro is available which expands the -/// contained tokens as though they came from a macro expansion. -/// -/// Within the `inline!` macro, any token preceded by `$` is passed as though it were an argument -/// with an automatically chosen fragment specifier. `$ident` will be passed as `ident`, `$1` or -/// `$"literal"` will be passed as `literal`, `$'lt` will be passed as `lifetime`, and `$(...)` will -/// pass the contained tokens as a `tt` sequence (the wrapping parenthesis are removed). If another -/// specifier is required it can be specified within parenthesis like `$(@expr ...)`. This will -/// expand the remaining tokens as a single argument. -/// -/// Multiple `inline!` macros may be nested within each other. This will expand as nested macro -/// calls. However, any arguments will be passed as though they came from the outermost context. -#[proc_macro_attribute] -pub fn inline_macros(args: TokenStream, input: TokenStream) -> TokenStream { - let mut args = args.into_iter(); - let mac_name = match args.next() { - Some(TT::Ident(name)) => Some(name), - Some(tt) => { - return make_error( - "unexpected argument, expected either an ident or no arguments", - tt.span(), - ); - }, - None => None, - }; - if let Some(tt) = args.next() { - return make_error( - "unexpected argument, expected either an ident or no arguments", - tt.span(), - ); - }; - - let mac_name = if let Some(mac_name) = mac_name { - Ident::new(&format!("__inline_mac_{mac_name}"), Span::call_site()) - } else { - let mut input = match LookaheadIter::new(input.clone().into_iter()) { - Some(x) => x, - None => return input, - }; - loop { - match input.next() { - None => break Ident::new("__inline_mac", Span::call_site()), - Some(TT::Ident(kind)) => match &*kind.to_string() { - "impl" => break Ident::new("__inline_mac_impl", Span::call_site()), - kind @ ("struct" | "enum" | "union" | "fn" | "mod" | "trait" | "type" | "const" | "static") => { - if let TT::Ident(name) = &input.tt { - break Ident::new(&format!("__inline_mac_{kind}_{name}"), Span::call_site()); - } else { - break Ident::new(&format!("__inline_mac_{kind}"), Span::call_site()); - } - }, - _ => {}, - }, - _ => {}, - } - } - }; - - let mut expander = Expander::default(); - let mut mac = MacWriter::new(mac_name); - if let Err(e) = expander.expand(input.into_iter(), &mut mac) { - return e; - } - let mut out = TokenStream::new(); - mac.finish(&mut out); - out.extend(expander.expn); - out -} - -/// Wraps a `TokenStream` iterator with a single token lookahead. -struct LookaheadIter { - tt: TT, - iter: IntoIter, -} -impl LookaheadIter { - fn new(mut iter: IntoIter) -> Option { - iter.next().map(|tt| Self { tt, iter }) - } - - /// Get's the lookahead token, replacing it with the next token in the stream. - /// Note: If there isn't a next token, this will not return the lookahead token. - fn next(&mut self) -> Option { - self.iter.next().map(|tt| mem::replace(&mut self.tt, tt)) - } -} - -/// Builds the macro used to implement all the `inline!` macro calls. -struct MacWriter { - name: Ident, - macros: TokenStream, - next_idx: usize, -} -impl MacWriter { - fn new(name: Ident) -> Self { - Self { - name, - macros: TokenStream::new(), - next_idx: 0, - } - } - - /// Inserts a new `inline!` call. - fn insert(&mut self, name_span: Span, bang_span: Span, body: Group, expander: &mut Expander) -> Result<()> { - let idx = self.next_idx; - self.next_idx += 1; - - let mut inner = Expander::for_arm(idx); - inner.expand(body.stream().into_iter(), self)?; - let new_arm = inner.arm.unwrap(); - - self.macros.extend([ - TT::Group(Group::new(Parenthesis, new_arm.args_def)), - TT::Punct(Punct::new('=', Joint)), - TT::Punct(Punct::new('>', Alone)), - TT::Group(Group::new(Parenthesis, inner.expn)), - TT::Punct(Punct::new(';', Alone)), - ]); - - expander.expn.extend([ - TT::Ident({ - let mut name = self.name.clone(); - name.set_span(name_span); - name - }), - TT::Punct(punct_with_span('!', Alone, bang_span)), - ]); - let mut call_body = TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]); - if let Some(arm) = expander.arm.as_mut() { - if !new_arm.args.is_empty() { - arm.add_sub_args(new_arm.args, &mut call_body); - } - } else { - call_body.extend(new_arm.args); - } - let mut g = Group::new(body.delimiter(), call_body); - g.set_span(body.span()); - expander.expn.extend([TT::Group(g)]); - Ok(()) - } - - /// Creates the macro definition. - fn finish(self, out: &mut TokenStream) { - if self.next_idx != 0 { - out.extend([ - TT::Ident(Ident::new("macro_rules", Span::call_site())), - TT::Punct(Punct::new('!', Alone)), - TT::Ident(self.name), - TT::Group(Group::new(Brace, self.macros)), - ]) - } - } -} - -struct MacroArm { - args_def: TokenStream, - args: Vec, -} -impl MacroArm { - fn add_single_arg_def(&mut self, kind: &str, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { - let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); - self.args_def.extend([ - TT::Punct(Punct::new('$', Alone)), - TT::Ident(name.clone()), - TT::Punct(Punct::new(':', Alone)), - TT::Ident(Ident::new(kind, Span::call_site())), - ]); - name.set_span(arg_span); - out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]); - } - - fn add_parenthesized_arg_def(&mut self, kind: Ident, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { - let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); - self.args_def.extend([TT::Group(Group::new( - Parenthesis, - TokenStream::from_iter([ - TT::Punct(Punct::new('$', Alone)), - TT::Ident(name.clone()), - TT::Punct(Punct::new(':', Alone)), - TT::Ident(kind), - ]), - ))]); - name.set_span(arg_span); - out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]); - } - - fn add_multi_arg_def(&mut self, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { - let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); - self.args_def.extend([TT::Group(Group::new( - Parenthesis, - TokenStream::from_iter([ - TT::Punct(Punct::new('$', Alone)), - TT::Group(Group::new( - Parenthesis, - TokenStream::from_iter([ - TT::Punct(Punct::new('$', Alone)), - TT::Ident(name.clone()), - TT::Punct(Punct::new(':', Alone)), - TT::Ident(Ident::new("tt", Span::call_site())), - ]), - )), - TT::Punct(Punct::new('*', Alone)), - ]), - ))]); - name.set_span(arg_span); - out.extend([ - TT::Punct(punct_with_span('$', Alone, dollar_span)), - TT::Group(group_with_span( - Parenthesis, - TokenStream::from_iter([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]), - dollar_span, - )), - TT::Punct(punct_with_span('*', Alone, dollar_span)), - ]); - } - - fn add_arg(&mut self, dollar_span: Span, tt: TT, input: &mut IntoIter, out: &mut TokenStream) -> Result<()> { - match tt { - TT::Punct(p) if p.as_char() == ESCAPE_CHAR => out.extend([TT::Punct(p)]), - TT::Punct(p) if p.as_char() == '\'' && p.spacing() == Joint => { - let lt_name = expect_tt( - input.next(), - |tt| match tt { - TT::Ident(x) => Some(x), - _ => None, - }, - "lifetime name", - p.span(), - )?; - let arg_span = p.span().join(lt_name.span()).unwrap_or(p.span()); - self.add_single_arg_def("lifetime", dollar_span, arg_span, out); - self.args.extend([TT::Punct(p), TT::Ident(lt_name)]); - }, - TT::Ident(x) => { - self.add_single_arg_def("ident", dollar_span, x.span(), out); - self.args.push(TT::Ident(x)); - }, - TT::Literal(x) => { - self.add_single_arg_def("literal", dollar_span, x.span(), out); - self.args.push(TT::Literal(x)); - }, - TT::Group(g) if g.delimiter() == Parenthesis => { - let mut inner = g.stream().into_iter(); - if let Some(TT::Punct(p)) = inner.next() - && p.as_char() == '@' - { - let kind = expect_tt( - inner.next(), - |tt| match tt { - TT::Ident(kind) => Some(kind), - _ => None, - }, - "a macro fragment specifier", - p.span(), - )?; - self.add_parenthesized_arg_def(kind, dollar_span, g.span(), out); - self.args.push(TT::Group(group_with_span(Parenthesis, inner.collect(), g.span()))) - } else { - self.add_multi_arg_def(dollar_span, g.span(), out); - self.args.push(TT::Group(g)); - } - }, - tt => return Err(make_error("unsupported escape", tt.span())), - }; - Ok(()) - } - - fn add_sub_args(&mut self, args: Vec, out: &mut TokenStream) { - self.add_multi_arg_def(Span::call_site(), Span::call_site(), out); - self.args - .extend([TT::Group(Group::new(Parenthesis, TokenStream::from_iter(args)))]); - } -} - -#[derive(Default)] -struct Expander { - arm: Option, - expn: TokenStream, -} -impl Expander { - fn for_arm(idx: usize) -> Self { - Self { - arm: Some(MacroArm { - args_def: TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]), - args: Vec::new(), - }), - expn: TokenStream::new(), - } - } - - fn write_tt(&mut self, tt: TT, mac: &mut MacWriter) -> Result<()> { - match tt { - TT::Group(g) => { - let outer = mem::take(&mut self.expn); - self.expand(g.stream().into_iter(), mac)?; - let inner = mem::replace(&mut self.expn, outer); - self.expn - .extend([TT::Group(group_with_span(g.delimiter(), inner, g.span()))]); - }, - tt => self.expn.extend([tt]), - } - Ok(()) - } - - fn expand(&mut self, input: IntoIter, mac: &mut MacWriter) -> Result<()> { - let Some(mut input) = LookaheadIter::new(input) else { - return Ok(()); - }; - while let Some(tt) = input.next() { - if let TT::Punct(p) = &tt - && p.as_char() == ESCAPE_CHAR - && let Some(arm) = self.arm.as_mut() - { - arm.add_arg(p.span(), mem::replace(&mut input.tt, tt), &mut input.iter, &mut self.expn)?; - if input.next().is_none() { - return Ok(()); - } - } else if let TT::Punct(p) = &input.tt - && p.as_char() == '!' - && let TT::Ident(name) = &tt - && name.to_string() == "inline" - { - let g = expect_tt( - input.iter.next(), - |tt| match tt { - TT::Group(g) => Some(g), - _ => None, - }, - "macro arguments", - p.span(), - )?; - mac.insert(name.span(), p.span(), g, self)?; - if input.next().is_none() { - return Ok(()); - } - } else { - self.write_tt(tt, mac)?; - } - } - self.write_tt(input.tt, mac) - } -} diff --git a/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/tests/ui-toml/excessive_nesting/excessive_nesting.rs index 25f0d0d6230a7..d737a832dd169 100644 --- a/tests/ui-toml/excessive_nesting/excessive_nesting.rs +++ b/tests/ui-toml/excessive_nesting/excessive_nesting.rs @@ -1,4 +1,4 @@ -//@aux-build:proc_macros.rs +//@aux-build:../../ui/auxiliary/proc_macros.rs #![rustfmt::skip] #![feature(custom_inner_attributes)] #![allow(unused)] @@ -156,7 +156,7 @@ fn main() { for i in {{{{xx}}}} {{{{{{{{}}}}}}}} while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} - + while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} }; 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 cdabe6460cdca..b97bb144468b0 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -28,6 +28,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect disallowed-types doc-valid-idents enable-raw-pointer-heuristic-for-send + enforce-iter-loop-reborrow enforced-import-renames enum-variant-name-threshold enum-variant-size-threshold @@ -99,6 +100,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect disallowed-types doc-valid-idents enable-raw-pointer-heuristic-for-send + enforce-iter-loop-reborrow enforced-import-renames enum-variant-name-threshold enum-variant-size-threshold diff --git a/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs b/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs deleted file mode 100644 index 1c591fc76f3e9..0000000000000 --- a/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs +++ /dev/null @@ -1,13 +0,0 @@ -extern crate proc_macro; - -use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree}; - -#[proc_macro] -pub fn unsafe_block(input: TokenStream) -> TokenStream { - let span = input.into_iter().next().unwrap().span(); - TokenStream::from_iter([TokenTree::Ident(Ident::new("unsafe", span)), { - let mut group = Group::new(Delimiter::Brace, TokenStream::new()); - group.set_span(span); - TokenTree::Group(group) - }]) -} diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index c0976f0d6007b..b28e1b7d1802d 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -1,4 +1,4 @@ -//@aux-build:proc_macro_unsafe.rs +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs #![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] #![allow(deref_nullptr, clippy::let_unit_value, clippy::missing_safety_doc)] @@ -564,4 +564,18 @@ fn issue_8679() { unsafe {} } +mod issue_11246 { + // Safety: foo + const _: () = unsafe {}; + + // Safety: A safety comment + const FOO: () = unsafe {}; + + // Safety: bar + static BAR: u8 = unsafe { 0 }; +} + +// Safety: Another safety comment +const FOO: () = unsafe {}; + fn main() {} diff --git a/tests/ui/bool_comparison.fixed b/tests/ui/bool_comparison.fixed index db85247f47d24..e3f2ca72d1cbd 100644 --- a/tests/ui/bool_comparison.fixed +++ b/tests/ui/bool_comparison.fixed @@ -1,6 +1,6 @@ #![allow(clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; diff --git a/tests/ui/bool_comparison.rs b/tests/ui/bool_comparison.rs index 0915f88544bc7..d1bc20d6831a5 100644 --- a/tests/ui/bool_comparison.rs +++ b/tests/ui/bool_comparison.rs @@ -1,6 +1,6 @@ #![allow(clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; diff --git a/tests/ui/cast_size_32bit.stderr b/tests/ui/cast_size.32bit.stderr similarity index 78% rename from tests/ui/cast_size_32bit.stderr rename to tests/ui/cast_size.32bit.stderr index fb51783a48788..379ca60862b9c 100644 --- a/tests/ui/cast_size_32bit.stderr +++ b/tests/ui/cast_size.32bit.stderr @@ -1,44 +1,46 @@ error: casting `isize` to `i8` may truncate the value - --> $DIR/cast_size_32bit.rs:12:5 + --> $DIR/cast_size.rs:15:5 | LL | 1isize as i8; | ^^^^^^^^^^^^ | = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]` help: ... or use `try_from` and handle the error accordingly | LL | i8::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size_32bit.rs:15:5 + --> $DIR/cast_size.rs:18:5 | LL | x0 as f64; | ^^^^^^^^^ | = note: `-D clippy::cast-precision-loss` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]` error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size_32bit.rs:16:5 + --> $DIR/cast_size.rs:19:5 | LL | x1 as f64; | ^^^^^^^^^ error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size_32bit.rs:17:5 + --> $DIR/cast_size.rs:20:5 | LL | x0 as f32; | ^^^^^^^^^ error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size_32bit.rs:18:5 + --> $DIR/cast_size.rs:21:5 | LL | x1 as f32; | ^^^^^^^^^ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:19:5 + --> $DIR/cast_size.rs:22:5 | LL | 1isize as i32; | ^^^^^^^^^^^^^ @@ -50,7 +52,7 @@ LL | i32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:20:5 + --> $DIR/cast_size.rs:23:5 | LL | 1isize as u32; | ^^^^^^^^^^^^^ @@ -62,7 +64,7 @@ LL | u32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:21:5 + --> $DIR/cast_size.rs:24:5 | LL | 1usize as u32; | ^^^^^^^^^^^^^ @@ -74,7 +76,7 @@ LL | u32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:22:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -86,15 +88,16 @@ LL | i32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:22:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ | = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:24:5 + --> $DIR/cast_size.rs:26:5 | LL | 1i64 as isize; | ^^^^^^^^^^^^^ @@ -106,7 +109,7 @@ LL | isize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:25:5 + --> $DIR/cast_size.rs:27:5 | LL | 1i64 as usize; | ^^^^^^^^^^^^^ @@ -118,7 +121,7 @@ LL | usize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:26:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ @@ -130,13 +133,13 @@ LL | isize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:26:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:27:5 + --> $DIR/cast_size.rs:29:5 | LL | 1u64 as usize; | ^^^^^^^^^^^^^ @@ -148,24 +151,31 @@ LL | usize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:28:5 + --> $DIR/cast_size.rs:30:5 | LL | 1u32 as isize; | ^^^^^^^^^^^^^ error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size_32bit.rs:33:5 + --> $DIR/cast_size.rs:35:5 | LL | 999_999_999 as f32; | ^^^^^^^^^^^^^^^^^^ -error: casting integer literal to `f64` is unnecessary - --> $DIR/cast_size_32bit.rs:34:5 +error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size.rs:36:5 + | +LL | 9_999_999_999_999_999usize as f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: literal out of range for `usize` + --> $DIR/cast_size.rs:36:5 | -LL | 3_999_999_999usize as f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3_999_999_999_f64` +LL | 9_999_999_999_999_999usize as f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + = note: the literal `9_999_999_999_999_999usize` does not fit into the type `usize` whose range is `0..=4294967295` + = note: `#[deny(overflowing_literals)]` on by default -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/cast_size.stderr b/tests/ui/cast_size.64bit.stderr similarity index 94% rename from tests/ui/cast_size.stderr rename to tests/ui/cast_size.64bit.stderr index bc9224be64401..7fae92b12503d 100644 --- a/tests/ui/cast_size.stderr +++ b/tests/ui/cast_size.64bit.stderr @@ -1,5 +1,5 @@ error: casting `isize` to `i8` may truncate the value - --> $DIR/cast_size.rs:12:5 + --> $DIR/cast_size.rs:15:5 | LL | 1isize as i8; | ^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL | i8::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size.rs:16:5 + --> $DIR/cast_size.rs:18:5 | LL | x0 as f64; | ^^^^^^^^^ @@ -28,19 +28,19 @@ LL | x1 as f64; | ^^^^^^^^^ error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size.rs:21:5 + --> $DIR/cast_size.rs:20:5 | LL | x0 as f32; | ^^^^^^^^^ error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size.rs:23:5 + --> $DIR/cast_size.rs:21:5 | LL | x1 as f32; | ^^^^^^^^^ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:25:5 + --> $DIR/cast_size.rs:22:5 | LL | 1isize as i32; | ^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | i32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:27:5 + --> $DIR/cast_size.rs:23:5 | LL | 1isize as u32; | ^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | u32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:29:5 + --> $DIR/cast_size.rs:24:5 | LL | 1usize as u32; | ^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | u32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:31:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | i32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:31:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | 1usize as i32; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:36:5 + --> $DIR/cast_size.rs:26:5 | LL | 1i64 as isize; | ^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | isize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:38:5 + --> $DIR/cast_size.rs:27:5 | LL | 1i64 as usize; | ^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | usize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:40:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ @@ -133,13 +133,13 @@ LL | isize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:40:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:43:5 + --> $DIR/cast_size.rs:29:5 | LL | 1u64 as usize; | ^^^^^^^^^^^^^ @@ -151,19 +151,19 @@ LL | usize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:45:5 + --> $DIR/cast_size.rs:30:5 | LL | 1u32 as isize; | ^^^^^^^^^^^^^ error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size.rs:51:5 + --> $DIR/cast_size.rs:35:5 | LL | 999_999_999 as f32; | ^^^^^^^^^^^^^^^^^^ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size.rs:53:5 + --> $DIR/cast_size.rs:36:5 | LL | 9_999_999_999_999_999usize as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/cast_size.rs b/tests/ui/cast_size.rs index 95626b20b27bf..d063a70ccdfcf 100644 --- a/tests/ui/cast_size.rs +++ b/tests/ui/cast_size.rs @@ -1,56 +1,37 @@ -//@ignore-32bit -#[warn( +//@stderr-per-bitwidth +//@no-rustfix + +#![warn( clippy::cast_precision_loss, clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_possible_wrap, clippy::cast_lossless )] -#[allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + fn main() { // Casting from *size 1isize as i8; - //~^ ERROR: casting `isize` to `i8` may truncate the value let x0 = 1isize; let x1 = 1usize; x0 as f64; - //~^ ERROR: casting `isize` to `f64` causes a loss of precision on targets with 64-bit - //~| NOTE: `-D clippy::cast-precision-loss` implied by `-D warnings` x1 as f64; - //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit x0 as f32; - //~^ ERROR: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 b x1 as f32; - //~^ ERROR: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 b 1isize as i32; - //~^ ERROR: casting `isize` to `i32` may truncate the value on targets with 64-bit wid 1isize as u32; - //~^ ERROR: casting `isize` to `u32` may truncate the value on targets with 64-bit wid 1usize as u32; - //~^ ERROR: casting `usize` to `u32` may truncate the value on targets with 64-bit wid 1usize as i32; - //~^ ERROR: casting `usize` to `i32` may truncate the value on targets with 64-bit wid - //~| ERROR: casting `usize` to `i32` may wrap around the value on targets with 32-bit - //~| NOTE: `-D clippy::cast-possible-wrap` implied by `-D warnings` - // Casting to *size 1i64 as isize; - //~^ ERROR: casting `i64` to `isize` may truncate the value on targets with 32-bit wid 1i64 as usize; - //~^ ERROR: casting `i64` to `usize` may truncate the value on targets with 32-bit wid 1u64 as isize; - //~^ ERROR: casting `u64` to `isize` may truncate the value on targets with 32-bit wid - //~| ERROR: casting `u64` to `isize` may wrap around the value on targets with 64-bit 1u64 as usize; - //~^ ERROR: casting `u64` to `usize` may truncate the value on targets with 32-bit wid 1u32 as isize; - //~^ ERROR: casting `u32` to `isize` may wrap around the value on targets with 32-bit 1u32 as usize; // Should not trigger any lint 1i32 as isize; // Neither should this 1i32 as usize; // Big integer literal to float 999_999_999 as f32; - //~^ ERROR: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, 9_999_999_999_999_999usize as f64; - //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit } -//@no-rustfix diff --git a/tests/ui/cast_size_32bit.rs b/tests/ui/cast_size_32bit.rs deleted file mode 100644 index 5a06e34bdb8fb..0000000000000 --- a/tests/ui/cast_size_32bit.rs +++ /dev/null @@ -1,56 +0,0 @@ -//@ignore-64bit -#[warn( - clippy::cast_precision_loss, - clippy::cast_possible_truncation, - clippy::cast_sign_loss, - clippy::cast_possible_wrap, - clippy::cast_lossless -)] -#[allow(clippy::no_effect, clippy::unnecessary_operation)] -fn main() { - // Casting from *size - 1isize as i8; - //~^ ERROR: casting `isize` to `i8` may truncate the value - let x0 = 1isize; - let x1 = 1usize; - x0 as f64; - //~^ ERROR: casting `isize` to `f64` causes a loss of precision on targets with 64-bit - //~| NOTE: `-D clippy::cast-precision-loss` implied by `-D warnings` - x1 as f64; - //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit - x0 as f32; - //~^ ERROR: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 b - x1 as f32; - //~^ ERROR: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 b - 1isize as i32; - //~^ ERROR: casting `isize` to `i32` may truncate the value on targets with 64-bit wid - 1isize as u32; - //~^ ERROR: casting `isize` to `u32` may truncate the value on targets with 64-bit wid - 1usize as u32; - //~^ ERROR: casting `usize` to `u32` may truncate the value on targets with 64-bit wid - 1usize as i32; - //~^ ERROR: casting `usize` to `i32` may truncate the value on targets with 64-bit wid - //~| ERROR: casting `usize` to `i32` may wrap around the value on targets with 32-bit - //~| NOTE: `-D clippy::cast-possible-wrap` implied by `-D warnings` - // Casting to *size - 1i64 as isize; - //~^ ERROR: casting `i64` to `isize` may truncate the value on targets with 32-bit wid - 1i64 as usize; - //~^ ERROR: casting `i64` to `usize` may truncate the value on targets with 32-bit wid - 1u64 as isize; - //~^ ERROR: casting `u64` to `isize` may truncate the value on targets with 32-bit wid - //~| ERROR: casting `u64` to `isize` may wrap around the value on targets with 64-bit - 1u64 as usize; - //~^ ERROR: casting `u64` to `usize` may truncate the value on targets with 32-bit wid - 1u32 as isize; - //~^ ERROR: casting `u32` to `isize` may wrap around the value on targets with 32-bit - 1u32 as usize; // Should not trigger any lint - 1i32 as isize; // Neither should this - 1i32 as usize; - // Big integer literal to float - 999_999_999 as f32; - //~^ ERROR: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, - 3_999_999_999usize as f64; - //~^ ERROR: casting integer literal to `f64` is unnecessary - //~| NOTE: `-D clippy::unnecessary-cast` implied by `-D warnings` -} diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index e82e7bcb06cb0..02f80cc52ac72 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -128,6 +128,57 @@ fn main() { assert!(x.is_ok(), "{:?}", x.unwrap_err()); } +fn issue11371() { + let option = Some(()); + + if option.is_some() { + option.as_ref().unwrap(); + //~^ ERROR: called `unwrap` on `option` after checking its variant with `is_some` + } else { + option.as_ref().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + let result = Ok::<(), ()>(()); + + if result.is_ok() { + result.as_ref().unwrap(); + //~^ ERROR: called `unwrap` on `result` after checking its variant with `is_ok` + } else { + result.as_ref().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + let mut option = Some(()); + if option.is_some() { + option.as_mut().unwrap(); + //~^ ERROR: called `unwrap` on `option` after checking its variant with `is_some` + } else { + option.as_mut().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + let mut result = Ok::<(), ()>(()); + if result.is_ok() { + result.as_mut().unwrap(); + //~^ ERROR: called `unwrap` on `result` after checking its variant with `is_ok` + } else { + result.as_mut().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + // This should not lint. Statics are, at the time of writing, not linted on anyway, + // but if at some point they are supported by this lint, it should correctly see that + // `X` is being mutated and not suggest `if let Some(..) = X {}` + static mut X: Option = Some(123); + unsafe { + if X.is_some() { + X = None; + X.unwrap(); + } + } +} + fn check_expect() { let x = Some(()); if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index ed603581ecddb..a5afbba7317e9 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -168,5 +168,73 @@ LL | if x.is_err() { LL | x.unwrap_err(); | ^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: called `unwrap` on `option` after checking its variant with `is_some` + --> $DIR/simple_conditionals.rs:135:9 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some(..) = &option` +LL | option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:138:9 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> $DIR/simple_conditionals.rs:145:9 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok(..) = &result` +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:148:9 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `option` after checking its variant with `is_some` + --> $DIR/simple_conditionals.rs:154:9 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some(..) = &mut option` +LL | option.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:157:9 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | option.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> $DIR/simple_conditionals.rs:163:9 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok(..) = &mut result` +LL | result.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:166:9 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | result.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors diff --git a/tests/ui/clone_on_copy_impl.rs b/tests/ui/clone_on_copy_impl.rs index b7c186bef7724..2d03544ad8b6b 100644 --- a/tests/ui/clone_on_copy_impl.rs +++ b/tests/ui/clone_on_copy_impl.rs @@ -1,4 +1,4 @@ -#![allow(clippy::incorrect_clone_impl_on_copy_type)] +#![allow(clippy::non_canonical_clone_impl)] use std::fmt; use std::marker::PhantomData; diff --git a/tests/ui/crashes/ice-11337.rs b/tests/ui/crashes/ice-11337.rs new file mode 100644 index 0000000000000..0bed4035f6bf4 --- /dev/null +++ b/tests/ui/crashes/ice-11337.rs @@ -0,0 +1,9 @@ +#![feature(trait_alias)] + +trait Confusing = Fn(i32) where F: Fn(u32); + +fn alias, F>(_: T, _: F) {} + +fn main() { + alias(|_| {}, |_| {}); +} diff --git a/tests/ui/crashes/ice-11422.fixed b/tests/ui/crashes/ice-11422.fixed new file mode 100644 index 0000000000000..ca5721cbb2bae --- /dev/null +++ b/tests/ui/crashes/ice-11422.fixed @@ -0,0 +1,25 @@ +#![warn(clippy::implied_bounds_in_impls)] + +use std::fmt::Debug; +use std::ops::*; + +fn gen() -> impl PartialOrd + Debug {} + +struct Bar {} +trait Foo {} +trait FooNested> {} +impl Foo for Bar {} +impl FooNested for Bar {} + +fn foo() -> impl Foo + FooNested { + Bar {} +} + +fn test_impl_ops() -> impl Add + Sub + Mul + Div { + 1 +} +fn test_impl_assign_ops() -> impl AddAssign + SubAssign + MulAssign + DivAssign { + 1 +} + +fn main() {} diff --git a/tests/ui/crashes/ice-11422.rs b/tests/ui/crashes/ice-11422.rs new file mode 100644 index 0000000000000..355ec2480bba4 --- /dev/null +++ b/tests/ui/crashes/ice-11422.rs @@ -0,0 +1,25 @@ +#![warn(clippy::implied_bounds_in_impls)] + +use std::fmt::Debug; +use std::ops::*; + +fn gen() -> impl PartialOrd + PartialEq + Debug {} + +struct Bar {} +trait Foo {} +trait FooNested> {} +impl Foo for Bar {} +impl FooNested for Bar {} + +fn foo() -> impl Foo + FooNested { + Bar {} +} + +fn test_impl_ops() -> impl Add + Sub + Mul + Div { + 1 +} +fn test_impl_assign_ops() -> impl AddAssign + SubAssign + MulAssign + DivAssign { + 1 +} + +fn main() {} diff --git a/tests/ui/crashes/ice-11422.stderr b/tests/ui/crashes/ice-11422.stderr new file mode 100644 index 0000000000000..fb80b5b147f57 --- /dev/null +++ b/tests/ui/crashes/ice-11422.stderr @@ -0,0 +1,16 @@ +error: this bound is already specified as the supertrait of `PartialOrd` + --> $DIR/ice-11422.rs:6:31 + | +LL | fn gen() -> impl PartialOrd + PartialEq + Debug {} + | ^^^^^^^^^ + | + = note: `-D clippy::implied-bounds-in-impls` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::implied_bounds_in_impls)]` +help: try removing this bound + | +LL - fn gen() -> impl PartialOrd + PartialEq + Debug {} +LL + fn gen() -> impl PartialOrd + Debug {} + | + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-360.rs b/tests/ui/crashes/ice-360.rs index 28589e1efeda5..0d10932b0987e 100644 --- a/tests/ui/crashes/ice-360.rs +++ b/tests/ui/crashes/ice-360.rs @@ -3,7 +3,8 @@ fn main() {} fn no_panic(slice: &[T]) { let mut iter = slice.iter(); loop { - //~^ ERROR: this loop could be written as a `while let` loop + //~^ ERROR: this loop never actually loops + //~| ERROR: this loop could be written as a `while let` loop //~| NOTE: `-D clippy::while-let-loop` implied by `-D warnings` let _ = match iter.next() { Some(ele) => ele, diff --git a/tests/ui/crashes/ice-360.stderr b/tests/ui/crashes/ice-360.stderr index dd016355b5325..a84697a9f291d 100644 --- a/tests/ui/crashes/ice-360.stderr +++ b/tests/ui/crashes/ice-360.stderr @@ -1,10 +1,24 @@ +error: this loop never actually loops + --> $DIR/ice-360.rs:5:5 + | +LL | / loop { +LL | | +LL | | +LL | | +... | +LL | | +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default + error: this loop could be written as a `while let` loop --> $DIR/ice-360.rs:5:5 | LL | / loop { LL | | LL | | -LL | | let _ = match iter.next() { +LL | | ... | LL | | LL | | } @@ -14,7 +28,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::while_let_loop)]` error: empty `loop {}` wastes CPU cycles - --> $DIR/ice-360.rs:12:9 + --> $DIR/ice-360.rs:13:9 | LL | loop {} | ^^^^^^^ @@ -23,5 +37,5 @@ LL | loop {} = note: `-D clippy::empty-loop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::empty_loop)]` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/derivable_impls.fixed b/tests/ui/derivable_impls.fixed index 6cc202414f5f4..68c5a5c5ca457 100644 --- a/tests/ui/derivable_impls.fixed +++ b/tests/ui/derivable_impls.fixed @@ -287,4 +287,17 @@ mod issue10158 { } } +mod issue11368 { + pub struct A { + a: u32, + } + + impl Default for A { + #[track_caller] + fn default() -> Self { + Self { a: 0 } + } + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs index 0aa9acd752dcc..21d73ba8b7778 100644 --- a/tests/ui/derivable_impls.rs +++ b/tests/ui/derivable_impls.rs @@ -323,4 +323,17 @@ mod issue10158 { } } +mod issue11368 { + pub struct A { + a: u32, + } + + impl Default for A { + #[track_caller] + fn default() -> Self { + Self { a: 0 } + } + } +} + fn main() {} diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index 310c701765ba9..20ac8a6e6be35 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -1,8 +1,4 @@ -#![allow( - clippy::incorrect_clone_impl_on_copy_type, - clippy::incorrect_partial_ord_impl_on_ord_type, - dead_code -)] +#![allow(clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, dead_code)] #![warn(clippy::expl_impl_clone_on_copy)] diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index e9b2ee34760f2..88942d9543226 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -1,5 +1,5 @@ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:12:1 + --> $DIR/derive.rs:8:1 | LL | / impl Clone for Qux { LL | | @@ -10,7 +10,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:12:1 + --> $DIR/derive.rs:8:1 | LL | / impl Clone for Qux { LL | | @@ -23,7 +23,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:37:1 + --> $DIR/derive.rs:33:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -34,7 +34,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:37:1 + --> $DIR/derive.rs:33:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -45,7 +45,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:49:1 + --> $DIR/derive.rs:45:1 | LL | / impl Clone for BigArray { LL | | @@ -56,7 +56,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:49:1 + --> $DIR/derive.rs:45:1 | LL | / impl Clone for BigArray { LL | | @@ -67,7 +67,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:61:1 + --> $DIR/derive.rs:57:1 | LL | / impl Clone for FnPtr { LL | | @@ -78,7 +78,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:61:1 + --> $DIR/derive.rs:57:1 | LL | / impl Clone for FnPtr { LL | | @@ -89,7 +89,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:82:1 + --> $DIR/derive.rs:78:1 | LL | / impl Clone for Generic2 { LL | | @@ -100,7 +100,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:82:1 + --> $DIR/derive.rs:78:1 | LL | / impl Clone for Generic2 { LL | | diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 2c19942d420c9..1c7e6d1c20230 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -1,6 +1,6 @@ #![warn(clippy::derive_ord_xor_partial_ord)] #![allow(clippy::unnecessary_wraps)] -#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] +#![allow(clippy::non_canonical_partial_ord_impl)] use std::cmp::Ordering; diff --git a/tests/ui/empty_loop.rs b/tests/ui/empty_loop.rs index 54e8fb4907c0f..be347563135c3 100644 --- a/tests/ui/empty_loop.rs +++ b/tests/ui/empty_loop.rs @@ -7,10 +7,12 @@ use proc_macros::{external, inline_macros}; fn should_trigger() { loop {} + #[allow(clippy::never_loop)] loop { loop {} } + #[allow(clippy::never_loop)] 'outer: loop { 'inner: loop {} } @@ -18,6 +20,7 @@ fn should_trigger() { #[inline_macros] fn should_not_trigger() { + #[allow(clippy::never_loop)] loop { panic!("This is fine") } diff --git a/tests/ui/empty_loop.stderr b/tests/ui/empty_loop.stderr index 84d7d61c7daa8..113556f673c8c 100644 --- a/tests/ui/empty_loop.stderr +++ b/tests/ui/empty_loop.stderr @@ -9,7 +9,7 @@ LL | loop {} = help: to override `-D warnings` add `#[allow(clippy::empty_loop)]` error: empty `loop {}` wastes CPU cycles - --> $DIR/empty_loop.rs:11:9 + --> $DIR/empty_loop.rs:12:9 | LL | loop {} | ^^^^^^^ @@ -17,7 +17,7 @@ LL | loop {} = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body error: empty `loop {}` wastes CPU cycles - --> $DIR/empty_loop.rs:15:9 + --> $DIR/empty_loop.rs:17:9 | LL | 'inner: loop {} | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/enum_clike_unportable_variant.rs b/tests/ui/enum_clike_unportable_variant.rs index 964e5634ddb11..c50404c5047bb 100644 --- a/tests/ui/enum_clike_unportable_variant.rs +++ b/tests/ui/enum_clike_unportable_variant.rs @@ -1,4 +1,4 @@ -//@ignore-target-x86 +//@ignore-32bit #![warn(clippy::enum_clike_unportable_variant)] #![allow(unused, non_upper_case_globals)] diff --git a/tests/ui/enum_clike_unportable_variant.stderr b/tests/ui/enum_clike_unportable_variant.stderr index 5935eea5e036e..93ad4daa97f77 100644 --- a/tests/ui/enum_clike_unportable_variant.stderr +++ b/tests/ui/enum_clike_unportable_variant.stderr @@ -5,51 +5,52 @@ LL | X = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::enum_clike_unportable_variant)]` error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:15:5 + --> $DIR/enum_clike_unportable_variant.rs:17:5 | LL | X = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:18:5 + --> $DIR/enum_clike_unportable_variant.rs:21:5 | LL | A = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:25:5 + --> $DIR/enum_clike_unportable_variant.rs:29:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:26:5 + --> $DIR/enum_clike_unportable_variant.rs:31:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:28:5 + --> $DIR/enum_clike_unportable_variant.rs:34:5 | LL | C = (i32::MIN as isize) - 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:34:5 + --> $DIR/enum_clike_unportable_variant.rs:41:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:35:5 + --> $DIR/enum_clike_unportable_variant.rs:43:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:40:5 + --> $DIR/enum_clike_unportable_variant.rs:49:5 | LL | X = ::Number, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index 86bee11eb8774..12158d0d12a2e 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -205,7 +205,7 @@ fn main() { } } - f_str(&&ref_str); // `needless_borrow` will suggest removing both references + f_str(&ref_str); // `needless_borrow` will suggest removing both references f_str(&ref_str); // `needless_borrow` will suggest removing only one reference let x = &&40; @@ -293,4 +293,32 @@ fn main() { fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc { *x } + + // Issue #11366 + let _: &mut u32 = match &mut Some(&mut 0u32) { + Some(x) => x, + None => panic!(), + }; + + // Issue #11474 + pub struct Variant { + pub anonymous: Variant0, + } + + pub union Variant0 { + pub anonymous: std::mem::ManuallyDrop, + } + + pub struct Variant00 { + pub anonymous: Variant000, + } + + pub union Variant000 { + pub val: i32, + } + + unsafe { + let mut p = core::mem::zeroed::(); + (*p.anonymous.anonymous).anonymous.val = 1; + } } diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index 7a505bdf558c3..dec021c183443 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -293,4 +293,32 @@ fn main() { fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc { *x } + + // Issue #11366 + let _: &mut u32 = match &mut Some(&mut 0u32) { + Some(x) => &mut *x, + None => panic!(), + }; + + // Issue #11474 + pub struct Variant { + pub anonymous: Variant0, + } + + pub union Variant0 { + pub anonymous: std::mem::ManuallyDrop, + } + + pub struct Variant00 { + pub anonymous: Variant000, + } + + pub union Variant000 { + pub val: i32, + } + + unsafe { + let mut p = core::mem::zeroed::(); + (*p.anonymous.anonymous).anonymous.val = 1; + } } diff --git a/tests/ui/explicit_auto_deref.stderr b/tests/ui/explicit_auto_deref.stderr index bea014d8ad555..3d2a7b0d9f4f3 100644 --- a/tests/ui/explicit_auto_deref.stderr +++ b/tests/ui/explicit_auto_deref.stderr @@ -194,10 +194,10 @@ LL | let _ = f_str(**ref_ref_str); | ^^^^^^^^^^^^^ help: try: `ref_ref_str` error: deref which would be done by auto-deref - --> $DIR/explicit_auto_deref.rs:208:13 + --> $DIR/explicit_auto_deref.rs:208:12 | LL | f_str(&&*ref_str); // `needless_borrow` will suggest removing both references - | ^^^^^^^^ help: try: `ref_str` + | ^^^^^^^^^ help: try: `ref_str` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:209:12 @@ -235,5 +235,11 @@ error: deref which would be done by auto-deref LL | *x | ^^ help: try: `x` -error: aborting due to 39 previous errors +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:299:20 + | +LL | Some(x) => &mut *x, + | ^^^^^^^ help: try: `x` + +error: aborting due to 40 previous errors diff --git a/tests/ui/explicit_iter_loop.fixed b/tests/ui/explicit_iter_loop.fixed index 57292114e3882..f08397defa539 100644 --- a/tests/ui/explicit_iter_loop.fixed +++ b/tests/ui/explicit_iter_loop.fixed @@ -4,6 +4,7 @@ clippy::similar_names, clippy::needless_borrow, clippy::deref_addrof, + clippy::unnecessary_mut_passed, dead_code )] @@ -20,15 +21,15 @@ fn main() { for _ in rvec {} let rmvec = &mut vec; - for _ in &*rmvec {} - for _ in &mut *rmvec {} + for _ in rmvec.iter() {} + for _ in rmvec.iter_mut() {} for _ in &vec {} // these are fine for _ in &mut vec {} // these are fine for _ in &[1, 2, 3] {} - for _ in &*(&mut [1, 2, 3]) {} + for _ in (&mut [1, 2, 3]).iter() {} for _ in &[0; 32] {} for _ in &[0; 33] {} diff --git a/tests/ui/explicit_iter_loop.rs b/tests/ui/explicit_iter_loop.rs index 66280c23843d2..2ee6825d445c2 100644 --- a/tests/ui/explicit_iter_loop.rs +++ b/tests/ui/explicit_iter_loop.rs @@ -4,6 +4,7 @@ clippy::similar_names, clippy::needless_borrow, clippy::deref_addrof, + clippy::unnecessary_mut_passed, dead_code )] diff --git a/tests/ui/explicit_iter_loop.stderr b/tests/ui/explicit_iter_loop.stderr index af46d74e989af..725d9b63cf8d6 100644 --- a/tests/ui/explicit_iter_loop.stderr +++ b/tests/ui/explicit_iter_loop.stderr @@ -1,5 +1,5 @@ error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:16:14 + --> $DIR/explicit_iter_loop.rs:17:14 | LL | for _ in vec.iter() {} | ^^^^^^^^^^ help: to write this more concisely, try: `&vec` @@ -11,133 +11,106 @@ LL | #![deny(clippy::explicit_iter_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:17:14 + --> $DIR/explicit_iter_loop.rs:18:14 | LL | for _ in vec.iter_mut() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:20:14 + --> $DIR/explicit_iter_loop.rs:21:14 | LL | for _ in rvec.iter() {} | ^^^^^^^^^^^ help: to write this more concisely, try: `rvec` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:23:14 - | -LL | for _ in rmvec.iter() {} - | ^^^^^^^^^^^^ help: to write this more concisely, try: `&*rmvec` - -error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:24:14 - | -LL | for _ in rmvec.iter_mut() {} - | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *rmvec` - -error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:29:14 + --> $DIR/explicit_iter_loop.rs:30:14 | LL | for _ in [1, 2, 3].iter() {} | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:31:14 - | -LL | for _ in (&mut [1, 2, 3]).iter() {} - | ^^^^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&*(&mut [1, 2, 3])` - -error: the method `iter` doesn't need a mutable reference - --> $DIR/explicit_iter_loop.rs:31:14 - | -LL | for _ in (&mut [1, 2, 3]).iter() {} - | ^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` - -error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:33:14 + --> $DIR/explicit_iter_loop.rs:34:14 | LL | for _ in [0; 32].iter() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:34:14 + --> $DIR/explicit_iter_loop.rs:35:14 | LL | for _ in [0; 33].iter() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 33]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:37:14 + --> $DIR/explicit_iter_loop.rs:38:14 | LL | for _ in ll.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&ll` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:39:14 + --> $DIR/explicit_iter_loop.rs:40:14 | LL | for _ in rll.iter() {} | ^^^^^^^^^^ help: to write this more concisely, try: `rll` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:42:14 + --> $DIR/explicit_iter_loop.rs:43:14 | LL | for _ in vd.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&vd` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:44:14 + --> $DIR/explicit_iter_loop.rs:45:14 | LL | for _ in rvd.iter() {} | ^^^^^^^^^^ help: to write this more concisely, try: `rvd` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:47:14 + --> $DIR/explicit_iter_loop.rs:48:14 | LL | for _ in bh.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bh` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:50:14 + --> $DIR/explicit_iter_loop.rs:51:14 | LL | for _ in hm.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hm` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:53:14 + --> $DIR/explicit_iter_loop.rs:54:14 | LL | for _ in bt.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bt` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:56:14 + --> $DIR/explicit_iter_loop.rs:57:14 | LL | for _ in hs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hs` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:59:14 + --> $DIR/explicit_iter_loop.rs:60:14 | LL | for _ in bs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bs` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:148:14 + --> $DIR/explicit_iter_loop.rs:149:14 | LL | for _ in x.iter() {} | ^^^^^^^^ help: to write this more concisely, try: `&x` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:149:14 + --> $DIR/explicit_iter_loop.rs:150:14 | LL | for _ in x.iter_mut() {} | ^^^^^^^^^^^^ help: to write this more concisely, try: `&mut x` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:152:14 + --> $DIR/explicit_iter_loop.rs:153:14 | LL | for _ in r.iter() {} | ^^^^^^^^ help: to write this more concisely, try: `r` -error: aborting due to 22 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/float_cmp.rs b/tests/ui/float_cmp.rs index a547a67430fd2..5057c64373291 100644 --- a/tests/ui/float_cmp.rs +++ b/tests/ui/float_cmp.rs @@ -41,6 +41,16 @@ impl PartialEq for X { } } +impl PartialEq for X { + fn eq(&self, o: &f32) -> bool { + if self.val.is_nan() { + o.is_nan() + } else { + self.val == *o // no error, inside "eq" fn + } + } +} + fn main() { ZERO == 0f32; //no error, comparison with zero is ok 1.0f32 != f32::INFINITY; // also comparison with infinity @@ -48,6 +58,9 @@ fn main() { ZERO == 0.0; //no error, comparison with zero is ok ZERO + ZERO != 1.0; //no error, comparison with zero is ok + let x = X { val: 1.0 }; + x == 1.0; // no error, custom type that implement PartialOrder for float is not checked + ONE == 1f32; ONE == 1.0 + 0.0; ONE + ONE == ZERO + ONE + ONE; diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr index cbe529954d0c2..217e2987917ab 100644 --- a/tests/ui/float_cmp.stderr +++ b/tests/ui/float_cmp.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:57:5 + --> $DIR/float_cmp.rs:70:5 | LL | ONE as f64 != 2.0; | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` @@ -9,7 +9,7 @@ LL | ONE as f64 != 2.0; = help: to override `-D warnings` add `#[allow(clippy::float_cmp)]` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:64:5 + --> $DIR/float_cmp.rs:77:5 | LL | x == 1.0; | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` @@ -17,7 +17,7 @@ LL | x == 1.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:69:5 + --> $DIR/float_cmp.rs:82:5 | LL | twice(x) != twice(ONE as f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` @@ -25,7 +25,7 @@ LL | twice(x) != twice(ONE as f64); = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:91:5 + --> $DIR/float_cmp.rs:104:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` @@ -33,7 +33,7 @@ LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays - --> $DIR/float_cmp.rs:98:5 + --> $DIR/float_cmp.rs:111:5 | LL | a1 == a2; | ^^^^^^^^ @@ -41,7 +41,7 @@ LL | a1 == a2; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:101:5 + --> $DIR/float_cmp.rs:114:5 | LL | a1[0] == a2[0]; | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` diff --git a/tests/ui/fn_to_numeric_cast_32bit.stderr b/tests/ui/fn_to_numeric_cast.32bit.stderr similarity index 70% rename from tests/ui/fn_to_numeric_cast_32bit.stderr rename to tests/ui/fn_to_numeric_cast.32bit.stderr index 671347d2bcd54..ea08d8c9cc162 100644 --- a/tests/ui/fn_to_numeric_cast_32bit.stderr +++ b/tests/ui/fn_to_numeric_cast.32bit.stderr @@ -1,141 +1,143 @@ error: casting function pointer `foo` to `i8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:10:13 + --> $DIR/fn_to_numeric_cast.rs:10:13 | LL | let _ = foo as i8; | ^^^^^^^^^ help: try: `foo as usize` | = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_with_truncation)]` error: casting function pointer `foo` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:11:13 + --> $DIR/fn_to_numeric_cast.rs:11:13 | LL | let _ = foo as i16; | ^^^^^^^^^^ help: try: `foo as usize` -error: casting function pointer `foo` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:12:13 +error: casting function pointer `foo` to `i32` + --> $DIR/fn_to_numeric_cast.rs:12:13 | LL | let _ = foo as i32; | ^^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast)]` error: casting function pointer `foo` to `i64` - --> $DIR/fn_to_numeric_cast_32bit.rs:13:13 + --> $DIR/fn_to_numeric_cast.rs:13:13 | LL | let _ = foo as i64; | ^^^^^^^^^^ help: try: `foo as usize` - | - = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` error: casting function pointer `foo` to `i128` - --> $DIR/fn_to_numeric_cast_32bit.rs:14:13 + --> $DIR/fn_to_numeric_cast.rs:14:13 | LL | let _ = foo as i128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `isize` - --> $DIR/fn_to_numeric_cast_32bit.rs:15:13 + --> $DIR/fn_to_numeric_cast.rs:15:13 | LL | let _ = foo as isize; | ^^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:17:13 + --> $DIR/fn_to_numeric_cast.rs:17:13 | LL | let _ = foo as u8; | ^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:18:13 + --> $DIR/fn_to_numeric_cast.rs:18:13 | LL | let _ = foo as u16; | ^^^^^^^^^^ help: try: `foo as usize` -error: casting function pointer `foo` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:19:13 +error: casting function pointer `foo` to `u32` + --> $DIR/fn_to_numeric_cast.rs:19:13 | LL | let _ = foo as u32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u64` - --> $DIR/fn_to_numeric_cast_32bit.rs:20:13 + --> $DIR/fn_to_numeric_cast.rs:20:13 | LL | let _ = foo as u64; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u128` - --> $DIR/fn_to_numeric_cast_32bit.rs:21:13 + --> $DIR/fn_to_numeric_cast.rs:21:13 | LL | let _ = foo as u128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `abc` to `i8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:34:13 + --> $DIR/fn_to_numeric_cast.rs:34:13 | LL | let _ = abc as i8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:35:13 + --> $DIR/fn_to_numeric_cast.rs:35:13 | LL | let _ = abc as i16; | ^^^^^^^^^^ help: try: `abc as usize` -error: casting function pointer `abc` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:36:13 +error: casting function pointer `abc` to `i32` + --> $DIR/fn_to_numeric_cast.rs:36:13 | LL | let _ = abc as i32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i64` - --> $DIR/fn_to_numeric_cast_32bit.rs:37:13 + --> $DIR/fn_to_numeric_cast.rs:37:13 | LL | let _ = abc as i64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i128` - --> $DIR/fn_to_numeric_cast_32bit.rs:38:13 + --> $DIR/fn_to_numeric_cast.rs:38:13 | LL | let _ = abc as i128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `isize` - --> $DIR/fn_to_numeric_cast_32bit.rs:39:13 + --> $DIR/fn_to_numeric_cast.rs:39:13 | LL | let _ = abc as isize; | ^^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:41:13 + --> $DIR/fn_to_numeric_cast.rs:41:13 | LL | let _ = abc as u8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:42:13 + --> $DIR/fn_to_numeric_cast.rs:42:13 | LL | let _ = abc as u16; | ^^^^^^^^^^ help: try: `abc as usize` -error: casting function pointer `abc` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:43:13 +error: casting function pointer `abc` to `u32` + --> $DIR/fn_to_numeric_cast.rs:43:13 | LL | let _ = abc as u32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u64` - --> $DIR/fn_to_numeric_cast_32bit.rs:44:13 + --> $DIR/fn_to_numeric_cast.rs:44:13 | LL | let _ = abc as u64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u128` - --> $DIR/fn_to_numeric_cast_32bit.rs:45:13 + --> $DIR/fn_to_numeric_cast.rs:45:13 | LL | let _ = abc as u128; | ^^^^^^^^^^^ help: try: `abc as usize` -error: casting function pointer `f` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:52:5 +error: casting function pointer `f` to `i32` + --> $DIR/fn_to_numeric_cast.rs:52:5 | LL | f as i32 | ^^^^^^^^ help: try: `f as usize` diff --git a/tests/ui/fn_to_numeric_cast.stderr b/tests/ui/fn_to_numeric_cast.64bit.stderr similarity index 85% rename from tests/ui/fn_to_numeric_cast.stderr rename to tests/ui/fn_to_numeric_cast.64bit.stderr index 53e8ac3c4b420..62f3bfa70ea50 100644 --- a/tests/ui/fn_to_numeric_cast.stderr +++ b/tests/ui/fn_to_numeric_cast.64bit.stderr @@ -8,19 +8,19 @@ LL | let _ = foo as i8; = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_with_truncation)]` error: casting function pointer `foo` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:13:13 + --> $DIR/fn_to_numeric_cast.rs:11:13 | LL | let _ = foo as i16; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:15:13 + --> $DIR/fn_to_numeric_cast.rs:12:13 | LL | let _ = foo as i32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `i64` - --> $DIR/fn_to_numeric_cast.rs:17:13 + --> $DIR/fn_to_numeric_cast.rs:13:13 | LL | let _ = foo as i64; | ^^^^^^^^^^ help: try: `foo as usize` @@ -29,115 +29,115 @@ LL | let _ = foo as i64; = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast)]` error: casting function pointer `foo` to `i128` - --> $DIR/fn_to_numeric_cast.rs:20:13 + --> $DIR/fn_to_numeric_cast.rs:14:13 | LL | let _ = foo as i128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `isize` - --> $DIR/fn_to_numeric_cast.rs:22:13 + --> $DIR/fn_to_numeric_cast.rs:15:13 | LL | let _ = foo as isize; | ^^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:25:13 + --> $DIR/fn_to_numeric_cast.rs:17:13 | LL | let _ = foo as u8; | ^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:27:13 + --> $DIR/fn_to_numeric_cast.rs:18:13 | LL | let _ = foo as u16; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:29:13 + --> $DIR/fn_to_numeric_cast.rs:19:13 | LL | let _ = foo as u32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u64` - --> $DIR/fn_to_numeric_cast.rs:31:13 + --> $DIR/fn_to_numeric_cast.rs:20:13 | LL | let _ = foo as u64; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u128` - --> $DIR/fn_to_numeric_cast.rs:33:13 + --> $DIR/fn_to_numeric_cast.rs:21:13 | LL | let _ = foo as u128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `abc` to `i8`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:47:13 + --> $DIR/fn_to_numeric_cast.rs:34:13 | LL | let _ = abc as i8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:49:13 + --> $DIR/fn_to_numeric_cast.rs:35:13 | LL | let _ = abc as i16; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:51:13 + --> $DIR/fn_to_numeric_cast.rs:36:13 | LL | let _ = abc as i32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i64` - --> $DIR/fn_to_numeric_cast.rs:53:13 + --> $DIR/fn_to_numeric_cast.rs:37:13 | LL | let _ = abc as i64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i128` - --> $DIR/fn_to_numeric_cast.rs:55:13 + --> $DIR/fn_to_numeric_cast.rs:38:13 | LL | let _ = abc as i128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `isize` - --> $DIR/fn_to_numeric_cast.rs:57:13 + --> $DIR/fn_to_numeric_cast.rs:39:13 | LL | let _ = abc as isize; | ^^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:60:13 + --> $DIR/fn_to_numeric_cast.rs:41:13 | LL | let _ = abc as u8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:62:13 + --> $DIR/fn_to_numeric_cast.rs:42:13 | LL | let _ = abc as u16; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:64:13 + --> $DIR/fn_to_numeric_cast.rs:43:13 | LL | let _ = abc as u32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u64` - --> $DIR/fn_to_numeric_cast.rs:66:13 + --> $DIR/fn_to_numeric_cast.rs:44:13 | LL | let _ = abc as u64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u128` - --> $DIR/fn_to_numeric_cast.rs:68:13 + --> $DIR/fn_to_numeric_cast.rs:45:13 | LL | let _ = abc as u128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `f` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:76:5 + --> $DIR/fn_to_numeric_cast.rs:52:5 | LL | f as i32 | ^^^^^^^^ help: try: `f as usize` diff --git a/tests/ui/fn_to_numeric_cast.rs b/tests/ui/fn_to_numeric_cast.rs index 09128d8176e08..9501eb5da4b20 100644 --- a/tests/ui/fn_to_numeric_cast.rs +++ b/tests/ui/fn_to_numeric_cast.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@stderr-per-bitwidth //@no-rustfix #![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] @@ -8,30 +8,17 @@ fn foo() -> String { fn test_function_to_numeric_cast() { let _ = foo as i8; - //~^ ERROR: casting function pointer `foo` to `i8`, which truncates the value - //~| NOTE: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` let _ = foo as i16; - //~^ ERROR: casting function pointer `foo` to `i16`, which truncates the value let _ = foo as i32; - //~^ ERROR: casting function pointer `foo` to `i32`, which truncates the value let _ = foo as i64; - //~^ ERROR: casting function pointer `foo` to `i64` - //~| NOTE: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` let _ = foo as i128; - //~^ ERROR: casting function pointer `foo` to `i128` let _ = foo as isize; - //~^ ERROR: casting function pointer `foo` to `isize` let _ = foo as u8; - //~^ ERROR: casting function pointer `foo` to `u8`, which truncates the value let _ = foo as u16; - //~^ ERROR: casting function pointer `foo` to `u16`, which truncates the value let _ = foo as u32; - //~^ ERROR: casting function pointer `foo` to `u32`, which truncates the value let _ = foo as u64; - //~^ ERROR: casting function pointer `foo` to `u64` let _ = foo as u128; - //~^ ERROR: casting function pointer `foo` to `u128` // Casting to usize is OK and should not warn let _ = foo as usize; @@ -45,28 +32,17 @@ fn test_function_var_to_numeric_cast() { let abc: fn() -> String = foo; let _ = abc as i8; - //~^ ERROR: casting function pointer `abc` to `i8`, which truncates the value let _ = abc as i16; - //~^ ERROR: casting function pointer `abc` to `i16`, which truncates the value let _ = abc as i32; - //~^ ERROR: casting function pointer `abc` to `i32`, which truncates the value let _ = abc as i64; - //~^ ERROR: casting function pointer `abc` to `i64` let _ = abc as i128; - //~^ ERROR: casting function pointer `abc` to `i128` let _ = abc as isize; - //~^ ERROR: casting function pointer `abc` to `isize` let _ = abc as u8; - //~^ ERROR: casting function pointer `abc` to `u8`, which truncates the value let _ = abc as u16; - //~^ ERROR: casting function pointer `abc` to `u16`, which truncates the value let _ = abc as u32; - //~^ ERROR: casting function pointer `abc` to `u32`, which truncates the value let _ = abc as u64; - //~^ ERROR: casting function pointer `abc` to `u64` let _ = abc as u128; - //~^ ERROR: casting function pointer `abc` to `u128` // Casting to usize is OK and should not warn let _ = abc as usize; @@ -74,7 +50,6 @@ fn test_function_var_to_numeric_cast() { fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 { f as i32 - //~^ ERROR: casting function pointer `f` to `i32`, which truncates the value } fn main() {} diff --git a/tests/ui/fn_to_numeric_cast_32bit.rs b/tests/ui/fn_to_numeric_cast_32bit.rs deleted file mode 100644 index 93e9361f4dc5b..0000000000000 --- a/tests/ui/fn_to_numeric_cast_32bit.rs +++ /dev/null @@ -1,80 +0,0 @@ -//@ignore-64bit - -#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] - -fn foo() -> String { - String::new() -} - -fn test_function_to_numeric_cast() { - let _ = foo as i8; - //~^ ERROR: casting function pointer `foo` to `i8`, which truncates the value - //~| NOTE: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` - let _ = foo as i16; - //~^ ERROR: casting function pointer `foo` to `i16`, which truncates the value - let _ = foo as i32; - //~^ ERROR: casting function pointer `foo` to `i32`, which truncates the value - let _ = foo as i64; - //~^ ERROR: casting function pointer `foo` to `i64` - //~| NOTE: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` - let _ = foo as i128; - //~^ ERROR: casting function pointer `foo` to `i128` - let _ = foo as isize; - //~^ ERROR: casting function pointer `foo` to `isize` - - let _ = foo as u8; - //~^ ERROR: casting function pointer `foo` to `u8`, which truncates the value - let _ = foo as u16; - //~^ ERROR: casting function pointer `foo` to `u16`, which truncates the value - let _ = foo as u32; - //~^ ERROR: casting function pointer `foo` to `u32`, which truncates the value - let _ = foo as u64; - //~^ ERROR: casting function pointer `foo` to `u64` - let _ = foo as u128; - //~^ ERROR: casting function pointer `foo` to `u128` - - // Casting to usize is OK and should not warn - let _ = foo as usize; - - // Cast `f` (a `FnDef`) to `fn()` should not warn - fn f() {} - let _ = f as fn(); -} - -fn test_function_var_to_numeric_cast() { - let abc: fn() -> String = foo; - - let _ = abc as i8; - //~^ ERROR: casting function pointer `abc` to `i8`, which truncates the value - let _ = abc as i16; - //~^ ERROR: casting function pointer `abc` to `i16`, which truncates the value - let _ = abc as i32; - //~^ ERROR: casting function pointer `abc` to `i32`, which truncates the value - let _ = abc as i64; - //~^ ERROR: casting function pointer `abc` to `i64` - let _ = abc as i128; - //~^ ERROR: casting function pointer `abc` to `i128` - let _ = abc as isize; - //~^ ERROR: casting function pointer `abc` to `isize` - - let _ = abc as u8; - //~^ ERROR: casting function pointer `abc` to `u8`, which truncates the value - let _ = abc as u16; - //~^ ERROR: casting function pointer `abc` to `u16`, which truncates the value - let _ = abc as u32; - //~^ ERROR: casting function pointer `abc` to `u32`, which truncates the value - let _ = abc as u64; - //~^ ERROR: casting function pointer `abc` to `u64` - let _ = abc as u128; - //~^ ERROR: casting function pointer `abc` to `u128` - - // Casting to usize is OK and should not warn - let _ = abc as usize; -} - -fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 { - f as i32 - //~^ ERROR: casting function pointer `f` to `i32`, which truncates the value -} - -fn main() {} diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 8fa0f34a6c423..77abd663e0a6e 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -117,3 +117,15 @@ fn f(b: bool, v: Option<()>) -> Option<()> { None } } + +fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> { + let x = if b { + #[allow(clippy::let_unit_value)] + let _ = v?; + Some(()) + } else { + None + }; + + Ok(()) +} diff --git a/tests/ui/ignored_unit_patterns.fixed b/tests/ui/ignored_unit_patterns.fixed index 2912eede4d9b6..6c6f21fee16b0 100644 --- a/tests/ui/ignored_unit_patterns.fixed +++ b/tests/ui/ignored_unit_patterns.fixed @@ -1,5 +1,5 @@ #![warn(clippy::ignored_unit_patterns)] -#![allow(clippy::redundant_pattern_matching, clippy::single_match)] +#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] fn foo() -> Result<(), ()> { unimplemented!() @@ -7,9 +7,19 @@ fn foo() -> Result<(), ()> { fn main() { match foo() { - Ok(()) => {}, - Err(()) => {}, + Ok(()) => {}, //~ ERROR: matching over `()` is more explicit + Err(()) => {}, //~ ERROR: matching over `()` is more explicit } if let Ok(()) = foo() {} + //~^ ERROR: matching over `()` is more explicit let _ = foo().map_err(|()| todo!()); + //~^ ERROR: matching over `()` is more explicit +} + +#[allow(unused)] +pub fn moo(_: ()) { + let () = foo().unwrap(); + //~^ ERROR: matching over `()` is more explicit + let _: () = foo().unwrap(); + let _: () = (); } diff --git a/tests/ui/ignored_unit_patterns.rs b/tests/ui/ignored_unit_patterns.rs index d180cd8d2fd36..5e8c2e03ba2cb 100644 --- a/tests/ui/ignored_unit_patterns.rs +++ b/tests/ui/ignored_unit_patterns.rs @@ -1,5 +1,5 @@ #![warn(clippy::ignored_unit_patterns)] -#![allow(clippy::redundant_pattern_matching, clippy::single_match)] +#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] fn foo() -> Result<(), ()> { unimplemented!() @@ -7,9 +7,19 @@ fn foo() -> Result<(), ()> { fn main() { match foo() { - Ok(_) => {}, - Err(_) => {}, + Ok(_) => {}, //~ ERROR: matching over `()` is more explicit + Err(_) => {}, //~ ERROR: matching over `()` is more explicit } if let Ok(_) = foo() {} + //~^ ERROR: matching over `()` is more explicit let _ = foo().map_err(|_| todo!()); + //~^ ERROR: matching over `()` is more explicit +} + +#[allow(unused)] +pub fn moo(_: ()) { + let _ = foo().unwrap(); + //~^ ERROR: matching over `()` is more explicit + let _: () = foo().unwrap(); + let _: () = (); } diff --git a/tests/ui/ignored_unit_patterns.stderr b/tests/ui/ignored_unit_patterns.stderr index a8ced22397dbb..df5e1d89e906e 100644 --- a/tests/ui/ignored_unit_patterns.stderr +++ b/tests/ui/ignored_unit_patterns.stderr @@ -20,10 +20,16 @@ LL | if let Ok(_) = foo() {} | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:14:28 + --> $DIR/ignored_unit_patterns.rs:15:28 | LL | let _ = foo().map_err(|_| todo!()); | ^ help: use `()` instead of `_`: `()` -error: aborting due to 4 previous errors +error: matching over `()` is more explicit + --> $DIR/ignored_unit_patterns.rs:21:9 + | +LL | let _ = foo().unwrap(); + | ^ help: use `()` instead of `_`: `()` + +error: aborting due to 5 previous errors diff --git a/tests/ui/implied_bounds_in_impls.fixed b/tests/ui/implied_bounds_in_impls.fixed index 952c2b70619bb..a50fa0ccf6e7f 100644 --- a/tests/ui/implied_bounds_in_impls.fixed +++ b/tests/ui/implied_bounds_in_impls.fixed @@ -65,4 +65,61 @@ impl SomeTrait for SomeStruct { } } +mod issue11422 { + use core::fmt::Debug; + // Some additional tests that would cause ICEs: + + // `PartialOrd` has a default generic parameter and does not need to be explicitly specified. + // This needs special handling. + fn default_generic_param1() -> impl PartialOrd + Debug {} + fn default_generic_param2() -> impl PartialOrd + Debug {} + + // Referring to `Self` in the supertrait clause needs special handling. + trait Trait1 {} + trait Trait2: Trait1 {} + impl Trait1<()> for () {} + impl Trait2 for () {} + + fn f() -> impl Trait1<()> + Trait2 {} +} + +mod issue11435 { + // Associated type needs to be included on DoubleEndedIterator in the suggestion + fn my_iter() -> impl DoubleEndedIterator { + 0..5 + } + + // Removing the `Clone` bound should include the `+` behind it in its remove suggestion + fn f() -> impl Copy { + 1 + } + + trait Trait1 { + type U; + } + impl Trait1 for () { + type U = i64; + } + trait Trait2: Trait1 {} + impl Trait2 for () {} + + // When the other trait has generics, it shouldn't add another pair of `<>` + fn f2() -> impl Trait2 {} + + trait Trait3 { + type X; + type Y; + } + trait Trait4: Trait3 {} + impl Trait3 for () { + type X = i32; + type Y = i128; + } + impl Trait4 for () {} + + // Associated type `X` is specified, but `Y` is not, so only that associated type should be moved + // over + fn f3() -> impl Trait4 {} +} + fn main() {} diff --git a/tests/ui/implied_bounds_in_impls.rs b/tests/ui/implied_bounds_in_impls.rs index 475f2621bbf62..e74ed4425b81e 100644 --- a/tests/ui/implied_bounds_in_impls.rs +++ b/tests/ui/implied_bounds_in_impls.rs @@ -65,4 +65,61 @@ impl SomeTrait for SomeStruct { } } +mod issue11422 { + use core::fmt::Debug; + // Some additional tests that would cause ICEs: + + // `PartialOrd` has a default generic parameter and does not need to be explicitly specified. + // This needs special handling. + fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {} + fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {} + + // Referring to `Self` in the supertrait clause needs special handling. + trait Trait1 {} + trait Trait2: Trait1 {} + impl Trait1<()> for () {} + impl Trait2 for () {} + + fn f() -> impl Trait1<()> + Trait2 {} +} + +mod issue11435 { + // Associated type needs to be included on DoubleEndedIterator in the suggestion + fn my_iter() -> impl Iterator + DoubleEndedIterator { + 0..5 + } + + // Removing the `Clone` bound should include the `+` behind it in its remove suggestion + fn f() -> impl Copy + Clone { + 1 + } + + trait Trait1 { + type U; + } + impl Trait1 for () { + type U = i64; + } + trait Trait2: Trait1 {} + impl Trait2 for () {} + + // When the other trait has generics, it shouldn't add another pair of `<>` + fn f2() -> impl Trait1 + Trait2 {} + + trait Trait3 { + type X; + type Y; + } + trait Trait4: Trait3 {} + impl Trait3 for () { + type X = i32; + type Y = i128; + } + impl Trait4 for () {} + + // Associated type `X` is specified, but `Y` is not, so only that associated type should be moved + // over + fn f3() -> impl Trait3 + Trait4 {} +} + fn main() {} diff --git a/tests/ui/implied_bounds_in_impls.stderr b/tests/ui/implied_bounds_in_impls.stderr index e2b1ecb9f1ec7..72dc2a183a383 100644 --- a/tests/ui/implied_bounds_in_impls.stderr +++ b/tests/ui/implied_bounds_in_impls.stderr @@ -120,5 +120,77 @@ LL - fn f() -> impl Deref + DerefMut { LL + fn f() -> impl DerefMut { | -error: aborting due to 10 previous errors +error: this bound is already specified as the supertrait of `PartialOrd` + --> $DIR/implied_bounds_in_impls.rs:74:41 + | +LL | fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {} + | ^^^^^^^^^ + | +help: try removing this bound + | +LL - fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {} +LL + fn default_generic_param1() -> impl PartialOrd + Debug {} + | + +error: this bound is already specified as the supertrait of `PartialOrd` + --> $DIR/implied_bounds_in_impls.rs:75:54 + | +LL | fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {} + | ^^^^^^^^^ + | +help: try removing this bound + | +LL - fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {} +LL + fn default_generic_param2() -> impl PartialOrd + Debug {} + | + +error: this bound is already specified as the supertrait of `DoubleEndedIterator` + --> $DIR/implied_bounds_in_impls.rs:88:26 + | +LL | fn my_iter() -> impl Iterator + DoubleEndedIterator { + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn my_iter() -> impl Iterator + DoubleEndedIterator { +LL + fn my_iter() -> impl DoubleEndedIterator { + | + +error: this bound is already specified as the supertrait of `Copy` + --> $DIR/implied_bounds_in_impls.rs:93:27 + | +LL | fn f() -> impl Copy + Clone { + | ^^^^^ + | +help: try removing this bound + | +LL - fn f() -> impl Copy + Clone { +LL + fn f() -> impl Copy { + | + +error: this bound is already specified as the supertrait of `Trait2` + --> $DIR/implied_bounds_in_impls.rs:107:21 + | +LL | fn f2() -> impl Trait1 + Trait2 {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn f2() -> impl Trait1 + Trait2 {} +LL + fn f2() -> impl Trait2 {} + | + +error: this bound is already specified as the supertrait of `Trait4` + --> $DIR/implied_bounds_in_impls.rs:122:21 + | +LL | fn f3() -> impl Trait3 + Trait4 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn f3() -> impl Trait3 + Trait4 {} +LL + fn f3() -> impl Trait4 {} + | + +error: aborting due to 16 previous errors diff --git a/tests/ui/iter_out_of_bounds.rs b/tests/ui/iter_out_of_bounds.rs new file mode 100644 index 0000000000000..3cfe6e82fc1e3 --- /dev/null +++ b/tests/ui/iter_out_of_bounds.rs @@ -0,0 +1,71 @@ +//@no-rustfix + +#![deny(clippy::iter_out_of_bounds)] +#![allow(clippy::useless_vec)] + +fn opaque_empty_iter() -> impl Iterator { + std::iter::empty() +} + +fn main() { + #[allow(clippy::never_loop)] + for _ in [1, 2, 3].iter().skip(4) { + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + unreachable!(); + } + for (i, _) in [1, 2, 3].iter().take(4).enumerate() { + //~^ ERROR: this `.take()` call takes more items than the iterator will produce + assert!(i <= 2); + } + + #[allow(clippy::needless_borrow)] + for _ in (&&&&&&[1, 2, 3]).iter().take(4) {} + //~^ ERROR: this `.take()` call takes more items than the iterator will produce + + for _ in [1, 2, 3].iter().skip(4) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + for _ in [1; 3].iter().skip(4) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + // Should not lint + for _ in opaque_empty_iter().skip(1) {} + + for _ in vec![1, 2, 3].iter().skip(4) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + for _ in vec![1; 3].iter().skip(4) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + let x = [1, 2, 3]; + for _ in x.iter().skip(4) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + let n = 4; + for _ in x.iter().skip(n) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + let empty = std::iter::empty::; + + for _ in empty().skip(1) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + for _ in empty().take(1) {} + //~^ ERROR: this `.take()` call takes more items than the iterator will produce + + for _ in std::iter::once(1).skip(2) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + for _ in std::iter::once(1).take(2) {} + //~^ ERROR: this `.take()` call takes more items than the iterator will produce + + for x in [].iter().take(1) { + //~^ ERROR: this `.take()` call takes more items than the iterator will produce + let _: &i32 = x; + } + + // ok, not out of bounds + for _ in [1].iter().take(1) {} + for _ in [1, 2, 3].iter().take(2) {} + for _ in [1, 2, 3].iter().skip(2) {} +} diff --git a/tests/ui/iter_out_of_bounds.stderr b/tests/ui/iter_out_of_bounds.stderr new file mode 100644 index 0000000000000..f235faec8e59c --- /dev/null +++ b/tests/ui/iter_out_of_bounds.stderr @@ -0,0 +1,119 @@ +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:12:14 + | +LL | for _ in [1, 2, 3].iter().skip(4) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator +note: the lint level is defined here + --> $DIR/iter_out_of_bounds.rs:3:9 + | +LL | #![deny(clippy::iter_out_of_bounds)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this `.take()` call takes more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:16:19 + | +LL | for (i, _) in [1, 2, 3].iter().take(4).enumerate() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and the returned iterator will simply yield the same items + +error: this `.take()` call takes more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:22:14 + | +LL | for _ in (&&&&&&[1, 2, 3]).iter().take(4) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and the returned iterator will simply yield the same items + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:25:14 + | +LL | for _ in [1, 2, 3].iter().skip(4) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:28:14 + | +LL | for _ in [1; 3].iter().skip(4) {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:34:14 + | +LL | for _ in vec![1, 2, 3].iter().skip(4) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:37:14 + | +LL | for _ in vec![1; 3].iter().skip(4) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:41:14 + | +LL | for _ in x.iter().skip(4) {} + | ^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:45:14 + | +LL | for _ in x.iter().skip(n) {} + | ^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:50:14 + | +LL | for _ in empty().skip(1) {} + | ^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.take()` call takes more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:53:14 + | +LL | for _ in empty().take(1) {} + | ^^^^^^^^^^^^^^^ + | + = note: this operation is useless and the returned iterator will simply yield the same items + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:56:14 + | +LL | for _ in std::iter::once(1).skip(2) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.take()` call takes more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:59:14 + | +LL | for _ in std::iter::once(1).take(2) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and the returned iterator will simply yield the same items + +error: this `.take()` call takes more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:62:14 + | +LL | for x in [].iter().take(1) { + | ^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and the returned iterator will simply yield the same items + +error: aborting due to 14 previous errors + diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index 9dd046fec1fee..7d8a584b0224c 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -63,11 +63,9 @@ fn main() { let _ = vec.iter().for_each(|x| assert!(!x.is_empty())); - // Not implemented yet - let _ = vec.iter().cloned().all(|x| x.len() == 1); + let _ = vec.iter().all(|x| x.len() == 1); - // Not implemented yet - let _ = vec.iter().cloned().any(|x| x.len() == 1); + let _ = vec.iter().any(|x| x.len() == 1); // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index 3cab366955481..58c374ab8cd10 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -64,10 +64,8 @@ fn main() { let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); - // Not implemented yet let _ = vec.iter().cloned().all(|x| x.len() == 1); - // Not implemented yet let _ = vec.iter().cloned().any(|x| x.len() == 1); // Should probably stay as it is. diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index fbcd33066d876..a9a739688ebf2 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -148,5 +148,21 @@ LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | | | help: try: `.for_each(|x| assert!(!x.is_empty()))` -error: aborting due to 17 previous errors +error: unneeded cloning of iterator items + --> $DIR/iter_overeager_cloned.rs:67:13 + | +LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); + | ^^^^^^^^^^------------------------------- + | | + | help: try: `.all(|x| x.len() == 1)` + +error: unneeded cloning of iterator items + --> $DIR/iter_overeager_cloned.rs:69:13 + | +LL | let _ = vec.iter().cloned().any(|x| x.len() == 1); + | ^^^^^^^^^^------------------------------- + | | + | help: try: `.any(|x| x.len() == 1)` + +error: aborting due to 19 previous errors diff --git a/tests/ui/iter_skip_next.fixed b/tests/ui/iter_skip_next.fixed index 310b24a9cde17..3e41b36324902 100644 --- a/tests/ui/iter_skip_next.fixed +++ b/tests/ui/iter_skip_next.fixed @@ -4,6 +4,7 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::iter_nth)] #![allow(clippy::useless_vec)] +#![allow(clippy::iter_out_of_bounds)] #![allow(unused_mut, dead_code)] extern crate option_helpers; diff --git a/tests/ui/iter_skip_next.rs b/tests/ui/iter_skip_next.rs index 222d6a2a1848b..6d96441ca96f5 100644 --- a/tests/ui/iter_skip_next.rs +++ b/tests/ui/iter_skip_next.rs @@ -4,6 +4,7 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::iter_nth)] #![allow(clippy::useless_vec)] +#![allow(clippy::iter_out_of_bounds)] #![allow(unused_mut, dead_code)] extern crate option_helpers; diff --git a/tests/ui/iter_skip_next.stderr b/tests/ui/iter_skip_next.stderr index 0b6bf652b1f1f..39b173e758693 100644 --- a/tests/ui/iter_skip_next.stderr +++ b/tests/ui/iter_skip_next.stderr @@ -1,5 +1,5 @@ error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:16:28 + --> $DIR/iter_skip_next.rs:17:28 | LL | let _ = some_vec.iter().skip(42).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` @@ -8,37 +8,37 @@ LL | let _ = some_vec.iter().skip(42).next(); = help: to override `-D warnings` add `#[allow(clippy::iter_skip_next)]` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:17:36 + --> $DIR/iter_skip_next.rs:18:36 | LL | let _ = some_vec.iter().cycle().skip(42).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:18:20 + --> $DIR/iter_skip_next.rs:19:20 | LL | let _ = (1..10).skip(10).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:19:33 + --> $DIR/iter_skip_next.rs:20:33 | LL | let _ = &some_vec[..].iter().skip(3).next(); | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:27:26 + --> $DIR/iter_skip_next.rs:28:26 | LL | let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect(); | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:29:29 + --> $DIR/iter_skip_next.rs:30:29 | LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:35:29 + --> $DIR/iter_skip_next.rs:36:29 | LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` diff --git a/tests/ui/iter_skip_next_unfixable.rs b/tests/ui/iter_skip_next_unfixable.rs index 9c224f4117d3f..6c98bdc8c8808 100644 --- a/tests/ui/iter_skip_next_unfixable.rs +++ b/tests/ui/iter_skip_next_unfixable.rs @@ -1,5 +1,5 @@ #![warn(clippy::iter_skip_next)] -#![allow(dead_code)] +#![allow(dead_code, clippy::iter_out_of_bounds)] //@no-rustfix /// Checks implementation of `ITER_SKIP_NEXT` lint fn main() { diff --git a/tests/ui/iter_skip_zero.fixed b/tests/ui/iter_skip_zero.fixed index 62a83d5905b41..447d07100e953 100644 --- a/tests/ui/iter_skip_zero.fixed +++ b/tests/ui/iter_skip_zero.fixed @@ -1,5 +1,5 @@ //@aux-build:proc_macros.rs -#![allow(clippy::useless_vec, unused)] +#![allow(clippy::useless_vec, clippy::iter_out_of_bounds, unused)] #![warn(clippy::iter_skip_zero)] #[macro_use] diff --git a/tests/ui/iter_skip_zero.rs b/tests/ui/iter_skip_zero.rs index c96696dde65d4..ba63c3981808e 100644 --- a/tests/ui/iter_skip_zero.rs +++ b/tests/ui/iter_skip_zero.rs @@ -1,5 +1,5 @@ //@aux-build:proc_macros.rs -#![allow(clippy::useless_vec, unused)] +#![allow(clippy::useless_vec, clippy::iter_out_of_bounds, unused)] #![warn(clippy::iter_skip_zero)] #[macro_use] diff --git a/tests/ui/large_enum_variant.32bit.stderr b/tests/ui/large_enum_variant.32bit.stderr new file mode 100644 index 0000000000000..0e0eee21cf30a --- /dev/null +++ b/tests/ui/large_enum_variant.32bit.stderr @@ -0,0 +1,280 @@ +error: large size difference between variants + --> $DIR/large_enum_variant.rs:11:1 + | +LL | / enum LargeEnum { +LL | | A(i32), + | | ------ the second-largest variant contains at least 4 bytes +LL | | B([i32; 8000]), + | | -------------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | + = note: `-D clippy::large-enum-variant` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]` +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[i32; 8000]>), + | ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:35:1 + | +LL | / enum LargeEnum2 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | ContainingLargeEnum(LargeEnum), + | | ------------------------------ the largest variant contains at least 32004 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingLargeEnum(Box), + | ~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:40:1 + | +LL | / enum LargeEnum3 { +LL | | ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), + | | --------------------------------------------------------- the largest variant contains at least 70004 bytes +LL | | VoidVariant, +LL | | StructLikeLittle { x: i32, y: i32 }, + | | ----------------------------------- the second-largest variant contains at least 8 bytes +LL | | } + | |_^ the entire enum is at least 70008 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>), + | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:46:1 + | +LL | / enum LargeEnum4 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | StructLikeLarge { x: [i32; 8000], y: i32 }, + | | ------------------------------------------ the largest variant contains at least 32004 bytes +LL | | } + | |_^ the entire enum is at least 32008 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | StructLikeLarge { x: Box<[i32; 8000]>, y: i32 }, + | ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:51:1 + | +LL | / enum LargeEnum5 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | StructLikeLarge2 { x: [i32; 8000] }, + | | ----------------------------------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | StructLikeLarge2 { x: Box<[i32; 8000]> }, + | ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:67:1 + | +LL | / enum LargeEnum7 { +LL | | A, +LL | | B([u8; 1255]), + | | ------------- the largest variant contains at least 1255 bytes +LL | | C([u8; 200]), + | | ------------ the second-largest variant contains at least 200 bytes +LL | | } + | |_^ the entire enum is at least 1256 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[u8; 1255]>), + | ~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:73:1 + | +LL | / enum LargeEnum8 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]), + | | ------------------------------------------------------------------------- the largest variant contains at least 70128 bytes +LL | | } + | |_^ the entire enum is at least 70132 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]), + | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:78:1 + | +LL | / enum LargeEnum9 { +LL | | A(Struct<()>), + | | ------------- the second-largest variant contains at least 4 bytes +LL | | B(Struct2), + | | ---------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box), + | ~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:83:1 + | +LL | / enum LargeEnumOk2 { +LL | | A(T), + | | ---- the second-largest variant contains at least 0 bytes +LL | | B(Struct2), + | | ---------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32000 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box), + | ~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:88:1 + | +LL | / enum LargeEnumOk3 { +LL | | A(Struct), + | | ------------ the second-largest variant contains at least 4 bytes +LL | | B(Struct2), + | | ---------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32000 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box), + | ~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:103:1 + | +LL | / enum CopyableLargeEnum { +LL | | A(bool), + | | ------- the second-largest variant contains at least 1 bytes +LL | | B([u64; 8000]), + | | -------------- the largest variant contains at least 64000 bytes +LL | | } + | |_^ the entire enum is at least 64004 bytes + | +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:103:6 + | +LL | enum CopyableLargeEnum { + | ^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:105:5 + | +LL | B([u64; 8000]), + | ^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:108:1 + | +LL | / enum ManuallyCopyLargeEnum { +LL | | A(bool), + | | ------- the second-largest variant contains at least 1 bytes +LL | | B([u64; 8000]), + | | -------------- the largest variant contains at least 64000 bytes +LL | | } + | |_^ the entire enum is at least 64004 bytes + | +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:108:6 + | +LL | enum ManuallyCopyLargeEnum { + | ^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:110:5 + | +LL | B([u64; 8000]), + | ^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:121:1 + | +LL | / enum SomeGenericPossiblyCopyEnum { +LL | | A(bool, std::marker::PhantomData), + | | ------------------------------------ the second-largest variant contains at least 1 bytes +LL | | B([u64; 4000]), + | | -------------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:121:6 + | +LL | enum SomeGenericPossiblyCopyEnum { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:123:5 + | +LL | B([u64; 4000]), + | ^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:134:1 + | +LL | / enum LargeEnumWithGenerics { +LL | | Small, + | | ----- the second-largest variant carries no data at all +LL | | Large((T, [u8; 512])), + | | --------------------- the largest variant contains at least 512 bytes +LL | | } + | |_^ the entire enum is at least 512 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | Large(Box<(T, [u8; 512])>), + | ~~~~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:143:1 + | +LL | / enum WithGenerics { +LL | | Large([Foo; 64]), + | | --------------------- the largest variant contains at least 512 bytes +LL | | Small(u8), + | | --------- the second-largest variant contains at least 1 bytes +LL | | } + | |_^ the entire enum is at least 516 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | Large(Box<[Foo; 64]>), + | ~~~~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:153:1 + | +LL | / enum LargeEnumOfConst { +LL | | Ok, + | | -- the second-largest variant carries no data at all +LL | | Error(PossiblyLargeEnumWithConst<256>), + | | -------------------------------------- the largest variant contains at least 514 bytes +LL | | } + | |_^ the entire enum is at least 514 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | Error(Box>), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 16 previous errors + diff --git a/tests/ui/large_enum_variant.stderr b/tests/ui/large_enum_variant.64bit.stderr similarity index 91% rename from tests/ui/large_enum_variant.stderr rename to tests/ui/large_enum_variant.64bit.stderr index 7026ca785f34c..3eba43e05ece5 100644 --- a/tests/ui/large_enum_variant.stderr +++ b/tests/ui/large_enum_variant.64bit.stderr @@ -1,5 +1,5 @@ error: large size difference between variants - --> $DIR/large_enum_variant.rs:10:1 + --> $DIR/large_enum_variant.rs:11:1 | LL | / enum LargeEnum { LL | | A(i32), @@ -17,7 +17,7 @@ LL | B(Box<[i32; 8000]>), | ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:34:1 + --> $DIR/large_enum_variant.rs:35:1 | LL | / enum LargeEnum2 { LL | | VariantOk(i32, u32), @@ -33,7 +33,7 @@ LL | ContainingLargeEnum(Box), | ~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:39:1 + --> $DIR/large_enum_variant.rs:40:1 | LL | / enum LargeEnum3 { LL | | ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), @@ -50,7 +50,7 @@ LL | ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>), | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:45:1 + --> $DIR/large_enum_variant.rs:46:1 | LL | / enum LargeEnum4 { LL | | VariantOk(i32, u32), @@ -66,7 +66,7 @@ LL | StructLikeLarge { x: Box<[i32; 8000]>, y: i32 }, | ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:50:1 + --> $DIR/large_enum_variant.rs:51:1 | LL | / enum LargeEnum5 { LL | | VariantOk(i32, u32), @@ -82,7 +82,7 @@ LL | StructLikeLarge2 { x: Box<[i32; 8000]> }, | ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:66:1 + --> $DIR/large_enum_variant.rs:67:1 | LL | / enum LargeEnum7 { LL | | A, @@ -99,7 +99,7 @@ LL | B(Box<[u8; 1255]>), | ~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:72:1 + --> $DIR/large_enum_variant.rs:73:1 | LL | / enum LargeEnum8 { LL | | VariantOk(i32, u32), @@ -115,7 +115,7 @@ LL | ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]> | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:77:1 + --> $DIR/large_enum_variant.rs:78:1 | LL | / enum LargeEnum9 { LL | | A(Struct<()>), @@ -131,7 +131,7 @@ LL | B(Box), | ~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:82:1 + --> $DIR/large_enum_variant.rs:83:1 | LL | / enum LargeEnumOk2 { LL | | A(T), @@ -147,7 +147,7 @@ LL | B(Box), | ~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:87:1 + --> $DIR/large_enum_variant.rs:88:1 | LL | / enum LargeEnumOk3 { LL | | A(Struct), @@ -163,7 +163,7 @@ LL | B(Box), | ~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:102:1 + --> $DIR/large_enum_variant.rs:103:1 | LL | / enum CopyableLargeEnum { LL | | A(bool), @@ -174,18 +174,18 @@ LL | | } | |_^ the entire enum is at least 64008 bytes | note: boxing a variant would require the type no longer be `Copy` - --> $DIR/large_enum_variant.rs:102:6 + --> $DIR/large_enum_variant.rs:103:6 | LL | enum CopyableLargeEnum { | ^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:104:5 + --> $DIR/large_enum_variant.rs:105:5 | LL | B([u64; 8000]), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:107:1 + --> $DIR/large_enum_variant.rs:108:1 | LL | / enum ManuallyCopyLargeEnum { LL | | A(bool), @@ -196,18 +196,18 @@ LL | | } | |_^ the entire enum is at least 64008 bytes | note: boxing a variant would require the type no longer be `Copy` - --> $DIR/large_enum_variant.rs:107:6 + --> $DIR/large_enum_variant.rs:108:6 | LL | enum ManuallyCopyLargeEnum { | ^^^^^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:109:5 + --> $DIR/large_enum_variant.rs:110:5 | LL | B([u64; 8000]), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:120:1 + --> $DIR/large_enum_variant.rs:121:1 | LL | / enum SomeGenericPossiblyCopyEnum { LL | | A(bool, std::marker::PhantomData), @@ -218,18 +218,18 @@ LL | | } | |_^ the entire enum is at least 32008 bytes | note: boxing a variant would require the type no longer be `Copy` - --> $DIR/large_enum_variant.rs:120:6 + --> $DIR/large_enum_variant.rs:121:6 | LL | enum SomeGenericPossiblyCopyEnum { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:122:5 + --> $DIR/large_enum_variant.rs:123:5 | LL | B([u64; 4000]), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:133:1 + --> $DIR/large_enum_variant.rs:134:1 | LL | / enum LargeEnumWithGenerics { LL | | Small, @@ -245,7 +245,7 @@ LL | Large(Box<(T, [u8; 512])>), | ~~~~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:142:1 + --> $DIR/large_enum_variant.rs:143:1 | LL | / enum WithGenerics { LL | | Large([Foo; 64]), @@ -261,7 +261,7 @@ LL | Large(Box<[Foo; 64]>), | ~~~~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:152:1 + --> $DIR/large_enum_variant.rs:153:1 | LL | / enum LargeEnumOfConst { LL | | Ok, diff --git a/tests/ui/large_enum_variant.rs b/tests/ui/large_enum_variant.rs index f101bda76a895..3625c011dbfa1 100644 --- a/tests/ui/large_enum_variant.rs +++ b/tests/ui/large_enum_variant.rs @@ -1,3 +1,4 @@ +//@stderr-per-bitwidth //@aux-build:proc_macros.rs //@no-rustfix #![allow(dead_code)] diff --git a/tests/ui/manual_range_patterns.fixed b/tests/ui/manual_range_patterns.fixed index ea06e27d1534e..b348d7071f6e9 100644 --- a/tests/ui/manual_range_patterns.fixed +++ b/tests/ui/manual_range_patterns.fixed @@ -13,8 +13,8 @@ fn main() { let _ = matches!(f, 1 | 2147483647); let _ = matches!(f, 0 | 2147483647); let _ = matches!(f, -2147483647 | 2147483647); - let _ = matches!(f, 1 | (2..=4)); - let _ = matches!(f, 1 | (2..4)); + let _ = matches!(f, 1..=4); + let _ = matches!(f, 1..4); let _ = matches!(f, 1..=48324729); let _ = matches!(f, 0..=48324730); let _ = matches!(f, 0..=3); @@ -25,9 +25,20 @@ fn main() { }; let _ = matches!(f, -5..=3); let _ = matches!(f, -1 | -5 | 3 | -2 | -4 | -3 | 0 | 1); // 2 is missing - let _ = matches!(f, -1000001..=1000001); + let _ = matches!(f, -1_000_001..=1_000_001); let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_002); + matches!(f, 0x00..=0x03); + matches!(f, 0x00..=0x07); + matches!(f, -0x09..=0x00); + + matches!(f, 0..=5); + matches!(f, 0..5); + + matches!(f, 0..10); + matches!(f, 0..=10); + matches!(f, 0..=10); + macro_rules! mac { ($e:expr) => { matches!($e, 1..=10) diff --git a/tests/ui/manual_range_patterns.rs b/tests/ui/manual_range_patterns.rs index eb29987b89fd1..a0750f54b73f1 100644 --- a/tests/ui/manual_range_patterns.rs +++ b/tests/ui/manual_range_patterns.rs @@ -28,6 +28,17 @@ fn main() { let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001); let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_002); + matches!(f, 0x00 | 0x01 | 0x02 | 0x03); + matches!(f, 0x00..=0x05 | 0x06 | 0x07); + matches!(f, -0x09 | -0x08 | -0x07..=0x00); + + matches!(f, 0..5 | 5); + matches!(f, 0 | 1..5); + + matches!(f, 0..=5 | 6..10); + matches!(f, 0..5 | 5..=10); + matches!(f, 5..=10 | 0..5); + macro_rules! mac { ($e:expr) => { matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10) diff --git a/tests/ui/manual_range_patterns.stderr b/tests/ui/manual_range_patterns.stderr index 3eee39af86e7b..fbeb9455769df 100644 --- a/tests/ui/manual_range_patterns.stderr +++ b/tests/ui/manual_range_patterns.stderr @@ -13,6 +13,18 @@ error: this OR pattern can be rewritten using a range LL | let _ = matches!(f, 4 | 2 | 3 | 1 | 5 | 6 | 9 | 7 | 8 | 10); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:16:25 + | +LL | let _ = matches!(f, 1 | (2..=4)); + | ^^^^^^^^^^^ help: try: `1..=4` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:17:25 + | +LL | let _ = matches!(f, 1 | (2..4)); + | ^^^^^^^^^^ help: try: `1..4` + error: this OR pattern can be rewritten using a range --> $DIR/manual_range_patterns.rs:18:25 | @@ -47,10 +59,58 @@ error: this OR pattern can be rewritten using a range --> $DIR/manual_range_patterns.rs:28:25 | LL | let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1000001..=1000001` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1_000_001..=1_000_001` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:31:17 + | +LL | matches!(f, 0x00 | 0x01 | 0x02 | 0x03); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x03` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:32:17 + | +LL | matches!(f, 0x00..=0x05 | 0x06 | 0x07); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x07` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:33:17 + | +LL | matches!(f, -0x09 | -0x08 | -0x07..=0x00); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-0x09..=0x00` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:35:17 + | +LL | matches!(f, 0..5 | 5); + | ^^^^^^^^ help: try: `0..=5` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:36:17 + | +LL | matches!(f, 0 | 1..5); + | ^^^^^^^^ help: try: `0..5` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:38:17 + | +LL | matches!(f, 0..=5 | 6..10); + | ^^^^^^^^^^^^^ help: try: `0..10` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:39:17 + | +LL | matches!(f, 0..5 | 5..=10); + | ^^^^^^^^^^^^^ help: try: `0..=10` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:40:17 + | +LL | matches!(f, 5..=10 | 0..5); + | ^^^^^^^^^^^^^ help: try: `0..=10` error: this OR pattern can be rewritten using a range - --> $DIR/manual_range_patterns.rs:33:26 + --> $DIR/manual_range_patterns.rs:44:26 | LL | matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` @@ -60,5 +120,5 @@ LL | mac!(f); | = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 9 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/missing_asserts_for_indexing.fixed b/tests/ui/missing_asserts_for_indexing.fixed new file mode 100644 index 0000000000000..a96827259f530 --- /dev/null +++ b/tests/ui/missing_asserts_for_indexing.fixed @@ -0,0 +1,121 @@ +#![allow(unused)] +#![warn(clippy::missing_asserts_for_indexing)] + +// ok +fn sum_with_assert(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_other_way(v: &[u8]) -> u8 { + assert!(5 <= v.len()); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_ge(v: &[u8]) -> u8 { + assert!(v.len() >= 5); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_ge_other_way(v: &[u8]) -> u8 { + assert!(4 < v.len()); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +fn sum_with_assert_lt(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_assert_le(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_incorrect_assert_len(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_incorrect_assert_len2(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +// ok, don't lint for single array access +fn single_access(v: &[u8]) -> u8 { + v[0] +} + +// ok +fn subslice_ok(v: &[u8]) { + assert!(v.len() > 3); + let _ = v[0]; + let _ = v[1..4]; +} + +fn subslice_bad(v: &[u8]) { + assert!(v.len() > 3); + let _ = v[0]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v[1..4]; +} + +// ok +fn subslice_inclusive_ok(v: &[u8]) { + assert!(v.len() > 4); + let _ = v[0]; + let _ = v[1..=4]; +} + +fn subslice_inclusive_bad(v: &[u8]) { + assert!(v.len() > 4); + let _ = v[0]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v[1..=4]; +} + +fn index_different_slices_ok(v1: &[u8], v2: &[u8]) { + assert!(v1.len() > 12); + assert!(v2.len() > 15); + let _ = v1[0] + v1[12]; + let _ = v2[5] + v2[15]; +} + +fn index_different_slices_wrong_len(v1: &[u8], v2: &[u8]) { + assert!(v1.len() > 12); + assert!(v2.len() > 15); + let _ = v1[0] + v1[12]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v2[5] + v2[15]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} +fn index_different_slices_one_wrong_len(v1: &[u8], v2: &[u8]) { + assert!(v1.len() > 12); + assert!(v2.len() > 15); + let _ = v1[0] + v1[12]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v2[5] + v2[15]; +} + +fn side_effect() -> &'static [u8] { + &[] +} + +fn index_side_effect_expr() { + let _ = side_effect()[0] + side_effect()[1]; +} + +// ok, single access for different slices +fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) { + let _ = v1[0] + v2[1]; +} + +fn main() {} diff --git a/tests/ui/missing_asserts_for_indexing.rs b/tests/ui/missing_asserts_for_indexing.rs new file mode 100644 index 0000000000000..0b4b883acf8ac --- /dev/null +++ b/tests/ui/missing_asserts_for_indexing.rs @@ -0,0 +1,121 @@ +#![allow(unused)] +#![warn(clippy::missing_asserts_for_indexing)] + +// ok +fn sum_with_assert(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_other_way(v: &[u8]) -> u8 { + assert!(5 <= v.len()); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_ge(v: &[u8]) -> u8 { + assert!(v.len() >= 5); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_ge_other_way(v: &[u8]) -> u8 { + assert!(4 < v.len()); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +fn sum_with_assert_lt(v: &[u8]) -> u8 { + assert!(v.len() < 5); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_assert_le(v: &[u8]) -> u8 { + assert!(v.len() <= 5); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_incorrect_assert_len(v: &[u8]) -> u8 { + assert!(v.len() > 3); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_incorrect_assert_len2(v: &[u8]) -> u8 { + assert!(v.len() >= 4); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +// ok, don't lint for single array access +fn single_access(v: &[u8]) -> u8 { + v[0] +} + +// ok +fn subslice_ok(v: &[u8]) { + assert!(v.len() > 3); + let _ = v[0]; + let _ = v[1..4]; +} + +fn subslice_bad(v: &[u8]) { + assert!(v.len() >= 3); + let _ = v[0]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v[1..4]; +} + +// ok +fn subslice_inclusive_ok(v: &[u8]) { + assert!(v.len() > 4); + let _ = v[0]; + let _ = v[1..=4]; +} + +fn subslice_inclusive_bad(v: &[u8]) { + assert!(v.len() >= 4); + let _ = v[0]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v[1..=4]; +} + +fn index_different_slices_ok(v1: &[u8], v2: &[u8]) { + assert!(v1.len() > 12); + assert!(v2.len() > 15); + let _ = v1[0] + v1[12]; + let _ = v2[5] + v2[15]; +} + +fn index_different_slices_wrong_len(v1: &[u8], v2: &[u8]) { + assert!(v1.len() >= 12); + assert!(v2.len() >= 15); + let _ = v1[0] + v1[12]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v2[5] + v2[15]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} +fn index_different_slices_one_wrong_len(v1: &[u8], v2: &[u8]) { + assert!(v1.len() >= 12); + assert!(v2.len() > 15); + let _ = v1[0] + v1[12]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v2[5] + v2[15]; +} + +fn side_effect() -> &'static [u8] { + &[] +} + +fn index_side_effect_expr() { + let _ = side_effect()[0] + side_effect()[1]; +} + +// ok, single access for different slices +fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) { + let _ = v1[0] + v2[1]; +} + +fn main() {} diff --git a/tests/ui/missing_asserts_for_indexing.stderr b/tests/ui/missing_asserts_for_indexing.stderr new file mode 100644 index 0000000000000..a3e66d7958e98 --- /dev/null +++ b/tests/ui/missing_asserts_for_indexing.stderr @@ -0,0 +1,253 @@ +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:30:5 + | +LL | assert!(v.len() < 5); + | -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:30:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:30:12 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:30:19 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:30:26 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:30:33 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + = note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:36:5 + | +LL | assert!(v.len() <= 5); + | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:36:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:36:12 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:36:19 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:36:26 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:36:33 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:42:5 + | +LL | assert!(v.len() > 3); + | -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:42:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:42:12 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:42:19 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:42:26 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:42:33 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:48:5 + | +LL | assert!(v.len() >= 4); + | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:48:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:48:12 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:48:19 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:48:26 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:48:33 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:66:13 + | +LL | assert!(v.len() >= 3); + | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 3)` +LL | let _ = v[0]; + | _____________^ +LL | | +LL | | let _ = v[1..4]; + | |___________________^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:66:13 + | +LL | let _ = v[0]; + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:68:13 + | +LL | let _ = v[1..4]; + | ^^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:80:13 + | +LL | assert!(v.len() >= 4); + | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` +LL | let _ = v[0]; + | _____________^ +LL | | +LL | | let _ = v[1..=4]; + | |____________________^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:80:13 + | +LL | let _ = v[0]; + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:82:13 + | +LL | let _ = v[1..=4]; + | ^^^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:95:13 + | +LL | assert!(v1.len() >= 12); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)` +LL | assert!(v2.len() >= 15); +LL | let _ = v1[0] + v1[12]; + | ^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:95:13 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:95:21 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:97:13 + | +LL | assert!(v2.len() >= 15); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v2.len() > 15)` +... +LL | let _ = v2[5] + v2[15]; + | ^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:97:13 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:97:21 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:103:13 + | +LL | assert!(v1.len() >= 12); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)` +LL | assert!(v2.len() > 15); +LL | let _ = v1[0] + v1[12]; + | ^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:103:13 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:103:21 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 9 previous errors + diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.rs b/tests/ui/missing_asserts_for_indexing_unfixable.rs new file mode 100644 index 0000000000000..4346ed892f277 --- /dev/null +++ b/tests/ui/missing_asserts_for_indexing_unfixable.rs @@ -0,0 +1,49 @@ +#![allow(unused)] +#![warn(clippy::missing_asserts_for_indexing)] + +fn sum(v: &[u8]) -> u8 { + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times without an `assert` +} + +fn subslice(v: &[u8]) { + let _ = v[0]; + //~^ ERROR: indexing into a slice multiple times without an `assert` + let _ = v[1..4]; +} + +fn variables(v: &[u8]) -> u8 { + let a = v[0]; + //~^ ERROR: indexing into a slice multiple times without an `assert` + let b = v[1]; + let c = v[2]; + a + b + c +} + +fn index_different_slices(v1: &[u8], v2: &[u8]) { + let _ = v1[0] + v1[12]; + let _ = v2[5] + v2[15]; +} + +fn index_different_slices2(v1: &[u8], v2: &[u8]) { + assert!(v1.len() > 12); + let _ = v1[0] + v1[12]; + let _ = v2[5] + v2[15]; +} + +struct Foo<'a> { + v: &'a [u8], + v2: &'a [u8], +} + +fn index_struct_field(f: &Foo<'_>) { + let _ = f.v[0] + f.v[1]; + //~^ ERROR: indexing into a slice multiple times without an `assert` +} + +fn index_struct_different_fields(f: &Foo<'_>) { + // ok, different fields + let _ = f.v[0] + f.v2[1]; +} + +fn main() {} diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/tests/ui/missing_asserts_for_indexing_unfixable.stderr new file mode 100644 index 0000000000000..12c9eed5d6619 --- /dev/null +++ b/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -0,0 +1,164 @@ +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v.len() > 4);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:12 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:19 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:26 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:33 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + = note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:10:13 + | +LL | let _ = v[0]; + | _____________^ +LL | | +LL | | let _ = v[1..4]; + | |___________________^ + | + = help: consider asserting the length before indexing: `assert!(v.len() > 3);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:10:13 + | +LL | let _ = v[0]; + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:12:13 + | +LL | let _ = v[1..4]; + | ^^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:16:13 + | +LL | let a = v[0]; + | _____________^ +LL | | +LL | | let b = v[1]; +LL | | let c = v[2]; + | |________________^ + | + = help: consider asserting the length before indexing: `assert!(v.len() > 2);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:16:13 + | +LL | let a = v[0]; + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:18:13 + | +LL | let b = v[1]; + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:19:13 + | +LL | let c = v[2]; + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:13 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v1.len() > 12);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:13 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:21 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:13 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:13 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:21 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:13 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:13 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:21 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:13 + | +LL | let _ = f.v[0] + f.v[1]; + | ^^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(f.v.len() > 1);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:13 + | +LL | let _ = f.v[0] + f.v[1]; + | ^^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:22 + | +LL | let _ = f.v[0] + f.v[1]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 7 previous errors + diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index ee67224b4a800..0a52b25229d76 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -503,3 +503,16 @@ mod issue_10535 { { } } + +mod issue_10253 { + struct S; + trait X { + fn f(&self); + } + impl X for &S { + fn f(&self) {} + } + fn f() { + (&S).f::<()>(); + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 1444f47d92099..34a95d184635d 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -503,3 +503,16 @@ mod issue_10535 { { } } + +mod issue_10253 { + struct S; + trait X { + fn f(&self); + } + impl X for &S { + fn f(&self) {} + } + fn f() { + (&S).f::<()>(); + } +} diff --git a/tests/ui/needless_raw_string.fixed b/tests/ui/needless_raw_string.fixed index 4db375178b4f2..855498105135e 100644 --- a/tests/ui/needless_raw_string.fixed +++ b/tests/ui/needless_raw_string.fixed @@ -9,8 +9,13 @@ fn main() { b"aaa"; br#""aaa""#; br#"\s"#; - // currently disabled: https://github.com/rust-lang/rust/issues/113333 - // cr#"aaa"#; - // cr#""aaa""#; - // cr#"\s"#; + c"aaa"; + cr#""aaa""#; + cr#"\s"#; + + " + a + multiline + string + "; } diff --git a/tests/ui/needless_raw_string.rs b/tests/ui/needless_raw_string.rs index 59c75fda41bef..06d4973038717 100644 --- a/tests/ui/needless_raw_string.rs +++ b/tests/ui/needless_raw_string.rs @@ -9,8 +9,13 @@ fn main() { br#"aaa"#; br#""aaa""#; br#"\s"#; - // currently disabled: https://github.com/rust-lang/rust/issues/113333 - // cr#"aaa"#; - // cr#""aaa""#; - // cr#"\s"#; + cr#"aaa"#; + cr#""aaa""#; + cr#"\s"#; + + r#" + a + multiline + string + "#; } diff --git a/tests/ui/needless_raw_string.stderr b/tests/ui/needless_raw_string.stderr index 83cc6d332ee56..e6806b31b1d96 100644 --- a/tests/ui/needless_raw_string.stderr +++ b/tests/ui/needless_raw_string.stderr @@ -2,16 +2,58 @@ error: unnecessary raw string literal --> $DIR/needless_raw_string.rs:6:5 | LL | r#"aaa"#; - | ^^^^^^^^ help: try: `"aaa"` + | ^^^^^^^^ | = note: `-D clippy::needless-raw-strings` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]` +help: try + | +LL - r#"aaa"#; +LL + "aaa"; + | error: unnecessary raw string literal --> $DIR/needless_raw_string.rs:9:5 | LL | br#"aaa"#; - | ^^^^^^^^^ help: try: `b"aaa"` + | ^^^^^^^^^ + | +help: try + | +LL - br#"aaa"#; +LL + b"aaa"; + | + +error: unnecessary raw string literal + --> $DIR/needless_raw_string.rs:12:5 + | +LL | cr#"aaa"#; + | ^^^^^^^^^ + | +help: try + | +LL - cr#"aaa"#; +LL + c"aaa"; + | + +error: unnecessary raw string literal + --> $DIR/needless_raw_string.rs:16:5 + | +LL | / r#" +LL | | a +LL | | multiline +LL | | string +LL | | "#; + | |______^ + | +help: try + | +LL ~ " +LL | a +LL | multiline +LL | string +LL ~ "; + | -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/needless_raw_string_hashes.fixed b/tests/ui/needless_raw_string_hashes.fixed index 84902157ab4fc..e980adeeff4c1 100644 --- a/tests/ui/needless_raw_string_hashes.fixed +++ b/tests/ui/needless_raw_string_hashes.fixed @@ -3,17 +3,22 @@ #![feature(c_str_literals)] fn main() { - r#"aaa"#; + r"\aaa"; r#"Hello "world"!"#; r####" "### "## "# "####; r###" "aa" "# "## "###; - br#"aaa"#; + br"\aaa"; br#"Hello "world"!"#; br####" "### "## "# "####; br###" "aa" "# "## "###; - // currently disabled: https://github.com/rust-lang/rust/issues/113333 - // cr#"aaa"#; - // cr##"Hello "world"!"##; - // cr######" "### "## "# "######; - // cr######" "aa" "# "## "######; + cr"\aaa"; + cr#"Hello "world"!"#; + cr####" "### "## "# "####; + cr###" "aa" "# "## "###; + + r" + \a + multiline + string + "; } diff --git a/tests/ui/needless_raw_string_hashes.rs b/tests/ui/needless_raw_string_hashes.rs index 62abae8385926..6113c5f25ae64 100644 --- a/tests/ui/needless_raw_string_hashes.rs +++ b/tests/ui/needless_raw_string_hashes.rs @@ -3,17 +3,22 @@ #![feature(c_str_literals)] fn main() { - r#"aaa"#; + r#"\aaa"#; r##"Hello "world"!"##; r######" "### "## "# "######; r######" "aa" "# "## "######; - br#"aaa"#; + br#"\aaa"#; br##"Hello "world"!"##; br######" "### "## "# "######; br######" "aa" "# "## "######; - // currently disabled: https://github.com/rust-lang/rust/issues/113333 - // cr#"aaa"#; - // cr##"Hello "world"!"##; - // cr######" "### "## "# "######; - // cr######" "aa" "# "## "######; + cr#"\aaa"#; + cr##"Hello "world"!"##; + cr######" "### "## "# "######; + cr######" "aa" "# "## "######; + + r#" + \a + multiline + string + "#; } diff --git a/tests/ui/needless_raw_string_hashes.stderr b/tests/ui/needless_raw_string_hashes.stderr index 94c51c423b86d..5a8e3d04543af 100644 --- a/tests/ui/needless_raw_string_hashes.stderr +++ b/tests/ui/needless_raw_string_hashes.stderr @@ -1,41 +1,167 @@ error: unnecessary hashes around raw string literal - --> $DIR/needless_raw_string_hashes.rs:7:5 + --> $DIR/needless_raw_string_hashes.rs:6:5 | -LL | r##"Hello "world"!"##; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `r#"Hello "world"!"#` +LL | r#"\aaa"#; + | ^^^^^^^^^ | = note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_string_hashes)]` +help: remove all the hashes around the literal + | +LL - r#"\aaa"#; +LL + r"\aaa"; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:7:5 + | +LL | r##"Hello "world"!"##; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: remove one hash from both sides of the literal + | +LL - r##"Hello "world"!"##; +LL + r#"Hello "world"!"#; + | error: unnecessary hashes around raw string literal --> $DIR/needless_raw_string_hashes.rs:8:5 | LL | r######" "### "## "# "######; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r####" "### "## "# "####` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 2 hashes from both sides of the literal + | +LL - r######" "### "## "# "######; +LL + r####" "### "## "# "####; + | error: unnecessary hashes around raw string literal --> $DIR/needless_raw_string_hashes.rs:9:5 | LL | r######" "aa" "# "## "######; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r###" "aa" "# "## "###` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 3 hashes from both sides of the literal + | +LL - r######" "aa" "# "## "######; +LL + r###" "aa" "# "## "###; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:10:5 + | +LL | br#"\aaa"#; + | ^^^^^^^^^^ + | +help: remove all the hashes around the literal + | +LL - br#"\aaa"#; +LL + br"\aaa"; + | error: unnecessary hashes around raw string literal --> $DIR/needless_raw_string_hashes.rs:11:5 | LL | br##"Hello "world"!"##; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `br#"Hello "world"!"#` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove one hash from both sides of the literal + | +LL - br##"Hello "world"!"##; +LL + br#"Hello "world"!"#; + | error: unnecessary hashes around raw string literal --> $DIR/needless_raw_string_hashes.rs:12:5 | LL | br######" "### "## "# "######; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br####" "### "## "# "####` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 2 hashes from both sides of the literal + | +LL - br######" "### "## "# "######; +LL + br####" "### "## "# "####; + | error: unnecessary hashes around raw string literal --> $DIR/needless_raw_string_hashes.rs:13:5 | LL | br######" "aa" "# "## "######; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br###" "aa" "# "## "###` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 3 hashes from both sides of the literal + | +LL - br######" "aa" "# "## "######; +LL + br###" "aa" "# "## "###; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:14:5 + | +LL | cr#"\aaa"#; + | ^^^^^^^^^^ + | +help: remove all the hashes around the literal + | +LL - cr#"\aaa"#; +LL + cr"\aaa"; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:15:5 + | +LL | cr##"Hello "world"!"##; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove one hash from both sides of the literal + | +LL - cr##"Hello "world"!"##; +LL + cr#"Hello "world"!"#; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:16:5 + | +LL | cr######" "### "## "# "######; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 2 hashes from both sides of the literal + | +LL - cr######" "### "## "# "######; +LL + cr####" "### "## "# "####; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:17:5 + | +LL | cr######" "aa" "# "## "######; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 3 hashes from both sides of the literal + | +LL - cr######" "aa" "# "## "######; +LL + cr###" "aa" "# "## "###; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:19:5 + | +LL | / r#" +LL | | \a +LL | | multiline +LL | | string +LL | | "#; + | |______^ + | +help: remove all the hashes around the literal + | +LL ~ r" +LL | \a +LL | multiline +LL | string +LL ~ "; + | -error: aborting due to 6 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 001b94391ca81..c67a6d4494e05 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -337,10 +337,8 @@ pub fn test26() { pub fn test27() { loop { - //~^ ERROR: this loop never actually loops 'label: { let x = true; - // Lints because we cannot prove it's always `true` if x { break 'label; } @@ -349,6 +347,59 @@ pub fn test27() { } } +// issue 11004 +pub fn test29() { + loop { + 'label: { + if true { + break 'label; + } + return; + } + } +} + +pub fn test30() { + 'a: loop { + 'b: { + for j in 0..2 { + if j == 1 { + break 'b; + } + } + break 'a; + } + } +} + +pub fn test31(b: bool) { + 'a: loop { + 'b: { + 'c: loop { + //~^ ERROR: this loop never actually loops + if b { break 'c } else { break 'b } + } + continue 'a; + } + break 'a; + } +} + +pub fn test32() { + loop { + //~^ ERROR: this loop never actually loops + panic!("oh no"); + } + loop { + //~^ ERROR: this loop never actually loops + unimplemented!("not yet"); + } + loop { + // no error + todo!("maybe later"); + } +} + fn main() { test1(); test2(); diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 234780007b1af..3982f36cea9f8 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -154,16 +154,31 @@ LL | if let Some(_) = (0..20).next() { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: this loop never actually loops - --> $DIR/never_loop.rs:339:5 + --> $DIR/never_loop.rs:378:13 + | +LL | / 'c: loop { +LL | | +LL | | if b { break 'c } else { break 'b } +LL | | } + | |_____________^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:389:5 | LL | / loop { LL | | -LL | | 'label: { -LL | | let x = true; -... | -LL | | } +LL | | panic!("oh no"); +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:393:5 + | +LL | / loop { +LL | | +LL | | unimplemented!("not yet"); LL | | } | |_____^ -error: aborting due to 14 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/incorrect_clone_impl_on_copy_type.fixed b/tests/ui/non_canonical_clone_impl.fixed similarity index 100% rename from tests/ui/incorrect_clone_impl_on_copy_type.fixed rename to tests/ui/non_canonical_clone_impl.fixed diff --git a/tests/ui/incorrect_clone_impl_on_copy_type.rs b/tests/ui/non_canonical_clone_impl.rs similarity index 100% rename from tests/ui/incorrect_clone_impl_on_copy_type.rs rename to tests/ui/non_canonical_clone_impl.rs diff --git a/tests/ui/incorrect_clone_impl_on_copy_type.stderr b/tests/ui/non_canonical_clone_impl.stderr similarity index 54% rename from tests/ui/incorrect_clone_impl_on_copy_type.stderr rename to tests/ui/non_canonical_clone_impl.stderr index 566a1a4b14b58..44196751b053b 100644 --- a/tests/ui/incorrect_clone_impl_on_copy_type.stderr +++ b/tests/ui/non_canonical_clone_impl.stderr @@ -1,5 +1,5 @@ -error: incorrect implementation of `clone` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:9:29 +error: non-canonical implementation of `clone` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:9:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -7,10 +7,11 @@ LL | | Self(self.0) LL | | } | |_____^ help: change this to: `{ *self }` | - = note: `#[deny(clippy::incorrect_clone_impl_on_copy_type)]` on by default + = note: `-D clippy::non-canonical-clone-impl` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_canonical_clone_impl)]` -error: incorrect implementation of `clone_from` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:13:5 +error: unnecessary implementation of `clone_from` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:13:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); @@ -18,8 +19,8 @@ LL | | *self = source.clone(); LL | | } | |_____^ help: remove it -error: incorrect implementation of `clone` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:80:29 +error: non-canonical implementation of `clone` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:80:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -27,8 +28,8 @@ LL | | Self(self.0) LL | | } | |_____^ help: change this to: `{ *self }` -error: incorrect implementation of `clone_from` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:84:5 +error: unnecessary implementation of `clone_from` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:84:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed b/tests/ui/non_canonical_partial_ord_impl.fixed similarity index 100% rename from tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed rename to tests/ui/non_canonical_partial_ord_impl.fixed diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs b/tests/ui/non_canonical_partial_ord_impl.rs similarity index 100% rename from tests/ui/incorrect_partial_ord_impl_on_ord_type.rs rename to tests/ui/non_canonical_partial_ord_impl.rs diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr b/tests/ui/non_canonical_partial_ord_impl.stderr similarity index 63% rename from tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr rename to tests/ui/non_canonical_partial_ord_impl.stderr index 1f706984662b6..05cc717b9ba1d 100644 --- a/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr +++ b/tests/ui/non_canonical_partial_ord_impl.stderr @@ -1,5 +1,5 @@ -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:16:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl.rs:16:1 | LL | / impl PartialOrd for A { LL | | fn partial_cmp(&self, other: &Self) -> Option { @@ -10,10 +10,11 @@ LL | || } LL | | } | |__^ | - = note: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default + = note: `-D clippy::non-canonical-partial-ord-impl` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]` -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:50:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl.rs:50:1 | LL | / impl PartialOrd for C { LL | | fn partial_cmp(&self, _: &Self) -> Option { diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs b/tests/ui/non_canonical_partial_ord_impl_fully_qual.rs similarity index 82% rename from tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs rename to tests/ui/non_canonical_partial_ord_impl_fully_qual.rs index 1173a95d06599..2f8d5cf30c77a 100644 --- a/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs +++ b/tests/ui/non_canonical_partial_ord_impl_fully_qual.rs @@ -21,8 +21,6 @@ impl cmp::Ord for A { } impl PartialOrd for A { - //~^ ERROR: incorrect implementation of `partial_cmp` on an `Ord` type - //~| NOTE: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default fn partial_cmp(&self, other: &Self) -> Option { // NOTE: This suggestion is wrong, as `Ord` is not in scope. But this should be fine as it isn't // automatically applied @@ -46,7 +44,6 @@ impl cmp::Ord for B { } impl PartialOrd for B { - //~^ ERROR: incorrect implementation of `partial_cmp` on an `Ord` type fn partial_cmp(&self, other: &Self) -> Option { // This calls `B.cmp`, not `Ord::cmp`! Some(self.cmp(other)) diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr b/tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr similarity index 67% rename from tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr rename to tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr index 09d7a32e3342a..4978d7a873960 100644 --- a/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr +++ b/tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr @@ -1,9 +1,7 @@ -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs:23:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl_fully_qual.rs:23:1 | LL | / impl PartialOrd for A { -LL | | -LL | | LL | | fn partial_cmp(&self, other: &Self) -> Option { | | _____________________________________________________________- LL | || // NOTE: This suggestion is wrong, as `Ord` is not in scope. But this should be fine as it isn't @@ -14,13 +12,13 @@ LL | || } LL | | } | |__^ | - = note: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default + = note: `-D clippy::non-canonical-partial-ord-impl` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]` -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs:48:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl_fully_qual.rs:46:1 | LL | / impl PartialOrd for B { -LL | | LL | | fn partial_cmp(&self, other: &Self) -> Option { | | _____________________________________________________________- LL | || // This calls `B.cmp`, not `Ord::cmp`! diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 0fd68eb92b1f2..4df9be2c21d9a 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -14,6 +14,8 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] +#![allow(clippy::non_canonical_clone_impl)] +#![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] #![allow(clippy::overly_complex_bool_expr)] #![allow(clippy::new_without_default)] @@ -33,10 +35,10 @@ #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(useless_ptr_null_checks)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] #![allow(forgetting_references)] -#![allow(useless_ptr_null_checks)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] @@ -62,6 +64,8 @@ #![warn(clippy::mixed_read_write_in_expression)] #![warn(clippy::useless_conversion)] #![warn(clippy::match_result_ok)] +#![warn(clippy::non_canonical_clone_impl)] +#![warn(clippy::non_canonical_partial_ord_impl)] #![warn(clippy::arithmetic_side_effects)] #![warn(clippy::overly_complex_bool_expr)] #![warn(clippy::new_without_default)] @@ -85,12 +89,12 @@ #![warn(drop_bounds)] #![warn(dropping_copy_types)] #![warn(dropping_references)] +#![warn(useless_ptr_null_checks)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] #![warn(forgetting_copy_types)] #![warn(forgetting_references)] -#![warn(useless_ptr_null_checks)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 927937fba83a9..940e60068e7b9 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -14,6 +14,8 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] +#![allow(clippy::non_canonical_clone_impl)] +#![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] #![allow(clippy::overly_complex_bool_expr)] #![allow(clippy::new_without_default)] @@ -33,10 +35,10 @@ #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(useless_ptr_null_checks)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] #![allow(forgetting_references)] -#![allow(useless_ptr_null_checks)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] @@ -62,6 +64,8 @@ #![warn(clippy::eval_order_dependence)] #![warn(clippy::identity_conversion)] #![warn(clippy::if_let_some_result)] +#![warn(clippy::incorrect_clone_impl_on_copy_type)] +#![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] #![warn(clippy::integer_arithmetic)] #![warn(clippy::logic_bug)] #![warn(clippy::new_without_default_derive)] @@ -85,12 +89,12 @@ #![warn(clippy::drop_bounds)] #![warn(clippy::drop_copy)] #![warn(clippy::drop_ref)] +#![warn(clippy::fn_null_check)] #![warn(clippy::for_loop_over_option)] #![warn(clippy::for_loop_over_result)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::forget_copy)] #![warn(clippy::forget_ref)] -#![warn(clippy::fn_null_check)] #![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 d98fc7b30db57..30824e154b8bf 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,322 +8,334 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` +error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` + --> $DIR/rename.rs:67:9 + | +LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` + +error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` + --> $DIR/rename.rs:68:9 + | +LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` + error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:83:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> $DIR/rename.rs:84:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:91:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` +error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` + --> $DIR/rename.rs:92:9 + | +LL | #![warn(clippy::fn_null_check)] + | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` + error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:97:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` -error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> $DIR/rename.rs:93:9 - | -LL | #![warn(clippy::fn_null_check)] - | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` - error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:98: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:95:9 + --> $DIR/rename.rs:99: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:96:9 + --> $DIR/rename.rs:100:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> $DIR/rename.rs:97:9 + --> $DIR/rename.rs:101:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:98:9 + --> $DIR/rename.rs:102:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:99:9 + --> $DIR/rename.rs:103: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:100:9 + --> $DIR/rename.rs:104: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:101:9 + --> $DIR/rename.rs:105: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:102:9 + --> $DIR/rename.rs:106:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> $DIR/rename.rs:103:9 + --> $DIR/rename.rs:107:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:104:9 + --> $DIR/rename.rs:108: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:105:9 + --> $DIR/rename.rs:109:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 54 previous errors +error: aborting due to 56 previous errors diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index 14a1f7e1db57f..b25348bf99617 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -1,3 +1,5 @@ +//@ignore-32bit + #![warn(clippy::result_large_err)] #![allow(clippy::large_enum_variant)] diff --git a/tests/ui/result_large_err.stderr b/tests/ui/result_large_err.stderr index d42dd6600a324..6602f396a9c0d 100644 --- a/tests/ui/result_large_err.stderr +++ b/tests/ui/result_large_err.stderr @@ -1,5 +1,5 @@ error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:8:23 + --> $DIR/result_large_err.rs:10:23 | LL | pub fn large_err() -> Result<(), [u8; 512]> { | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -9,7 +9,7 @@ LL | pub fn large_err() -> Result<(), [u8; 512]> { = help: to override `-D warnings` add `#[allow(clippy::result_large_err)]` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:20:21 + --> $DIR/result_large_err.rs:22:21 | LL | pub fn ret() -> Result<(), Self> { | ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes @@ -17,7 +17,7 @@ LL | pub fn ret() -> Result<(), Self> { = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:26:26 + --> $DIR/result_large_err.rs:28:26 | LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes @@ -25,7 +25,7 @@ LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> { = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:32:45 + --> $DIR/result_large_err.rs:34:45 | LL | pub fn large_err_via_type_alias(x: T) -> Fdlr { | ^^^^^^^ the `Err`-variant is at least 240 bytes @@ -33,7 +33,7 @@ LL | pub fn large_err_via_type_alias(x: T) -> Fdlr { = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:41:34 + --> $DIR/result_large_err.rs:43:34 | LL | pub fn param_large_error() -> Result<(), (u128, R, FullyDefinedLargeError)> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 256 bytes @@ -41,7 +41,7 @@ LL | pub fn param_large_error() -> Result<(), (u128, R, FullyDefinedLargeErro = help: try reducing the size of `(u128, R, FullyDefinedLargeError)`, for example by boxing large elements or replacing it with `Box<(u128, R, FullyDefinedLargeError)>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:53:34 + --> $DIR/result_large_err.rs:55:34 | LL | _Omg([u8; 512]), | --------------- the largest variant contains at least 512 bytes @@ -52,7 +52,7 @@ LL | pub fn large_enum_error() -> Result<(), Self> { = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:66:30 + --> $DIR/result_large_err.rs:68:30 | LL | _Biggest([u8; 1024]), | -------------------- the largest variant contains at least 1024 bytes @@ -65,7 +65,7 @@ LL | fn large_enum_error() -> Result<(), Self> { = help: try reducing the size of `MultipleLargeVariants`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:73:25 + --> $DIR/result_large_err.rs:75:25 | LL | fn large_error() -> Result<(), [u8; 512]> { | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -73,7 +73,7 @@ LL | fn large_error() -> Result<(), [u8; 512]> { = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:93:29 + --> $DIR/result_large_err.rs:95:29 | LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -81,7 +81,7 @@ LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:103:40 + --> $DIR/result_large_err.rs:105:40 | LL | pub fn param_large_union() -> Result<(), UnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -89,7 +89,7 @@ LL | pub fn param_large_union() -> Result<(), UnionError> { = help: try reducing the size of `UnionError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:113:34 + --> $DIR/result_large_err.rs:115:34 | LL | pub fn array_error_subst() -> Result<(), ArrayError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes @@ -97,7 +97,7 @@ LL | pub fn array_error_subst() -> Result<(), ArrayError> { = help: try reducing the size of `ArrayError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:118:31 + --> $DIR/result_large_err.rs:120:31 | LL | pub fn array_error() -> Result<(), ArrayError<(i32, T), U>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes diff --git a/tests/ui/similar_names.rs b/tests/ui/similar_names.rs index c5a941316da30..f46af56c6e2b0 100644 --- a/tests/ui/similar_names.rs +++ b/tests/ui/similar_names.rs @@ -3,6 +3,7 @@ unused, clippy::println_empty_string, clippy::empty_loop, + clippy::never_loop, clippy::diverging_sub_expression, clippy::let_unit_value )] diff --git a/tests/ui/similar_names.stderr b/tests/ui/similar_names.stderr index 011dbe679c082..44ae3532a4862 100644 --- a/tests/ui/similar_names.stderr +++ b/tests/ui/similar_names.stderr @@ -1,11 +1,11 @@ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:21:9 + --> $DIR/similar_names.rs:22:9 | LL | let bpple: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:19:9 + --> $DIR/similar_names.rs:20:9 | LL | let apple: i32; | ^^^^^ @@ -13,73 +13,73 @@ LL | let apple: i32; = help: to override `-D warnings` add `#[allow(clippy::similar_names)]` error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:24:9 + --> $DIR/similar_names.rs:25:9 | LL | let cpple: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:19:9 + --> $DIR/similar_names.rs:20:9 | LL | let apple: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:49:9 + --> $DIR/similar_names.rs:50:9 | LL | let bluby: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:48:9 + --> $DIR/similar_names.rs:49:9 | LL | let blubx: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:54:9 + --> $DIR/similar_names.rs:55:9 | LL | let coke: i32; | ^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:52:9 + --> $DIR/similar_names.rs:53:9 | LL | let cake: i32; | ^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:73:9 + --> $DIR/similar_names.rs:74:9 | LL | let xyzeabc: i32; | ^^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:71:9 + --> $DIR/similar_names.rs:72:9 | LL | let xyz1abc: i32; | ^^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:78:9 + --> $DIR/similar_names.rs:79:9 | LL | let parsee: i32; | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:76:9 + --> $DIR/similar_names.rs:77:9 | LL | let parser: i32; | ^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:100:16 + --> $DIR/similar_names.rs:101:16 | LL | bpple: sprang, | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:99:16 + --> $DIR/similar_names.rs:100:16 | LL | apple: spring, | ^^^^^^ diff --git a/tests/ui/single_call_fn.rs b/tests/ui/single_call_fn.rs index d6493f234133d..3cc8061647dff 100644 --- a/tests/ui/single_call_fn.rs +++ b/tests/ui/single_call_fn.rs @@ -1,3 +1,4 @@ +//@ignore-32bit //@aux-build:proc_macros.rs #![allow(clippy::redundant_closure_call, unused)] #![warn(clippy::single_call_fn)] diff --git a/tests/ui/single_call_fn.stderr b/tests/ui/single_call_fn.stderr index 4acb383c40846..d5cd707754c2f 100644 --- a/tests/ui/single_call_fn.stderr +++ b/tests/ui/single_call_fn.stderr @@ -1,5 +1,5 @@ error: this function is only used once - --> $DIR/single_call_fn.rs:33:1 + --> $DIR/single_call_fn.rs:34:1 | LL | / fn c() { LL | | println!("really"); @@ -9,7 +9,7 @@ LL | | } | |_^ | help: used here - --> $DIR/single_call_fn.rs:40:5 + --> $DIR/single_call_fn.rs:41:5 | LL | c(); | ^ @@ -17,37 +17,37 @@ LL | c(); = help: to override `-D warnings` add `#[allow(clippy::single_call_fn)]` error: this function is only used once - --> $DIR/single_call_fn.rs:12:1 + --> $DIR/single_call_fn.rs:13:1 | LL | fn i() {} | ^^^^^^^^^ | help: used here - --> $DIR/single_call_fn.rs:17:13 + --> $DIR/single_call_fn.rs:18:13 | LL | let a = i; | ^ error: this function is only used once - --> $DIR/single_call_fn.rs:43:1 + --> $DIR/single_call_fn.rs:44:1 | LL | fn a() {} | ^^^^^^^^^ | help: used here - --> $DIR/single_call_fn.rs:46:5 + --> $DIR/single_call_fn.rs:47:5 | LL | a(); | ^ error: this function is only used once - --> $DIR/single_call_fn.rs:13:1 + --> $DIR/single_call_fn.rs:14:1 | LL | fn j() {} | ^^^^^^^^^ | help: used here - --> $DIR/single_call_fn.rs:24:9 + --> $DIR/single_call_fn.rs:25:9 | LL | j(); | ^ diff --git a/tests/ui/slow_vector_initialization.rs b/tests/ui/slow_vector_initialization.rs index f8d85ed38cd5a..5c3086c9d69ea 100644 --- a/tests/ui/slow_vector_initialization.rs +++ b/tests/ui/slow_vector_initialization.rs @@ -1,5 +1,5 @@ -use std::iter::repeat; //@no-rustfix +use std::iter::repeat; fn main() { resize_vector(); extend_vector(); @@ -86,6 +86,20 @@ fn from_empty_vec() { vec1 = Vec::new(); vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + + vec1 = vec![]; + vec1.resize(10, 0); + //~^ ERROR: slow zero-filling initialization + + macro_rules! x { + () => { + vec![] + }; + } + + // `vec![]` comes from another macro, don't warn + vec1 = x!(); + vec1.resize(10, 0); } fn do_stuff(vec: &mut [u8]) {} diff --git a/tests/ui/slow_vector_initialization.stderr b/tests/ui/slow_vector_initialization.stderr index f4501551656a2..4d24400ecb596 100644 --- a/tests/ui/slow_vector_initialization.stderr +++ b/tests/ui/slow_vector_initialization.stderr @@ -97,8 +97,16 @@ LL | vec1 = Vec::new(); LL | vec1.resize(10, 0); | ^^^^^^^^^^^^^^^^^^ +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:91:5 + | +LL | vec1 = vec![]; + | ------ help: consider replacing this with: `vec![0; 10]` +LL | vec1.resize(10, 0); + | ^^^^^^^^^^^^^^^^^^ + error: this argument is a mutable reference, but not used mutably - --> $DIR/slow_vector_initialization.rs:91:18 + --> $DIR/slow_vector_initialization.rs:105:18 | LL | fn do_stuff(vec: &mut [u8]) {} | ^^^^^^^^^ help: consider changing to: `&[u8]` @@ -106,5 +114,5 @@ LL | fn do_stuff(vec: &mut [u8]) {} = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed new file mode 100644 index 0000000000000..8027c053fb5b0 --- /dev/null +++ b/tests/ui/std_instead_of_core.fixed @@ -0,0 +1,62 @@ +#![warn(clippy::std_instead_of_core)] +#![allow(unused_imports)] + +extern crate alloc; + +#[warn(clippy::std_instead_of_core)] +fn std_instead_of_core() { + // Regular import + use core::hash::Hasher; + //~^ ERROR: used import from `std` instead of `core` + // Absolute path + use ::core::hash::Hash; + //~^ ERROR: used import from `std` instead of `core` + // Don't lint on `env` macro + use std::env; + + // Multiple imports + use core::fmt::{Debug, Result}; + //~^ ERROR: used import from `std` instead of `core` + + // Function calls + let ptr = core::ptr::null::(); + //~^ ERROR: used import from `std` instead of `core` + let ptr_mut = ::core::ptr::null_mut::(); + //~^ ERROR: used import from `std` instead of `core` + + // Types + let cell = core::cell::Cell::new(8u32); + //~^ ERROR: used import from `std` instead of `core` + let cell_absolute = ::core::cell::Cell::new(8u32); + //~^ ERROR: used import from `std` instead of `core` + + 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 core::iter::Iterator; + //~^ ERROR: used import from `std` instead of `core` +} + +#[warn(clippy::std_instead_of_alloc)] +fn std_instead_of_alloc() { + // Only lint once. + use alloc::vec; + //~^ ERROR: used import from `std` instead of `alloc` + use alloc::vec::Vec; + //~^ ERROR: used import from `std` instead of `alloc` +} + +#[warn(clippy::alloc_instead_of_core)] +fn alloc_instead_of_core() { + use core::slice::from_ref; + //~^ ERROR: used import from `alloc` instead of `core` +} + +fn main() { + std_instead_of_core(); + std_instead_of_alloc(); + alloc_instead_of_core(); +} diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index 2e44487d77e35..63a096384d716 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -17,7 +17,6 @@ fn std_instead_of_core() { // Multiple imports use std::fmt::{Debug, Result}; //~^ ERROR: used import from `std` instead of `core` - //~| ERROR: used import from `std` instead of `core` // Function calls let ptr = std::ptr::null::(); diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index 7435d716157ae..ca26f77bd37f9 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -2,103 +2,76 @@ error: used import from `std` instead of `core` --> $DIR/std_instead_of_core.rs:9:9 | LL | use std::hash::Hasher; - | ^^^^^^^^^^^^^^^^^ + | ^^^ help: consider importing the item from `core`: `core` | - = help: consider importing the item from `core` = note: `-D clippy::std-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:12:9 + --> $DIR/std_instead_of_core.rs:12:11 | LL | use ::std::hash::Hash; - | ^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:18:20 + --> $DIR/std_instead_of_core.rs:18:9 | LL | use std::fmt::{Debug, Result}; - | ^^^^^ - | - = help: consider importing the item from `core` - -error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:18:27 - | -LL | use std::fmt::{Debug, Result}; - | ^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:23:15 + --> $DIR/std_instead_of_core.rs:22:15 | LL | let ptr = std::ptr::null::(); - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:25:19 + --> $DIR/std_instead_of_core.rs:24:21 | LL | let ptr_mut = ::std::ptr::null_mut::(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:29:16 + --> $DIR/std_instead_of_core.rs:28:16 | LL | let cell = std::cell::Cell::new(8u32); - | ^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:31:25 + --> $DIR/std_instead_of_core.rs:30:27 | LL | let cell_absolute = ::std::cell::Cell::new(8u32); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:40:9 + --> $DIR/std_instead_of_core.rs:39:9 | LL | use std::iter::Iterator; - | ^^^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:47:9 + --> $DIR/std_instead_of_core.rs:46:9 | LL | use std::vec; - | ^^^^^^^^ + | ^^^ help: consider importing the item from `alloc`: `alloc` | - = help: consider importing the item from `alloc` = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:49:9 + --> $DIR/std_instead_of_core.rs:48:9 | LL | use std::vec::Vec; - | ^^^^^^^^^^^^^ - | - = help: consider importing the item from `alloc` + | ^^^ help: consider importing the item from `alloc`: `alloc` error: used import from `alloc` instead of `core` - --> $DIR/std_instead_of_core.rs:55:9 + --> $DIR/std_instead_of_core.rs:54:9 | LL | use alloc::slice::from_ref; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ help: consider importing the item from `core`: `core` | - = help: consider importing the item from `core` = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/transmute_32bit.stderr b/tests/ui/transmute_32bit.stderr index 75ddca60d2aaa..baa819e30fded 100644 --- a/tests/ui/transmute_32bit.stderr +++ b/tests/ui/transmute_32bit.stderr @@ -1,39 +1,29 @@ -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `f32` to a pointer --> $DIR/transmute_32bit.rs:6:31 | LL | let _: *const usize = std::mem::transmute(6.0f32); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: source type: `f32` (32 bits) - = note: target type: `*const usize` (64 bits) + = note: `-D clippy::wrong-transmute` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::wrong_transmute)]` -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `f32` to a pointer --> $DIR/transmute_32bit.rs:8:29 | LL | let _: *mut usize = std::mem::transmute(6.0f32); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `f32` (32 bits) - = note: target type: `*mut usize` (64 bits) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `char` to a pointer --> $DIR/transmute_32bit.rs:10:31 | LL | let _: *const usize = std::mem::transmute('x'); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `char` (32 bits) - = note: target type: `*const usize` (64 bits) + | ^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `char` to a pointer --> $DIR/transmute_32bit.rs:12:29 | LL | let _: *mut usize = std::mem::transmute('x'); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `char` (32 bits) - = note: target type: `*mut usize` (64 bits) + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/unnecessary_struct_initialization.fixed b/tests/ui/unnecessary_struct_initialization.fixed index b5dedef5f4c87..f3cf65da2d6ad 100644 --- a/tests/ui/unnecessary_struct_initialization.fixed +++ b/tests/ui/unnecessary_struct_initialization.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::incorrect_clone_impl_on_copy_type, unused)] +#![allow(clippy::non_canonical_clone_impl, unused)] #![warn(clippy::unnecessary_struct_initialization)] struct S { diff --git a/tests/ui/unnecessary_struct_initialization.rs b/tests/ui/unnecessary_struct_initialization.rs index 2222c3ddf3708..bd5302f9d85db 100644 --- a/tests/ui/unnecessary_struct_initialization.rs +++ b/tests/ui/unnecessary_struct_initialization.rs @@ -1,4 +1,4 @@ -#![allow(clippy::incorrect_clone_impl_on_copy_type, unused)] +#![allow(clippy::non_canonical_clone_impl, unused)] #![warn(clippy::unnecessary_struct_initialization)] struct S { diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index 3ff2acbe28f49..bcbca971a7820 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -120,6 +120,7 @@ fn issue11075() { stringify!($e) }; } + #[allow(clippy::never_loop)] for _string in [repro!(true), repro!(null)] { unimplemented!(); } diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index 2ab025f424ac6..087425585de8b 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -120,6 +120,7 @@ fn issue11075() { stringify!($e) }; } + #[allow(clippy::never_loop)] for _string in vec![repro!(true), repro!(null)] { unimplemented!(); } diff --git a/tests/ui/vec.stderr b/tests/ui/vec.stderr index a28024d236a1e..fc261838fe36d 100644 --- a/tests/ui/vec.stderr +++ b/tests/ui/vec.stderr @@ -86,31 +86,31 @@ LL | for _ in vec![1, 2, 3] {} | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` error: useless use of `vec!` - --> $DIR/vec.rs:123:20 + --> $DIR/vec.rs:124:20 | LL | for _string in vec![repro!(true), repro!(null)] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[repro!(true), repro!(null)]` error: useless use of `vec!` - --> $DIR/vec.rs:140:18 + --> $DIR/vec.rs:141:18 | LL | in_macro!(1, vec![1, 2], vec![1; 2]); | ^^^^^^^^^^ help: you can use an array directly: `[1, 2]` error: useless use of `vec!` - --> $DIR/vec.rs:140:30 + --> $DIR/vec.rs:141:30 | LL | in_macro!(1, vec![1, 2], vec![1; 2]); | ^^^^^^^^^^ help: you can use an array directly: `[1; 2]` error: useless use of `vec!` - --> $DIR/vec.rs:159:14 + --> $DIR/vec.rs:160:14 | LL | for a in vec![1, 2, 3] { | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` error: useless use of `vec!` - --> $DIR/vec.rs:163:14 + --> $DIR/vec.rs:164:14 | LL | for a in vec![String::new(), String::new()] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[String::new(), String::new()]` diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr index fc24dba45437c..6d382a267ad81 100644 --- a/tests/ui/write_literal_2.stderr +++ b/tests/ui/write_literal_2.stderr @@ -2,7 +2,9 @@ error: unnecessary raw string literal --> $DIR/write_literal_2.rs:13:24 | LL | writeln!(v, r"{}", r"{hello}"); - | ^^^^^^^^^^ help: try: `"{hello}"` + | -^^^^^^^^^ + | | + | help: use a string literal instead: `"{hello}"` | = note: `-D clippy::needless-raw-strings` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]` From 7f870201d3c46f40e6be34424ded337fe702fecb Mon Sep 17 00:00:00 2001 From: mojave2 Date: Wed, 13 Sep 2023 11:13:51 +0800 Subject: [PATCH 014/149] add `byref` checking for the guard's local --- clippy_lints/src/matches/redundant_guards.rs | 77 +++++++++++++++----- tests/ui/redundant_guards.fixed | 41 +++++++++++ tests/ui/redundant_guards.rs | 41 +++++++++++ tests/ui/redundant_guards.stderr | 38 +++++++++- 4 files changed, 176 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index 29af4812351e3..1f61e4df30dbf 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -34,24 +34,45 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { ], MatchSource::Normal, ) = if_expr.kind + && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, scrutinee, outer_arm) { + if is_field && is_byref { return; } + let pat_span = if let PatKind::Ref(pat, _) = arm.pat.kind { + if is_byref { pat.span } else { continue; } + } else { + if is_byref { continue; } + arm.pat.span + }; + emit_redundant_guards( cx, outer_arm, if_expr.span, - scrutinee, - arm.pat.span, + pat_span, + binding_span, + is_field, arm.guard, ); } // `Some(x) if let Some(2) = x` - else if let Guard::IfLet(let_expr) = guard { + else if let Guard::IfLet(let_expr) = guard + && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, let_expr.init, outer_arm) + { + if is_field && is_byref { return; } + let pat_span = if let PatKind::Ref(pat, _) = let_expr.pat.kind { + if is_byref && !is_field { pat.span } else { continue; } + } else { + if is_byref { continue; } + let_expr.pat.span + }; + emit_redundant_guards( cx, outer_arm, let_expr.span, - let_expr.init, - let_expr.pat.span, + pat_span, + binding_span, + is_field, None, ); } @@ -67,31 +88,48 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { // // This isn't necessary in the other two checks, as they must be a pattern already. && cx.typeck_results().expr_ty(local) == cx.typeck_results().expr_ty(pat) + && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, local, outer_arm) { + if is_field && is_byref { return; } + let pat_span = if let ExprKind::AddrOf(rustc_ast::BorrowKind::Ref, _, expr) = pat.kind { + if is_byref { expr.span } else { continue; } + } else { + if is_byref { continue; } + pat.span + }; + emit_redundant_guards( cx, outer_arm, if_expr.span, - local, - pat.span, + pat_span, + binding_span, + is_field, None, ); } } } -fn get_pat_binding<'tcx>(cx: &LateContext<'tcx>, guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>) -> Option<(Span, bool)> { +fn get_pat_binding<'tcx>( + cx: &LateContext<'tcx>, + guard_expr: &Expr<'_>, + outer_arm: &Arm<'tcx>, +) -> Option<(Span, bool, bool)> { if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) { let mut span = None; let mut multiple_bindings = false; + let mut is_byref = false; // `each_binding` gives the `HirId` of the `Pat` itself, not the binding outer_arm.pat.walk(|pat| { - if let PatKind::Binding(_, hir_id, _, _) = pat.kind + if let PatKind::Binding(bind_annot, hir_id, _, _) = pat.kind && hir_id == local - && span.replace(pat.span).is_some() { - multiple_bindings = true; - return false; + is_byref = matches!(bind_annot.0, rustc_ast::ByRef::Yes); + if span.replace(pat.span).is_some() { + multiple_bindings = true; + return false; + } } true @@ -102,7 +140,8 @@ fn get_pat_binding<'tcx>(cx: &LateContext<'tcx>, guard_expr: &Expr<'_>, outer_ar return span.map(|span| { ( span, - !matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), + matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), + is_byref, ) }); } @@ -115,14 +154,12 @@ fn emit_redundant_guards<'tcx>( cx: &LateContext<'tcx>, outer_arm: &Arm<'tcx>, guard_span: Span, - local: &Expr<'_>, pat_span: Span, + binding_span: Span, + field_binding: bool, inner_guard: Option>, ) { let mut app = Applicability::MaybeIncorrect; - let Some((pat_binding, can_use_shorthand)) = get_pat_binding(cx, local, outer_arm) else { - return; - }; span_lint_and_then( cx, @@ -134,10 +171,10 @@ fn emit_redundant_guards<'tcx>( diag.multipart_suggestion_verbose( "try", vec![ - if can_use_shorthand { - (pat_binding, binding_replacement.into_owned()) + if field_binding { + (binding_span.shrink_to_hi(), format!(": {binding_replacement}")) } else { - (pat_binding.shrink_to_hi(), format!(": {binding_replacement}")) + (binding_span, binding_replacement.into_owned()) }, ( guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()), diff --git a/tests/ui/redundant_guards.fixed b/tests/ui/redundant_guards.fixed index 9a1ec3a4d36a6..6793b9c0f6d38 100644 --- a/tests/ui/redundant_guards.fixed +++ b/tests/ui/redundant_guards.fixed @@ -143,3 +143,44 @@ fn g(opt_s: Option) { _ => {}, } } + +mod issue11465 { + enum A { + Foo([u8; 3]), + } + + struct B { + b: String, + c: i32, + } + + fn issue11465() { + let c = Some(1); + match c { + Some(1) => {}, + Some(2) => {}, + Some(3) => {}, + _ => {}, + }; + + let enum_a = A::Foo([98, 97, 114]); + match enum_a { + A::Foo(ref arr) if arr == b"foo" => {}, + A::Foo(ref arr) if let b"bar" = arr => {}, + A::Foo(ref arr) if matches!(arr, b"baz") => {}, + _ => {}, + }; + + let struct_b = B { + b: "bar".to_string(), + c: 42, + }; + match struct_b { + B { ref b, .. } if b == "bar" => {}, + B { ref c, .. } if c == &1 => {}, + B { ref c, .. } if let &1 = c => {}, + B { ref c, .. } if matches!(c, &1) => {}, + _ => {}, + } + } +} diff --git a/tests/ui/redundant_guards.rs b/tests/ui/redundant_guards.rs index e2e0ee816c51b..e63cd29e8afe9 100644 --- a/tests/ui/redundant_guards.rs +++ b/tests/ui/redundant_guards.rs @@ -143,3 +143,44 @@ fn g(opt_s: Option) { _ => {}, } } + +mod issue11465 { + enum A { + Foo([u8; 3]), + } + + struct B { + b: String, + c: i32, + } + + fn issue11465() { + let c = Some(1); + match c { + Some(ref x) if x == &1 => {}, + Some(ref x) if let &2 = x => {}, + Some(ref x) if matches!(x, &3) => {}, + _ => {}, + }; + + let enum_a = A::Foo([98, 97, 114]); + match enum_a { + A::Foo(ref arr) if arr == b"foo" => {}, + A::Foo(ref arr) if let b"bar" = arr => {}, + A::Foo(ref arr) if matches!(arr, b"baz") => {}, + _ => {}, + }; + + let struct_b = B { + b: "bar".to_string(), + c: 42, + }; + match struct_b { + B { ref b, .. } if b == "bar" => {}, + B { ref c, .. } if c == &1 => {}, + B { ref c, .. } if let &1 = c => {}, + B { ref c, .. } if matches!(c, &1) => {}, + _ => {}, + } + } +} diff --git a/tests/ui/redundant_guards.stderr b/tests/ui/redundant_guards.stderr index 5007723438263..54b5f2464e6a6 100644 --- a/tests/ui/redundant_guards.stderr +++ b/tests/ui/redundant_guards.stderr @@ -94,5 +94,41 @@ LL - x if matches!(x, Some(0)) => .., LL + Some(0) => .., | -error: aborting due to 8 previous errors +error: redundant guard + --> $DIR/redundant_guards.rs:160:28 + | +LL | Some(ref x) if x == &1 => {}, + | ^^^^^^^ + | +help: try + | +LL - Some(ref x) if x == &1 => {}, +LL + Some(1) => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:161:28 + | +LL | Some(ref x) if let &2 = x => {}, + | ^^^^^^^^^^ + | +help: try + | +LL - Some(ref x) if let &2 = x => {}, +LL + Some(2) => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:162:28 + | +LL | Some(ref x) if matches!(x, &3) => {}, + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL - Some(ref x) if matches!(x, &3) => {}, +LL + Some(3) => {}, + | + +error: aborting due to 11 previous errors From 5cd391c4adf0a50d07fe403ce7ddcbeb9922c8b9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 12 Sep 2023 23:47:47 +0200 Subject: [PATCH 015/149] make the set of methods between our two Const types more consistent --- clippy_lints/src/matches/overlapping_arms.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 8be3c178a2912..6e13148f2fc18 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -37,22 +37,14 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, None => { let min_val_const = ty.numeric_min_val(cx.tcx)?; - let min_constant = mir::ConstantKind::from_value( - cx.tcx.valtree_to_const_val((ty, min_val_const.to_valtree())), - ty, - ); - miri_to_const(cx, min_constant)? + miri_to_const(cx, mir::ConstantKind::from_ty_const(min_val_const, cx.tcx))? }, }; let rhs_const = match rhs { Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, None => { let max_val_const = ty.numeric_max_val(cx.tcx)?; - let max_constant = mir::ConstantKind::from_value( - cx.tcx.valtree_to_const_val((ty, max_val_const.to_valtree())), - ty, - ); - miri_to_const(cx, max_constant)? + miri_to_const(cx, mir::ConstantKind::from_ty_const(max_val_const, cx.tcx))? }, }; let lhs_val = lhs_const.int_value(cx, ty)?; From e3267b1fe7bf90348f76b42fba6ebd09ef8ef714 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 13 Sep 2023 14:39:41 +0200 Subject: [PATCH 016/149] Fix mutaby used async function argument in closure for `needless_pass_by_ref_mut` --- clippy_lints/src/needless_pass_by_ref_mut.rs | 85 +++++++++++++++----- tests/ui/needless_pass_by_ref_mut.rs | 28 ++++++- tests/ui/needless_pass_by_ref_mut.stderr | 26 +++++- 3 files changed, 118 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 7b00eabf97b41..bea44ed92ef38 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -4,12 +4,13 @@ use clippy_utils::source::snippet; use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor}; +use rustc_hir::intravisit::{walk_fn, walk_qpath, FnKind, Visitor}; use rustc_hir::{ - Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath, + Body, BodyId, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, + PatKind, QPath, }; use rustc_hir_typeck::expr_use_visitor as euv; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::associated_body; use rustc_middle::hir::nested_filter::OnlyBodies; @@ -95,6 +96,30 @@ fn should_skip<'tcx>( is_from_proc_macro(cx, &input) } +fn check_closures<'tcx>( + ctx: &mut MutablyUsedVariablesCtxt<'tcx>, + cx: &LateContext<'tcx>, + infcx: &InferCtxt<'tcx>, + checked_closures: &mut FxHashSet, + closures: FxHashSet, +) { + let hir = cx.tcx.hir(); + for closure in closures { + if !checked_closures.insert(closure) { + continue; + } + ctx.prev_bind = None; + ctx.prev_move_to_closure.clear(); + if let Some(body) = hir + .find_by_def_id(closure) + .and_then(associated_body) + .map(|(_, body_id)| hir.body(body_id)) + { + euv::ExprUseVisitor::new(ctx, infcx, closure, cx.param_env, cx.typeck_results()).consume_body(body); + } + } +} + impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { fn check_fn( &mut self, @@ -161,25 +186,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); if is_async { let mut checked_closures = FxHashSet::default(); + + // We retrieve all the closures declared in the async function because they will + // not be found by `euv::Delegate`. + let mut closures_retriever = ClosuresRetriever { + cx, + closures: FxHashSet::default(), + }; + walk_fn(&mut closures_retriever, kind, decl, body.id(), fn_def_id); + check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures_retriever.closures); + while !ctx.async_closures.is_empty() { let closures = ctx.async_closures.clone(); ctx.async_closures.clear(); - let hir = cx.tcx.hir(); - for closure in closures { - if !checked_closures.insert(closure) { - continue; - } - ctx.prev_bind = None; - ctx.prev_move_to_closure.clear(); - if let Some(body) = hir - .find_by_def_id(closure) - .and_then(associated_body) - .map(|(_, body_id)| hir.body(body_id)) - { - euv::ExprUseVisitor::new(&mut ctx, &infcx, closure, cx.param_env, cx.typeck_results()) - .consume_body(body); - } - } + check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures); } } ctx @@ -439,3 +459,30 @@ impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> { } } } + +struct ClosuresRetriever<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + closures: FxHashSet, +} + +impl<'a, 'tcx> Visitor<'tcx> for ClosuresRetriever<'a, 'tcx> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_fn( + &mut self, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'tcx>, + body_id: BodyId, + _span: Span, + fn_def_id: LocalDefId, + ) { + if matches!(kind, FnKind::Closure) { + self.closures.insert(fn_def_id); + } + walk_fn(self, kind, decl, body_id, fn_def_id); + } +} diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index e1e5e8fd220b1..da2a72caacb4e 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -1,4 +1,4 @@ -#![allow(clippy::if_same_then_else, clippy::no_effect)] +#![allow(clippy::if_same_then_else, clippy::no_effect, clippy::redundant_closure_call)] #![feature(lint_reasons)] //@no-rustfix use std::ptr::NonNull; @@ -231,6 +231,32 @@ async fn async_vec2(b: &mut Vec) { b.push(true); } +// Should not warn. +pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { + || { + *n += 1; + } +} + +// Should warn. +pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { + //~^ ERROR: this argument is a mutable reference, but not used mutably + || *n + 1 +} + +// Should not warn. +pub async fn closure3(n: &mut usize) { + (|| *n += 1)(); +} + +// Should warn. +pub async fn closure4(n: &mut usize) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + (|| { + let _x = *n + 1; + })(); +} + fn main() { let mut u = 0; let mut v = vec![0]; diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index df3df045776b8..0fb9458d79488 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -107,5 +107,29 @@ error: this argument is a mutable reference, but not used mutably LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` -error: aborting due to 17 previous errors +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:235:25 + | +LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { + | ^^^^^^^^^^ help: consider changing to: `&usize` + | + = warning: changing this function will impact semver compatibility + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:242:20 + | +LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { + | ^^^^^^^^^^ help: consider changing to: `&usize` + | + = warning: changing this function will impact semver compatibility + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:253:26 + | +LL | pub async fn closure4(n: &mut usize) { + | ^^^^^^^^^^ help: consider changing to: `&usize` + | + = warning: changing this function will impact semver compatibility + +error: aborting due to 20 previous errors From 0c7dcba28a502f8a6e9a5da533cb74feda0adb4c Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 13 Sep 2023 18:58:47 +0000 Subject: [PATCH 017/149] Remove `derive_new` test dependency It is the last thing depending on syn 1.0 in clippy --- Cargo.toml | 1 - tests/compile-test.rs | 2 -- tests/ui/redundant_field_names.fixed | 11 +++++++++-- tests/ui/redundant_field_names.rs | 11 +++++++++-- tests/ui/redundant_field_names.stderr | 2 +- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2d8b590dbe311..66786004f6e72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ itertools = "0.10.1" # UI test dependencies clippy_utils = { path = "clippy_utils" } -derive-new = "0.5" if_chain = "1.0" quote = "1.0" serde = { version = "1.0.125", features = ["derive"] } diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 9fcc269dbf8c2..f340cf5938a7d 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -18,7 +18,6 @@ use test_utils::IS_RUSTC_TEST_SUITE; // in the depinfo file (otherwise cargo thinks they are unused) extern crate clippy_lints; extern crate clippy_utils; -extern crate derive_new; extern crate futures; extern crate if_chain; extern crate itertools; @@ -33,7 +32,6 @@ mod test_utils; static TEST_DEPENDENCIES: &[&str] = &[ "clippy_lints", "clippy_utils", - "derive_new", "futures", "if_chain", "itertools", diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index bbe3b38e547bb..c578e786426f8 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,8 +1,9 @@ +//@aux-build:proc_macros.rs #![warn(clippy::redundant_field_names)] #![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] -extern crate derive_new; +extern crate proc_macros; use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; @@ -18,7 +19,6 @@ struct Person { foo: u8, } -#[derive(new)] pub struct S { v: String, } @@ -57,6 +57,13 @@ fn main() { let _ = Range { start, end }; let _ = RangeInclusive::new(start, end); let _ = RangeToInclusive { end }; + + external! { + let v = String::new(); + let _ = S { + v: v + }; + } } fn issue_3476() { diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 9afa191ce7c70..d8c2286d5ad65 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,8 +1,9 @@ +//@aux-build:proc_macros.rs #![warn(clippy::redundant_field_names)] #![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] -extern crate derive_new; +extern crate proc_macros; use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; @@ -18,7 +19,6 @@ struct Person { foo: u8, } -#[derive(new)] pub struct S { v: String, } @@ -57,6 +57,13 @@ fn main() { let _ = Range { start: start, end: end }; let _ = RangeInclusive::new(start, end); let _ = RangeToInclusive { end: end }; + + external! { + let v = String::new(); + let _ = S { + v: v + }; + } } fn issue_3476() { diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 5fee60b8ea411..6eb1cc7531928 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -44,7 +44,7 @@ LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:81:25 + --> $DIR/redundant_field_names.rs:88:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` From 2aaaeb4e8166cf7ba52cf606eec2219730a06395 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 11 Sep 2023 20:01:48 +0200 Subject: [PATCH 018/149] use AllocId instead of Allocation in ConstValue::ByRef --- clippy_utils/src/consts.rs | 53 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index adeb673b6b9dc..03341feffb8cd 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -684,34 +684,37 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t }, _ => None, }, - mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() { - ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), - ty::Array(sub_type, len) => match sub_type.kind() { - ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) { - Some(len) => alloc - .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap())) - .to_owned() - .array_chunks::<4>() - .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk)))) - .collect::>>>() - .map(Constant::Vec), - _ => None, - }, - ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) { - Some(len) => alloc - .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap())) - .to_owned() - .array_chunks::<8>() - .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk)))) - .collect::>>>() - .map(Constant::Vec), + mir::ConstantKind::Val(ConstValue::ByRef { alloc_id, offset: _ }, _) => { + let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory(); + match result.ty().kind() { + ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), + ty::Array(sub_type, len) => match sub_type.kind() { + ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) { + Some(len) => alloc + .inner() + .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap())) + .to_owned() + .array_chunks::<4>() + .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk)))) + .collect::>>>() + .map(Constant::Vec), + _ => None, + }, + ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) { + Some(len) => alloc + .inner() + .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap())) + .to_owned() + .array_chunks::<8>() + .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk)))) + .collect::>>>() + .map(Constant::Vec), + _ => None, + }, _ => None, }, _ => None, - }, - _ => None, + } }, _ => None, } From b2d5d68c58a745c80bc0187dba8c85e1519faabc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 12 Sep 2023 07:49:25 +0200 Subject: [PATCH 019/149] =?UTF-8?q?cleanup=20op=5Fto=5Fconst=20a=20bit;=20?= =?UTF-8?q?rename=20ConstValue::ByRef=20=E2=86=92=20Indirect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs | 2 +- clippy_utils/src/consts.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index f66f33fee1669..4a5b6fa5c18d6 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -232,7 +232,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { + ConstValue::Indirect { alloc, offset } if offset.bytes() == 0 => { read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity()) }, _ => None, diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 03341feffb8cd..61b944667a4ae 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -684,7 +684,7 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t }, _ => None, }, - mir::ConstantKind::Val(ConstValue::ByRef { alloc_id, offset: _ }, _) => { + mir::ConstantKind::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => { let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory(); match result.ty().kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), From 8d3bbb09640479b43b48a3c3a81580a1d3fbbc04 Mon Sep 17 00:00:00 2001 From: mojave2 Date: Thu, 14 Sep 2023 14:57:05 +0800 Subject: [PATCH 020/149] handle the byref binding in the struct pattern --- clippy_lints/src/matches/redundant_guards.rs | 99 ++++++++++---------- tests/ui/redundant_guards.fixed | 6 +- tests/ui/redundant_guards.stderr | 38 +++++++- 3 files changed, 90 insertions(+), 53 deletions(-) diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index 1f61e4df30dbf..d51d7edc8e353 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -2,11 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::{for_each_expr, is_local_used}; -use rustc_ast::LitKind; +use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind}; use rustc_lint::LateContext; +use rustc_span::symbol::Ident; use rustc_span::Span; use std::ops::ControlFlow; @@ -34,45 +35,37 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { ], MatchSource::Normal, ) = if_expr.kind - && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, scrutinee, outer_arm) + && let Some(binding) = get_pat_binding(cx, scrutinee, outer_arm) { - if is_field && is_byref { return; } - let pat_span = if let PatKind::Ref(pat, _) = arm.pat.kind { - if is_byref { pat.span } else { continue; } - } else { - if is_byref { continue; } - arm.pat.span + let pat_span = match (arm.pat.kind, binding.byref_ident) { + (PatKind::Ref(pat, _), Some(_)) => pat.span, + (PatKind::Ref(..), None) | (_, Some(_)) => continue, + _ => arm.pat.span, }; - emit_redundant_guards( cx, outer_arm, if_expr.span, pat_span, - binding_span, - is_field, + &binding, arm.guard, ); } // `Some(x) if let Some(2) = x` else if let Guard::IfLet(let_expr) = guard - && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, let_expr.init, outer_arm) + && let Some(binding) = get_pat_binding(cx, let_expr.init, outer_arm) { - if is_field && is_byref { return; } - let pat_span = if let PatKind::Ref(pat, _) = let_expr.pat.kind { - if is_byref && !is_field { pat.span } else { continue; } - } else { - if is_byref { continue; } - let_expr.pat.span + let pat_span = match (let_expr.pat.kind, binding.byref_ident) { + (PatKind::Ref(pat, _), Some(_)) => pat.span, + (PatKind::Ref(..), None) | (_, Some(_)) => continue, + _ => let_expr.pat.span, }; - emit_redundant_guards( cx, outer_arm, let_expr.span, pat_span, - binding_span, - is_field, + &binding, None, ); } @@ -88,61 +81,63 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { // // This isn't necessary in the other two checks, as they must be a pattern already. && cx.typeck_results().expr_ty(local) == cx.typeck_results().expr_ty(pat) - && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, local, outer_arm) + && let Some(binding) = get_pat_binding(cx, local, outer_arm) { - if is_field && is_byref { return; } - let pat_span = if let ExprKind::AddrOf(rustc_ast::BorrowKind::Ref, _, expr) = pat.kind { - if is_byref { expr.span } else { continue; } - } else { - if is_byref { continue; } - pat.span + let pat_span = match (pat.kind, binding.byref_ident) { + (ExprKind::AddrOf(BorrowKind::Ref, _, expr), Some(_)) => expr.span, + (ExprKind::AddrOf(..), None) | (_, Some(_)) => continue, + _ => pat.span, }; - emit_redundant_guards( cx, outer_arm, if_expr.span, pat_span, - binding_span, - is_field, + &binding, None, ); } } } +struct PatBindingInfo { + span: Span, + byref_ident: Option, + is_field: bool, +} + fn get_pat_binding<'tcx>( cx: &LateContext<'tcx>, guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>, -) -> Option<(Span, bool, bool)> { +) -> Option { if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) { let mut span = None; + let mut byref_ident = None; let mut multiple_bindings = false; - let mut is_byref = false; // `each_binding` gives the `HirId` of the `Pat` itself, not the binding outer_arm.pat.walk(|pat| { - if let PatKind::Binding(bind_annot, hir_id, _, _) = pat.kind + if let PatKind::Binding(bind_annot, hir_id, ident, _) = pat.kind && hir_id == local { - is_byref = matches!(bind_annot.0, rustc_ast::ByRef::Yes); + if matches!(bind_annot.0, rustc_ast::ByRef::Yes) { + let _ = byref_ident.insert(ident); + } + // the second call of `replce()` returns a `Some(span)`, meaning a multi-binding pattern if span.replace(pat.span).is_some() { multiple_bindings = true; return false; } } - true }); // Ignore bindings from or patterns, like `First(x) | Second(x, _) | Third(x, _, _)` if !multiple_bindings { - return span.map(|span| { - ( - span, - matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), - is_byref, - ) + return span.map(|span| PatBindingInfo { + span, + byref_ident, + is_field: matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), }); } } @@ -155,8 +150,7 @@ fn emit_redundant_guards<'tcx>( outer_arm: &Arm<'tcx>, guard_span: Span, pat_span: Span, - binding_span: Span, - field_binding: bool, + pat_binding: &PatBindingInfo, inner_guard: Option>, ) { let mut app = Applicability::MaybeIncorrect; @@ -168,14 +162,21 @@ fn emit_redundant_guards<'tcx>( "redundant guard", |diag| { let binding_replacement = snippet_with_applicability(cx, pat_span, "", &mut app); + let suggestion_span = match *pat_binding { + PatBindingInfo { + span, + byref_ident: Some(ident), + is_field: true, + } => (span, format!("{ident}: {binding_replacement}")), + PatBindingInfo { + span, is_field: true, .. + } => (span.shrink_to_hi(), format!(": {binding_replacement}")), + PatBindingInfo { span, .. } => (span, binding_replacement.into_owned()), + }; diag.multipart_suggestion_verbose( "try", vec![ - if field_binding { - (binding_span.shrink_to_hi(), format!(": {binding_replacement}")) - } else { - (binding_span, binding_replacement.into_owned()) - }, + suggestion_span, ( guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()), inner_guard.map_or_else(String::new, |guard| { diff --git a/tests/ui/redundant_guards.fixed b/tests/ui/redundant_guards.fixed index 6793b9c0f6d38..20fcc1254a66d 100644 --- a/tests/ui/redundant_guards.fixed +++ b/tests/ui/redundant_guards.fixed @@ -177,9 +177,9 @@ mod issue11465 { }; match struct_b { B { ref b, .. } if b == "bar" => {}, - B { ref c, .. } if c == &1 => {}, - B { ref c, .. } if let &1 = c => {}, - B { ref c, .. } if matches!(c, &1) => {}, + B { c: 1, .. } => {}, + B { c: 1, .. } => {}, + B { c: 1, .. } => {}, _ => {}, } } diff --git a/tests/ui/redundant_guards.stderr b/tests/ui/redundant_guards.stderr index 54b5f2464e6a6..0a6146413d39a 100644 --- a/tests/ui/redundant_guards.stderr +++ b/tests/ui/redundant_guards.stderr @@ -130,5 +130,41 @@ LL - Some(ref x) if matches!(x, &3) => {}, LL + Some(3) => {}, | -error: aborting due to 11 previous errors +error: redundant guard + --> $DIR/redundant_guards.rs:180:32 + | +LL | B { ref c, .. } if c == &1 => {}, + | ^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if c == &1 => {}, +LL + B { c: 1, .. } => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:181:32 + | +LL | B { ref c, .. } if let &1 = c => {}, + | ^^^^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if let &1 = c => {}, +LL + B { c: 1, .. } => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:182:32 + | +LL | B { ref c, .. } if matches!(c, &1) => {}, + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if matches!(c, &1) => {}, +LL + B { c: 1, .. } => {}, + | + +error: aborting due to 14 previous errors From 272df70b50992885dcc70b8c34471234985b3be2 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 13 Sep 2023 13:55:23 +0000 Subject: [PATCH 021/149] treat host effect params as erased generics in codegen This fixes the changes brought to codegen tests when effect params are added to libcore, by not attempting to monomorphize functions that get the host param by being `const fn`. --- clippy_lints/src/derive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index d3311792cfa8b..2bdac1352dce3 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -343,7 +343,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h // If the current self type doesn't implement Copy (due to generic constraints), search to see if // there's a Copy impl for any instance of the adt. if !is_copy(cx, ty) { - if ty_subs.non_erasable_generics().next().is_some() { + if ty_subs.non_erasable_generics(cx.tcx, ty_adt.did()).next().is_some() { let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).map_or(false, |impls| { impls.iter().any(|&id| { matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) From 477a9b88bf31168e747a463b4942a8d64ae5917d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 12 Sep 2023 14:44:38 +0200 Subject: [PATCH 022/149] fix clippy (and MIR printing) handling of ConstValue::Indirect slices --- clippy_utils/src/consts.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 61b944667a4ae..fcb90c63a6fc6 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -671,19 +671,10 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), _ => None, }, - mir::ConstantKind::Val(ConstValue::Slice { data, start, end }, _) => match result.ty().kind() { - ty::Ref(_, tam, _) => match tam.kind() { - ty::Str => String::from_utf8( - data.inner() - .inspect_with_uninit_and_ptr_outside_interpreter(start..end) - .to_owned(), - ) - .ok() - .map(Constant::Str), - _ => None, - }, - _ => None, - }, + mir::ConstantKind::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => { + let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; + String::from_utf8(data.to_owned()).ok().map(Constant::Str) + } mir::ConstantKind::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => { let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory(); match result.ty().kind() { From c29de92d85f22bdc8814e87e4d4ee9f46d3c679e Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 1 Sep 2023 17:05:30 +0000 Subject: [PATCH 023/149] Return a value from find_format_args instead of using a callback --- clippy_lints/src/explicit_write.rs | 90 +++++++++---------- clippy_lints/src/format.rs | 14 ++- clippy_lints/src/format_args.rs | 20 ++--- clippy_lints/src/format_impl.rs | 41 +++++---- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 13 ++- .../src/utils/format_args_collector.rs | 28 ++++-- clippy_lints/src/write.rs | 10 +-- clippy_utils/src/macros.rs | 37 ++++---- 9 files changed, 126 insertions(+), 129 deletions(-) diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 4b9ca8c917e52..b612cc00bf97e 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -57,54 +57,52 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { } else { None } + && let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root()) { - find_format_args(cx, write_arg, ExpnId::root(), |format_args| { - let calling_macro = - // ordering is important here, since `writeln!` uses `write!` internally - if is_expn_of(write_call.span, "writeln").is_some() { - Some("writeln") - } else if is_expn_of(write_call.span, "write").is_some() { - Some("write") - } else { - None - }; - let prefix = if dest_name == "stderr" { - "e" - } else { - "" - }; + // ordering is important here, since `writeln!` uses `write!` internally + let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() { + Some("writeln") + } else if is_expn_of(write_call.span, "write").is_some() { + Some("write") + } else { + None + }; + let prefix = if dest_name == "stderr" { + "e" + } else { + "" + }; - // We need to remove the last trailing newline from the string because the - // underlying `fmt::write` function doesn't know whether `println!` or `print!` was - // used. - let (used, sugg_mac) = if let Some(macro_name) = calling_macro { - ( - format!("{macro_name}!({dest_name}(), ...)"), - macro_name.replace("write", "print"), - ) - } else { - ( - format!("{dest_name}().write_fmt(...)"), - "print".into(), - ) - }; - let mut applicability = Applicability::MachineApplicable; - let inputs_snippet = snippet_with_applicability( - cx, - format_args_inputs_span(format_args), - "..", - &mut applicability, - ); - span_lint_and_sugg( - cx, - EXPLICIT_WRITE, - expr.span, - &format!("use of `{used}.unwrap()`"), - "try", - format!("{prefix}{sugg_mac}!({inputs_snippet})"), - applicability, - ); - }); + // We need to remove the last trailing newline from the string because the + // underlying `fmt::write` function doesn't know whether `println!` or `print!` was + // used. + let (used, sugg_mac) = if let Some(macro_name) = calling_macro { + ( + format!("{macro_name}!({dest_name}(), ...)"), + macro_name.replace("write", "print"), + ) + } else { + ( + format!("{dest_name}().write_fmt(...)"), + "print".into(), + ) + }; + let mut applicability = Applicability::MachineApplicable; + let inputs_snippet = snippet_with_applicability( + cx, + format_args_inputs_span(&format_args), + "..", + &mut applicability, + ); + span_lint_and_sugg( + cx, + EXPLICIT_WRITE, + expr.span, + &format!("use of `{used}.unwrap()`"), + "try", + format!("{prefix}{sugg_mac}!({inputs_snippet})"), + applicability, + ); } } } diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index f4f8bdc2c44e5..b748d32936792 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -43,14 +43,10 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); impl<'tcx> LateLintPass<'tcx> for UselessFormat { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { - return; - }; - if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) { - return; - } - - find_format_args(cx, expr, macro_call.expn, |format_args| { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) + && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + { let mut applicability = Applicability::MachineApplicable; let call_site = macro_call.span; @@ -91,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { }, _ => {}, } - }); + } } } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 01c714c414b5a..39abf5c2def56 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -186,15 +186,10 @@ impl FormatArgs { impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { - return; - }; - if !is_format_macro(cx, macro_call.def_id) { - return; - } - let name = cx.tcx.item_name(macro_call.def_id); - - find_format_args(cx, expr, macro_call.expn, |format_args| { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && is_format_macro(cx, macro_call.def_id) + && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + { for piece in &format_args.template { if let FormatArgsPiece::Placeholder(placeholder) = piece && let Ok(index) = placeholder.argument.index @@ -206,12 +201,13 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if placeholder.format_trait != FormatTrait::Display || placeholder.format_options != FormatOptions::default() - || is_aliased(format_args, index) + || is_aliased(&format_args, index) { continue; } if let Ok(arg_hir_expr) = arg_expr { + let name = cx.tcx.item_name(macro_call.def_id); check_format_in_format_args(cx, macro_call.span, name, arg_hir_expr); check_to_string_in_format_args(cx, name, arg_hir_expr); } @@ -219,9 +215,9 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { } if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, format_args, macro_call.span, macro_call.def_id, self.ignore_mixed); + check_uninlined_args(cx, &format_args, macro_call.span, macro_call.def_id, self.ignore_mixed); } - }); + } } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 76369bccf9e3b..1d2f7cb71303b 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -170,30 +170,29 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, if let Some(outer_macro) = root_macro_call_first_node(cx, expr) && let macro_def_id = outer_macro.def_id && is_format_macro(cx, macro_def_id) + && let Some(format_args) = find_format_args(cx, expr, outer_macro.expn) { - find_format_args(cx, expr, outer_macro.expn, |format_args| { - for piece in &format_args.template { - if let FormatArgsPiece::Placeholder(placeholder) = piece - && let trait_name = match placeholder.format_trait { - FormatTrait::Display => sym::Display, - FormatTrait::Debug => sym::Debug, - FormatTrait::LowerExp => sym!(LowerExp), - FormatTrait::UpperExp => sym!(UpperExp), - FormatTrait::Octal => sym!(Octal), - FormatTrait::Pointer => sym::Pointer, - FormatTrait::Binary => sym!(Binary), - FormatTrait::LowerHex => sym!(LowerHex), - FormatTrait::UpperHex => sym!(UpperHex), - } - && trait_name == impl_trait.name - && let Ok(index) = placeholder.argument.index - && let Some(arg) = format_args.arguments.all_args().get(index) - && let Ok(arg_expr) = find_format_arg_expr(expr, arg) - { - check_format_arg_self(cx, expr.span, arg_expr, impl_trait); + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(placeholder) = piece + && let trait_name = match placeholder.format_trait { + FormatTrait::Display => sym::Display, + FormatTrait::Debug => sym::Debug, + FormatTrait::LowerExp => sym!(LowerExp), + FormatTrait::UpperExp => sym!(UpperExp), + FormatTrait::Octal => sym!(Octal), + FormatTrait::Pointer => sym::Pointer, + FormatTrait::Binary => sym!(Binary), + FormatTrait::LowerHex => sym!(LowerHex), + FormatTrait::UpperHex => sym!(UpperHex), } + && trait_name == impl_trait.name + && let Ok(index) = placeholder.argument.index + && let Some(arg) = format_args.arguments.all_args().get(index) + && let Ok(arg_expr) = find_format_arg_expr(expr, arg) + { + check_format_arg_self(cx, expr.span, arg_expr, impl_trait); } - }); + } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f5035dcfeeb6a..3f6bdb2b62cc8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -609,7 +609,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: .collect(), )) }); - store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector)); + store.register_early_pass(|| Box::::default()); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index d3e90e4bba392..40e487bf65058 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -131,13 +131,12 @@ pub(super) fn check<'tcx>( let mut applicability = Applicability::MachineApplicable; - //Special handling for `format!` as arg_root + // Special handling for `format!` as arg_root if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) { - if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) { - return; - } - find_format_args(cx, arg_root, macro_call.expn, |format_args| { - let span = format_args_inputs_span(format_args); + if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) + && let Some(format_args) = find_format_args(cx, arg_root, macro_call.expn) + { + let span = format_args_inputs_span(&format_args); let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_sugg( cx, @@ -148,7 +147,7 @@ pub(super) fn check<'tcx>( format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, ); - }); + } return; } diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs index 6d3493523e6fc..94a9a7c241bb7 100644 --- a/clippy_lints/src/utils/format_args_collector.rs +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -1,12 +1,15 @@ -use clippy_utils::macros::collect_ast_format_args; +use clippy_utils::macros::AST_FORMAT_ARGS; use clippy_utils::source::snippet_opt; use itertools::Itertools; -use rustc_ast::{Expr, ExprKind, FormatArgs}; +use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; +use rustc_data_structures::fx::FxHashMap; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::hygiene; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{hygiene, Span}; use std::iter::once; +use std::mem; +use std::rc::Rc; declare_clippy_lint! { /// ### What it does @@ -17,7 +20,12 @@ declare_clippy_lint! { "collects `format_args` AST nodes for use in later lints" } -declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]); +#[derive(Default)] +pub struct FormatArgsCollector { + format_args: FxHashMap>, +} + +impl_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]); impl EarlyLintPass for FormatArgsCollector { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { @@ -26,9 +34,17 @@ impl EarlyLintPass for FormatArgsCollector { return; } - collect_ast_format_args(expr.span, args); + self.format_args + .insert(expr.span.with_parent(None), Rc::new((**args).clone())); } } + + fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) { + AST_FORMAT_ARGS.with(|ast_format_args| { + let result = ast_format_args.set(mem::take(&mut self.format_args)); + debug_assert!(result.is_ok()); + }); + } } /// Detects if the format string or an argument has its span set by a proc macro to something inside diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index a9957b18a53b3..da083fb14aae1 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -304,7 +304,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { _ => return, } - find_format_args(cx, expr, macro_call.expn, |format_args| { + if let Some(format_args) = find_format_args(cx, expr, macro_call.expn) { // ignore `writeln!(w)` and `write!(v, some_macro!())` if format_args.span.from_expansion() { return; @@ -312,15 +312,15 @@ impl<'tcx> LateLintPass<'tcx> for Write { match diag_name { sym::print_macro | sym::eprint_macro | sym::write_macro => { - check_newline(cx, format_args, ¯o_call, name); + check_newline(cx, &format_args, ¯o_call, name); }, sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { - check_empty_string(cx, format_args, ¯o_call, name); + check_empty_string(cx, &format_args, ¯o_call, name); }, _ => {}, } - check_literal(cx, format_args, name); + check_literal(cx, &format_args, name); if !self.in_debug_impl { for piece in &format_args.template { @@ -334,7 +334,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { } } } - }); + } } } diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 98724fcbe96a3..82508bcdb857a 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -10,8 +10,9 @@ use rustc_lint::LateContext; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol}; -use std::cell::RefCell; +use std::cell::OnceCell; use std::ops::ControlFlow; +use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ @@ -374,30 +375,21 @@ thread_local! { /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an /// assumption that the early pass that populates the map and the later late passes will all be /// running on the same thread. - static AST_FORMAT_ARGS: RefCell> = { + #[doc(hidden)] + pub static AST_FORMAT_ARGS: OnceCell>> = { static CALLED: AtomicBool = AtomicBool::new(false); debug_assert!( !CALLED.swap(true, Ordering::SeqCst), "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread", ); - RefCell::default() + OnceCell::new() }; } -/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by -/// `FormatArgsCollector` -pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) { - AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args - .borrow_mut() - .insert(span.with_parent(None), format_args.clone()); - }); -} - -/// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a -/// descendant of `expn_id` -pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) { +/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of +/// `expn_id` +pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option> { let format_args_expr = for_each_expr(start, |expr| { let ctxt = expr.span.ctxt(); if ctxt.outer_expn().is_descendant_of(expn_id) { @@ -412,13 +404,14 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, } else { ControlFlow::Continue(Descend::No) } - }); + })?; - if let Some(expr) = format_args_expr { - AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args.borrow().get(&expr.span.with_parent(None)).map(callback); - }); - } + AST_FORMAT_ARGS.with(|ast_format_args| { + ast_format_args + .get()? + .get(&format_args_expr.span.with_parent(None)) + .map(Rc::clone) + }) } /// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if From da4244e79c49d4a938c02eebb955f9b217001213 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 14 Sep 2023 17:47:36 +0000 Subject: [PATCH 024/149] Ignore `#[doc(hidden)]` functions in clippy doc lints --- clippy_lints/src/doc.rs | 4 ++++ tests/ui/doc_errors.rs | 15 +++++++++++++++ tests/ui/doc_errors.stderr | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 4498e9ccdec25..bf2add6aa64f7 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -479,6 +479,10 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ Some(("fake".into(), "fake".into())) } + if is_doc_hidden(attrs) { + return None; + } + let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let mut doc = String::new(); for fragment in &fragments { diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index d661231c59ea8..9b3783aaf09de 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -101,6 +101,11 @@ impl Struct1 { fn block_comment_leading_asterisks() -> Result<(), ()> { unimplemented!(); } + + #[doc(hidden)] + fn doc_hidden() -> Result<(), ()> { + unimplemented!(); + } } pub trait Trait1 { @@ -111,6 +116,11 @@ pub trait Trait1 { /// # Errors /// A description of the errors goes here. fn trait_method_with_errors_header() -> Result<(), ()>; + + #[doc(hidden)] + fn doc_hidden() -> Result<(), ()> { + unimplemented!(); + } } impl Trait1 for Struct1 { @@ -123,6 +133,11 @@ impl Trait1 for Struct1 { } } +#[doc(hidden)] +pub trait DocHidden { + fn f() -> Result<(), ()>; +} + fn main() -> Result<(), ()> { Ok(()) } diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index 28b2db2440b7a..dc59675b9e5f2 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -38,7 +38,7 @@ LL | pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:108:5 + --> $DIR/doc_errors.rs:113:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 1d49cb6118c346871f85dc3a0e7bb3330b09687b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 11 Sep 2023 09:52:45 +0200 Subject: [PATCH 025/149] move required_consts check to general post-mono-check function --- clippy_lints/src/non_copy_const.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 243192385c256..8846633378798 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -204,7 +204,7 @@ fn is_value_unfrozen_raw<'tcx>( // similar to 2., but with the a frozen variant) (e.g. borrowing // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`). // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). - err == ErrorHandled::TooGeneric + matches!(err, ErrorHandled::TooGeneric(..)) }, |val| val.map_or(true, |val| inner(cx, val, ty)), ) @@ -244,8 +244,8 @@ pub fn const_eval_resolve<'tcx>( }; tcx.const_eval_global_id_for_typeck(param_env, cid, span) }, - Ok(None) => Err(ErrorHandled::TooGeneric), - Err(err) => Err(ErrorHandled::Reported(err.into())), + Ok(None) => Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP))), + Err(err) => Err(ErrorHandled::Reported(err.into(), span.unwrap_or(rustc_span::DUMMY_SP))), } } From 3226e4b01089b4e8e8d474a8d498bb0c68e76ed2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 11 Sep 2023 23:09:11 +0200 Subject: [PATCH 026/149] don't point at const usage site for resolution-time errors also share the code that emits the actual error --- tests/ui-toml/suppress_lint_in_const/test.stderr | 2 +- tests/ui/indexing_slicing_index.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui-toml/suppress_lint_in_const/test.stderr b/tests/ui-toml/suppress_lint_in_const/test.stderr index d14974faffa67..f8ace7995939a 100644 --- a/tests/ui-toml/suppress_lint_in_const/test.stderr +++ b/tests/ui-toml/suppress_lint_in_const/test.stderr @@ -4,7 +4,7 @@ error[E0080]: evaluation of `main::{constant#3}` failed LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -note: erroneous constant used +note: erroneous constant encountered --> $DIR/test.rs:37:5 | LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 64facf208034b..1c34875d2b8ab 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -24,7 +24,7 @@ error[E0080]: evaluation of `main::{constant#3}` failed LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -note: erroneous constant used +note: erroneous constant encountered --> $DIR/indexing_slicing_index.rs:48:5 | LL | const { &ARR[idx4()] }; From a2a31a0a2fd356f681c78498719dea8aa2523190 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 14 Sep 2023 21:20:50 +0000 Subject: [PATCH 027/149] Ignore closures for some type lints --- clippy_lints/src/types/mod.rs | 11 ++++++----- tests/ui/redundant_allocation.rs | 5 +++++ tests/ui/vec_box_sized.fixed | 5 +++++ tests/ui/vec_box_sized.rs | 5 +++++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 79f9d45d597ef..71a4b3fba1b59 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -315,7 +315,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn( &mut self, cx: &LateContext<'_>, - _: FnKind<'_>, + fn_kind: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, @@ -340,6 +340,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { CheckTyContext { is_in_trait_impl, is_exported, + in_body: matches!(fn_kind, FnKind::Closure), ..CheckTyContext::default() }, ); @@ -427,7 +428,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { cx, ty, CheckTyContext { - is_local: true, + in_body: true, ..CheckTyContext::default() }, ); @@ -481,7 +482,7 @@ impl Types { } match hir_ty.kind { - TyKind::Path(ref qpath) if !context.is_local => { + TyKind::Path(ref qpath) if !context.in_body => { let hir_id = hir_ty.hir_id; let res = cx.qpath_res(qpath, hir_id); if let Some(def_id) = res.opt_def_id() { @@ -581,8 +582,8 @@ impl Types { #[derive(Clone, Copy, Default)] struct CheckTyContext { is_in_trait_impl: bool, - /// `true` for types on local variables. - is_local: bool, + /// `true` for types on local variables and in closure signatures. + in_body: bool, /// `true` for types that are part of the public API. is_exported: bool, is_nested_call: bool, diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index b3257c04f8296..e70f8e71fae85 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -159,4 +159,9 @@ mod box_fat_ptr { //~| NOTE: `Box>` is already on the heap, `Rc>>` makes } +// https://github.com/rust-lang/rust-clippy/issues/11417 +fn type_in_closure() { + let _ = |_: &mut Box>| {}; +} + fn main() {} diff --git a/tests/ui/vec_box_sized.fixed b/tests/ui/vec_box_sized.fixed index 4a5ef83856a41..4363d2224afd2 100644 --- a/tests/ui/vec_box_sized.fixed +++ b/tests/ui/vec_box_sized.fixed @@ -49,4 +49,9 @@ mod inner_mod { } } +// https://github.com/rust-lang/rust-clippy/issues/11417 +fn in_closure() { + let _ = |_: Vec>| {}; +} + fn main() {} diff --git a/tests/ui/vec_box_sized.rs b/tests/ui/vec_box_sized.rs index ea020405a30f0..f4e27fe4bd509 100644 --- a/tests/ui/vec_box_sized.rs +++ b/tests/ui/vec_box_sized.rs @@ -49,4 +49,9 @@ mod inner_mod { } } +// https://github.com/rust-lang/rust-clippy/issues/11417 +fn in_closure() { + let _ = |_: Vec>| {}; +} + fn main() {} From 3c0fc15848395ed5ef714c8817df8bd4f1bc430f Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 14 Sep 2023 22:17:51 +0000 Subject: [PATCH 028/149] Truncate files when opening in metadata-collector --- .../internal_lints/metadata_collector.rs | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index f49c3fadb0780..c38a3e81b0f72 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -31,7 +31,7 @@ use serde::{Serialize, Serializer}; use std::collections::{BTreeSet, BinaryHeap}; use std::fmt; use std::fmt::Write as _; -use std::fs::{self, OpenOptions}; +use std::fs::{self, File}; use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::process::Command; @@ -229,25 +229,10 @@ impl Drop for MetadataCollector { collect_renames(&mut lints); // Outputting json - if Path::new(JSON_OUTPUT_FILE).exists() { - fs::remove_file(JSON_OUTPUT_FILE).unwrap(); - } - let mut file = OpenOptions::new() - .write(true) - .create(true) - .open(JSON_OUTPUT_FILE) - .unwrap(); - writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap(); + fs::write(JSON_OUTPUT_FILE, serde_json::to_string_pretty(&lints).unwrap()).unwrap(); // Outputting markdown - if Path::new(MARKDOWN_OUTPUT_FILE).exists() { - fs::remove_file(MARKDOWN_OUTPUT_FILE).unwrap(); - } - let mut file = OpenOptions::new() - .write(true) - .create(true) - .open(MARKDOWN_OUTPUT_FILE) - .unwrap(); + let mut file = File::create(MARKDOWN_OUTPUT_FILE).unwrap(); writeln!( file, "") { - // I know this is kinda wasteful, we just don't have regex on `clippy_lints` so... this is the best - // we can do AFAIK. - changelog = changelog[..position].to_string(); - } + let changelog = std::fs::read_to_string(CHANGELOG_PATH).unwrap(); + let mut changelog_file = File::create(CHANGELOG_PATH).unwrap(); + let position = changelog + .find("") + .unwrap(); writeln!( changelog_file, - "{changelog}\n{}\n", + "{}\n{}\n", + &changelog[..position], self.configs_to_markdown(ClippyConfiguration::to_markdown_link) ) .unwrap(); From 1d76eede99126dc6534a0d89dbf04121cd990042 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Fri, 15 Sep 2023 12:24:45 +0800 Subject: [PATCH 029/149] trigger [`transmute_null_to_fn`] on chain of casts --- .../src/transmute/transmute_null_to_fn.rs | 30 ++++++++++++------- tests/ui/transmute_null_to_fn.rs | 11 +++++++ tests/ui/transmute_null_to_fn.stderr | 26 +++++++++++++++- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index 4944381da24d5..b26365e34ab94 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -28,35 +28,43 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t return false; } - match arg.kind { + let casts_peeled = peel_casts(arg); + match casts_peeled.kind { // Catching: // transmute over constants that resolve to `null`. - ExprKind::Path(ref _qpath) if matches!(constant(cx, cx.typeck_results(), arg), Some(Constant::RawPtr(0))) => { + ExprKind::Path(ref _qpath) + if matches!( + constant(cx, cx.typeck_results(), casts_peeled), + Some(Constant::RawPtr(0)) + ) => + { lint_expr(cx, expr); true }, - - // Catching: - // `std::mem::transmute(0 as *const i32)` - ExprKind::Cast(inner_expr, _cast_ty) if is_integer_literal(inner_expr, 0) => { - lint_expr(cx, expr); - true - }, - // Catching: // `std::mem::transmute(std::ptr::null::())` ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => { lint_expr(cx, expr); true }, - _ => { // FIXME: // Also catch transmutations of variables which are known nulls. // To do this, MIR const propagation seems to be the better tool. // Whenever MIR const prop routines are more developed, this will // become available. As of this writing (25/03/19) it is not yet. + if is_integer_literal(casts_peeled, 0) { + lint_expr(cx, expr); + return true; + } false }, } } + +fn peel_casts<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + match &expr.kind { + ExprKind::Cast(inner_expr, _) => peel_casts(inner_expr), + _ => expr, + } +} diff --git a/tests/ui/transmute_null_to_fn.rs b/tests/ui/transmute_null_to_fn.rs index 7d780c803ffd5..b07851e864f68 100644 --- a/tests/ui/transmute_null_to_fn.rs +++ b/tests/ui/transmute_null_to_fn.rs @@ -25,6 +25,17 @@ fn transmute_const() { } } +fn issue_11485() { + unsafe { + let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); + //~^ ERROR: transmuting a known null pointer into a function pointer + let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); + //~^ ERROR: transmuting a known null pointer into a function pointer + let _: fn() = std::mem::transmute(ZPTR as *const u8); + //~^ ERROR: transmuting a known null pointer into a function pointer + } +} + fn main() { one_liners(); transmute_const(); diff --git a/tests/ui/transmute_null_to_fn.stderr b/tests/ui/transmute_null_to_fn.stderr index ab0ac0dd480bc..9073080cbf3c6 100644 --- a/tests/ui/transmute_null_to_fn.stderr +++ b/tests/ui/transmute_null_to_fn.stderr @@ -24,5 +24,29 @@ LL | let _: fn() = std::mem::transmute(ZPTR); | = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value -error: aborting due to 3 previous errors +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:30:23 + | +LL | let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:32:23 + | +LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:34:23 + | +LL | let _: fn() = std::mem::transmute(ZPTR as *const u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: aborting due to 6 previous errors From c9b212d5ffa5dcfe36ed41658c71dcbcbf949dd8 Mon Sep 17 00:00:00 2001 From: mojave2 Date: Fri, 15 Sep 2023 14:23:58 +0800 Subject: [PATCH 030/149] fix filter_map_bool_then with a bool reference --- clippy_lints/src/methods/filter_map_bool_then.rs | 5 +++-- tests/ui/filter_map_bool_then.fixed | 5 +++++ tests/ui/filter_map_bool_then.rs | 5 +++++ tests/ui/filter_map_bool_then.stderr | 8 +++++++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index fafc970977009..ac5bff6fee0aa 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -8,7 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::Binder; +use rustc_middle::ty::{self, Binder}; use rustc_span::{sym, Span}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { @@ -36,6 +36,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) && match_def_path(cx, def_id, &BOOL_THEN) && !is_from_proc_macro(cx, expr) + && let ref_bool = matches!(cx.typeck_results().expr_ty(recv).kind(), ty::Ref(..)) && let Some(param_snippet) = snippet_opt(cx, param.span) && let Some(filter) = snippet_opt(cx, recv.span) && let Some(map) = snippet_opt(cx, then_body.span) @@ -46,7 +47,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & call_span, "usage of `bool::then` in `filter_map`", "use `filter` then `map` instead", - format!("filter(|&{param_snippet}| {filter}).map(|{param_snippet}| {map})"), + format!("filter(|&{param_snippet}| {}{filter}).map(|{param_snippet}| {map})", if ref_bool { "*" } else { "" }), Applicability::MachineApplicable, ); } diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed index 6de870a928985..67b984ce0254d 100644 --- a/tests/ui/filter_map_bool_then.fixed +++ b/tests/ui/filter_map_bool_then.fixed @@ -55,3 +55,8 @@ fn main() { fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str> { iter.filter_map(|(_, s): (&str, _)| Some(s)).collect() } + +fn issue11503() { + let bools: &[bool] = &[true, false, false, true]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| *b).map(|(i, b)| i).collect(); +} diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs index 4108177e3a0e3..b716fd70fa062 100644 --- a/tests/ui/filter_map_bool_then.rs +++ b/tests/ui/filter_map_bool_then.rs @@ -55,3 +55,8 @@ fn main() { fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str> { iter.filter_map(|(_, s): (&str, _)| Some(s)).collect() } + +fn issue11503() { + let bools: &[bool] = &[true, false, false, true]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); +} diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr index 86ef6edf8eeda..1fee93afb36a0 100644 --- a/tests/ui/filter_map_bool_then.stderr +++ b/tests/ui/filter_map_bool_then.stderr @@ -37,5 +37,11 @@ error: usage of `bool::then` in `filter_map` LL | v.clone().iter().filter_map(|i| (i == &NonCopy).then(|| i)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i == &NonCopy)).map(|i| i)` -error: aborting due to 6 previous errors +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:61:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| *b).map(|(i, b)| i)` + +error: aborting due to 7 previous errors From c81888eab32e09318a9b95fdad22aea27f6933ea Mon Sep 17 00:00:00 2001 From: mojave2 Date: Fri, 15 Sep 2023 15:05:43 +0800 Subject: [PATCH 031/149] fix FP of let_unit_value on async fn args --- clippy_lints/src/unit_types/let_unit_value.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 704d7abd7e55a..e7915953d85b3 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; -use rustc_middle::lint::in_external_macro; +use rustc_middle::lint::{in_external_macro, is_from_async_await}; use rustc_middle::ty; use super::LET_UNIT_VALUE; @@ -16,6 +16,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { if let Some(init) = local.init && !local.pat.span.from_expansion() && !in_external_macro(cx.sess(), local.span) + && !is_from_async_await(local.span) && cx.typeck_results().pat_ty(local.pat).is_unit() { if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer)) From 981e96008b58d34e9c52b0aa3b318974c75b9d1d Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 11 Sep 2023 22:46:15 +0200 Subject: [PATCH 032/149] new lint: `path_ends_with_ext` --- CHANGELOG.md | 2 + book/src/lint_configuration.md | 10 ++++ clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 7 +++ clippy_lints/src/methods/mod.rs | 47 ++++++++++++++++ .../src/methods/path_ends_with_ext.rs | 53 +++++++++++++++++++ clippy_lints/src/utils/conf.rs | 5 ++ .../toml_unknown_key/conf_unknown_key.stderr | 2 + tests/ui/path_ends_with_ext.fixed | 36 +++++++++++++ tests/ui/path_ends_with_ext.rs | 36 +++++++++++++ tests/ui/path_ends_with_ext.stderr | 17 ++++++ 11 files changed, 216 insertions(+) create mode 100644 clippy_lints/src/methods/path_ends_with_ext.rs create mode 100644 tests/ui/path_ends_with_ext.fixed create mode 100644 tests/ui/path_ends_with_ext.rs create mode 100644 tests/ui/path_ends_with_ext.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aca13a6cf642..5e01e8d07bd79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5245,6 +5245,7 @@ Released 2018-09-13 [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite +[`path_ends_with_ext`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch [`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters @@ -5575,5 +5576,6 @@ Released 2018-09-13 [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings [`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments [`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates +[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles [`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 52c795e04fe95..3aeb6206fca93 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -751,6 +751,16 @@ Which crates to allow absolute paths from * [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths) +## `allowed-dotfiles` +Additional dotfiles (files or directories starting with a dot) to allow + +**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet`) + +--- +**Affected lints:** +* [`path_ends_with_ext`](https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext) + + ## `enforce-iter-loop-reborrow` #### Example ``` diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index ecf44e05920ae..4772f3d80371b 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -399,6 +399,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::OR_FUN_CALL_INFO, crate::methods::OR_THEN_UNWRAP_INFO, crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, + crate::methods::PATH_ENDS_WITH_EXT_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3f6bdb2b62cc8..8f603d5d98819 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -662,12 +662,19 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const; store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); + let allowed_dotfiles = conf + .allowed_dotfiles + .iter() + .cloned() + .chain(methods::DEFAULT_ALLOWED_DOTFILES.iter().copied().map(ToOwned::to_owned)) + .collect::>(); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, msrv(), allow_expect_in_tests, allow_unwrap_in_tests, + allowed_dotfiles.clone(), )) }); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 81223fa8d954f..f468236480684 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -74,6 +74,7 @@ mod option_map_unwrap_or; mod or_fun_call; mod or_then_unwrap; mod path_buf_push_overwrite; +mod path_ends_with_ext; mod range_zip_with_len; mod read_line_without_trim; mod readonly_write_lock; @@ -120,6 +121,8 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; use if_chain::if_chain; +pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; use rustc_hir_analysis::hir_ty_to_ty; @@ -3563,11 +3566,51 @@ declare_clippy_lint! { "calls to `.take()` or `.skip()` that are out of bounds" } +declare_clippy_lint! { + /// ### What it does + /// Looks for calls to `Path::ends_with` calls where the argument looks like a file extension. + /// + /// By default, Clippy has a short list of known filenames that start with a dot + /// but aren't necessarily file extensions (e.g. the `.git` folder), which are allowed by default. + /// The `allowed-dotfiles` configuration can be used to allow additional + /// file extensions that Clippy should not lint. + /// + /// ### Why is this bad? + /// This doesn't actually compare file extensions. Rather, `ends_with` compares the given argument + /// to the last **component** of the path and checks if it matches exactly. + /// + /// ### Known issues + /// File extensions are often at most three characters long, so this only lints in those cases + /// in an attempt to avoid false positives. + /// Any extension names longer than that are assumed to likely be real path components and are + /// therefore ignored. + /// + /// ### Example + /// ```rust + /// # use std::path::Path; + /// fn is_markdown(path: &Path) -> bool { + /// path.ends_with(".md") + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::path::Path; + /// fn is_markdown(path: &Path) -> bool { + /// path.extension().is_some_and(|ext| ext == "md") + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub PATH_ENDS_WITH_EXT, + suspicious, + "attempting to compare file extensions using `Path::ends_with`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, + allowed_dotfiles: FxHashSet, } impl Methods { @@ -3577,12 +3620,14 @@ impl Methods { msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, + allowed_dotfiles: FxHashSet, ) -> Self { Self { avoid_breaking_exported_api, msrv, allow_expect_in_tests, allow_unwrap_in_tests, + allowed_dotfiles, } } } @@ -3703,6 +3748,7 @@ impl_lint_pass!(Methods => [ FILTER_MAP_BOOL_THEN, READONLY_WRITE_LOCK, ITER_OUT_OF_BOUNDS, + PATH_ENDS_WITH_EXT, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3978,6 +4024,7 @@ impl Methods { if let ExprKind::MethodCall(.., span) = expr.kind { case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg); } + path_ends_with_ext::check(cx, recv, arg, expr, &self.msrv, &self.allowed_dotfiles); }, ("expect", [_]) => { match method_call(recv) { diff --git a/clippy_lints/src/methods/path_ends_with_ext.rs b/clippy_lints/src/methods/path_ends_with_ext.rs new file mode 100644 index 0000000000000..3347c8c162015 --- /dev/null +++ b/clippy_lints/src/methods/path_ends_with_ext.rs @@ -0,0 +1,53 @@ +use super::PATH_ENDS_WITH_EXT; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs; +use clippy_utils::msrvs::Msrv; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_ast::{LitKind, StrStyle}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::sym; +use std::fmt::Write; + +pub const DEFAULT_ALLOWED_DOTFILES: &[&str] = &[ + "git", "svn", "gem", "npm", "vim", "env", "rnd", "ssh", "vnc", "smb", "nvm", "bin", +]; + +pub(super) fn check( + cx: &LateContext<'_>, + recv: &Expr<'_>, + path: &Expr<'_>, + expr: &Expr<'_>, + msrv: &Msrv, + allowed_dotfiles: &FxHashSet, +) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::Path) + && !path.span.from_expansion() + && let ExprKind::Lit(lit) = path.kind + && let LitKind::Str(path, StrStyle::Cooked) = lit.node + && let Some(path) = path.as_str().strip_prefix('.') + && (1..=3).contains(&path.len()) + && !allowed_dotfiles.contains(path) + && path.chars().all(char::is_alphanumeric) + { + let mut sugg = snippet(cx, recv.span, "..").into_owned(); + if msrv.meets(msrvs::OPTION_IS_SOME_AND) { + let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#); + } else { + let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#); + }; + + span_lint_and_sugg( + cx, + PATH_ENDS_WITH_EXT, + expr.span, + "this looks like a failed attempt at checking for the file extension", + "try", + sugg, + Applicability::MaybeIncorrect + ); + } +} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 2688947552253..e1280ef38ef71 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -561,6 +561,11 @@ define_Conf! { /// Which crates to allow absolute paths from (absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet = rustc_data_structures::fx::FxHashSet::default()), + /// Lint: PATH_ENDS_WITH_EXT. + /// + /// Additional dotfiles (files or directories starting with a dot) to allow + (allowed_dotfiles: rustc_data_structures::fx::FxHashSet = + rustc_data_structures::fx::FxHashSet::default()), /// Lint: EXPLICIT_ITER_LOOP /// /// Whether to recommend using implicit into iter for reborrowed values. 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 b97bb144468b0..4bed5c149f517 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -10,6 +10,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-print-in-tests allow-private-module-inception allow-unwrap-in-tests + allowed-dotfiles allowed-idents-below-min-chars allowed-scripts arithmetic-side-effects-allowed @@ -82,6 +83,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-print-in-tests allow-private-module-inception allow-unwrap-in-tests + allowed-dotfiles allowed-idents-below-min-chars allowed-scripts arithmetic-side-effects-allowed diff --git a/tests/ui/path_ends_with_ext.fixed b/tests/ui/path_ends_with_ext.fixed new file mode 100644 index 0000000000000..49767e242cee5 --- /dev/null +++ b/tests/ui/path_ends_with_ext.fixed @@ -0,0 +1,36 @@ +#![warn(clippy::path_ends_with_ext)] +use std::path::Path; + +macro_rules! arg { + () => { + ".md" + }; +} + +fn test(path: &Path) { + path.extension().is_some_and(|ext| ext == "md"); + //~^ ERROR: this looks like a failed attempt at checking for the file extension + + // some "extensions" are allowed by default + path.ends_with(".git"); + + // most legitimate "dotfiles" are longer than 3 chars, so we allow them as well + path.ends_with(".bashrc"); + + // argument from expn shouldn't trigger + path.ends_with(arg!()); + + path.ends_with(".."); + path.ends_with("./a"); + path.ends_with("."); + path.ends_with(""); +} + +// is_some_and was stabilized in 1.70, so suggest map_or(false, ..) if under that +#[clippy::msrv = "1.69"] +fn under_msv(path: &Path) -> bool { + path.extension().map_or(false, |ext| ext == "md") + //~^ ERROR: this looks like a failed attempt at checking for the file extension +} + +fn main() {} diff --git a/tests/ui/path_ends_with_ext.rs b/tests/ui/path_ends_with_ext.rs new file mode 100644 index 0000000000000..2dfd046218afa --- /dev/null +++ b/tests/ui/path_ends_with_ext.rs @@ -0,0 +1,36 @@ +#![warn(clippy::path_ends_with_ext)] +use std::path::Path; + +macro_rules! arg { + () => { + ".md" + }; +} + +fn test(path: &Path) { + path.ends_with(".md"); + //~^ ERROR: this looks like a failed attempt at checking for the file extension + + // some "extensions" are allowed by default + path.ends_with(".git"); + + // most legitimate "dotfiles" are longer than 3 chars, so we allow them as well + path.ends_with(".bashrc"); + + // argument from expn shouldn't trigger + path.ends_with(arg!()); + + path.ends_with(".."); + path.ends_with("./a"); + path.ends_with("."); + path.ends_with(""); +} + +// is_some_and was stabilized in 1.70, so suggest map_or(false, ..) if under that +#[clippy::msrv = "1.69"] +fn under_msv(path: &Path) -> bool { + path.ends_with(".md") + //~^ ERROR: this looks like a failed attempt at checking for the file extension +} + +fn main() {} diff --git a/tests/ui/path_ends_with_ext.stderr b/tests/ui/path_ends_with_ext.stderr new file mode 100644 index 0000000000000..a73ab4d08e9c5 --- /dev/null +++ b/tests/ui/path_ends_with_ext.stderr @@ -0,0 +1,17 @@ +error: this looks like a failed attempt at checking for the file extension + --> $DIR/path_ends_with_ext.rs:11:5 + | +LL | path.ends_with(".md"); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `path.extension().is_some_and(|ext| ext == "md")` + | + = note: `-D clippy::path-ends-with-ext` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::path_ends_with_ext)]` + +error: this looks like a failed attempt at checking for the file extension + --> $DIR/path_ends_with_ext.rs:32:5 + | +LL | path.ends_with(".md") + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `path.extension().map_or(false, |ext| ext == "md")` + +error: aborting due to 2 previous errors + From 7426c83d8f5d1688763642503b59d0f0f653a3f5 Mon Sep 17 00:00:00 2001 From: mojave2 Date: Sat, 16 Sep 2023 00:05:57 +0800 Subject: [PATCH 033/149] add a test case --- tests/ui/let_unit.fixed | 2 ++ tests/ui/let_unit.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 57374bd5fcdad..f98ce9d50a994 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -177,3 +177,5 @@ fn attributes() { async fn issue10433() { let _pending: () = std::future::pending().await; } + +pub async fn issue11502(a: ()) {} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index 09077c60d5019..6d942ca8908c4 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -177,3 +177,5 @@ fn attributes() { async fn issue10433() { let _pending: () = std::future::pending().await; } + +pub async fn issue11502(a: ()) {} From fed036a57c96d91ac632f0bd40a27ef78b5b96c6 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 15 Sep 2023 07:06:50 -0400 Subject: [PATCH 034/149] Fix cycle detection in `needless_borrow` --- clippy_lints/src/dereference.rs | 3 --- clippy_utils/src/mir/mod.rs | 40 ++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 8090f821d1ff2..d0a898adff44d 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -12,7 +12,6 @@ use hir::def::DefKind; use hir::MatchSource; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -1172,8 +1171,6 @@ fn referent_used_exactly_once<'tcx>( && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind && !place.is_indirect_first_projection() - // Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710) - && TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none() { let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); if possible_borrowers diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 131f3c0aa3946..f04467dc19d37 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -1,7 +1,8 @@ use rustc_hir::{Expr, HirId}; +use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ - traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, + traversal, BasicBlock, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, }; use rustc_middle::ty::TyCtxt; @@ -79,8 +80,32 @@ impl<'a, 'tcx> Visitor<'tcx> for V<'a> { } } +/// Checks if the block is part of a cycle +pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { + let mut seen = BitSet::new_empty(body.basic_blocks.len()); + let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2); + + seen.insert(block); + let mut next = block; + loop { + for succ in body.basic_blocks[next].terminator().successors() { + if seen.insert(succ) { + to_visit.push(succ); + } else if succ == block { + return true; + } + } + + if let Some(x) = to_visit.pop() { + next = x; + } else { + return false; + } + } +} + /// Convenience wrapper around `visit_local_usage`. -pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option { +pub fn used_exactly_once(mir: &Body<'_>, local: rustc_middle::mir::Local) -> Option { visit_local_usage( &[local], mir, @@ -91,11 +116,14 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle: ) .map(|mut vec| { let LocalUsage { local_use_locs, .. } = vec.remove(0); - local_use_locs + let mut locations = local_use_locs .into_iter() - .filter(|location| !is_local_assignment(mir, local, *location)) - .count() - == 1 + .filter(|&location| !is_local_assignment(mir, local, location)); + if let Some(location) = locations.next() { + locations.next().is_none() && !block_in_cycle(mir, location.block) + } else { + false + } }) } From 860e800fa08734ff9d15a67115f8220e5fa2202e Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 16 Sep 2023 14:14:51 +0200 Subject: [PATCH 035/149] [`filter_map_bool_then`]: peel as many refs as needed --- clippy_lints/src/methods/filter_map_bool_then.rs | 15 +++++++++++---- tests/ui/filter_map_bool_then.fixed | 8 ++++++++ tests/ui/filter_map_bool_then.rs | 8 ++++++++ tests/ui/filter_map_bool_then.stderr | 14 +++++++++++++- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index ac5bff6fee0aa..dd4aa2766630e 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -2,13 +2,13 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::BOOL_THEN; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::is_copy; +use clippy_utils::ty::{is_copy, peel_mid_ty_refs_is_mutable}; use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Binder}; +use rustc_middle::ty::Binder; use rustc_span::{sym, Span}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { @@ -36,7 +36,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) && match_def_path(cx, def_id, &BOOL_THEN) && !is_from_proc_macro(cx, expr) - && let ref_bool = matches!(cx.typeck_results().expr_ty(recv).kind(), ty::Ref(..)) + // Peel all refs (e.g. `&&&&mut &&&bool` -> `bool`) and get its count so we can suggest the exact + // amount of derefs to get to the bool in the filter. + // `peel_mid_ty_refs` alone doesn't handle mutable reference, so we use `_is_mutable` + // instead which counts them too and just ignore the resulting mutability + && let (_, needed_derefs, _) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) && let Some(param_snippet) = snippet_opt(cx, param.span) && let Some(filter) = snippet_opt(cx, recv.span) && let Some(map) = snippet_opt(cx, then_body.span) @@ -47,7 +51,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & call_span, "usage of `bool::then` in `filter_map`", "use `filter` then `map` instead", - format!("filter(|&{param_snippet}| {}{filter}).map(|{param_snippet}| {map})", if ref_bool { "*" } else { "" }), + format!( + "filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})", + derefs="*".repeat(needed_derefs) + ), Applicability::MachineApplicable, ); } diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed index 67b984ce0254d..d1c946585f577 100644 --- a/tests/ui/filter_map_bool_then.fixed +++ b/tests/ui/filter_map_bool_then.fixed @@ -59,4 +59,12 @@ fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str fn issue11503() { let bools: &[bool] = &[true, false, false, true]; let _: Vec = bools.iter().enumerate().filter(|&(i, b)| *b).map(|(i, b)| i).collect(); + + // Need to insert multiple derefs if there is more than one layer of references + let bools: &[&&bool] = &[&&true, &&false, &&false, &&true]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| ***b).map(|(i, b)| i).collect(); + + // Should also suggest derefs when going through a mutable reference + let bools: &[&mut bool] = &[&mut true]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| **b).map(|(i, b)| i).collect(); } diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs index b716fd70fa062..2d0ed282013d0 100644 --- a/tests/ui/filter_map_bool_then.rs +++ b/tests/ui/filter_map_bool_then.rs @@ -59,4 +59,12 @@ fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str fn issue11503() { let bools: &[bool] = &[true, false, false, true]; let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + + // Need to insert multiple derefs if there is more than one layer of references + let bools: &[&&bool] = &[&&true, &&false, &&false, &&true]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + + // Should also suggest derefs when going through a mutable reference + let bools: &[&mut bool] = &[&mut true]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); } diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr index 1fee93afb36a0..174786ff32ef6 100644 --- a/tests/ui/filter_map_bool_then.stderr +++ b/tests/ui/filter_map_bool_then.stderr @@ -43,5 +43,17 @@ error: usage of `bool::then` in `filter_map` LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| *b).map(|(i, b)| i)` -error: aborting due to 7 previous errors +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:65:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ***b).map(|(i, b)| i)` + +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:69:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| **b).map(|(i, b)| i)` + +error: aborting due to 9 previous errors From 2ec6f3b1ed22c3fa0b20eda5cf335c54f81b4462 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 16 Sep 2023 15:13:44 +0200 Subject: [PATCH 036/149] also count derefs through custom `Deref` impls --- clippy_lints/src/methods/filter_map_bool_then.rs | 13 +++++++------ tests/ui/filter_map_bool_then.fixed | 11 +++++++++++ tests/ui/filter_map_bool_then.rs | 11 +++++++++++ tests/ui/filter_map_bool_then.stderr | 8 +++++++- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index dd4aa2766630e..33657254965c1 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -2,12 +2,13 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::BOOL_THEN; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{is_copy, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::is_copy; use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::Binder; use rustc_span::{sym, Span}; @@ -36,11 +37,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) && match_def_path(cx, def_id, &BOOL_THEN) && !is_from_proc_macro(cx, expr) - // Peel all refs (e.g. `&&&&mut &&&bool` -> `bool`) and get its count so we can suggest the exact - // amount of derefs to get to the bool in the filter. - // `peel_mid_ty_refs` alone doesn't handle mutable reference, so we use `_is_mutable` - // instead which counts them too and just ignore the resulting mutability - && let (_, needed_derefs, _) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) + // Count the number of derefs needed to get to the bool because we need those in the suggestion + && let needed_derefs = cx.typeck_results().expr_adjustments(recv) + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count() && let Some(param_snippet) = snippet_opt(cx, param.span) && let Some(filter) = snippet_opt(cx, recv.span) && let Some(map) = snippet_opt(cx, then_body.span) diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed index d1c946585f577..6a1b81fdbcbe8 100644 --- a/tests/ui/filter_map_bool_then.fixed +++ b/tests/ui/filter_map_bool_then.fixed @@ -67,4 +67,15 @@ fn issue11503() { // Should also suggest derefs when going through a mutable reference let bools: &[&mut bool] = &[&mut true]; let _: Vec = bools.iter().enumerate().filter(|&(i, b)| **b).map(|(i, b)| i).collect(); + + // Should also suggest derefs when going through a custom deref + struct DerefToBool; + impl std::ops::Deref for DerefToBool { + type Target = bool; + fn deref(&self) -> &Self::Target { + &true + } + } + let bools: &[&&DerefToBool] = &[&&DerefToBool]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| ****b).map(|(i, b)| i).collect(); } diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs index 2d0ed282013d0..a41e88f8805de 100644 --- a/tests/ui/filter_map_bool_then.rs +++ b/tests/ui/filter_map_bool_then.rs @@ -67,4 +67,15 @@ fn issue11503() { // Should also suggest derefs when going through a mutable reference let bools: &[&mut bool] = &[&mut true]; let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + + // Should also suggest derefs when going through a custom deref + struct DerefToBool; + impl std::ops::Deref for DerefToBool { + type Target = bool; + fn deref(&self) -> &Self::Target { + &true + } + } + let bools: &[&&DerefToBool] = &[&&DerefToBool]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); } diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr index 174786ff32ef6..fab6987913a10 100644 --- a/tests/ui/filter_map_bool_then.stderr +++ b/tests/ui/filter_map_bool_then.stderr @@ -55,5 +55,11 @@ error: usage of `bool::then` in `filter_map` LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| **b).map(|(i, b)| i)` -error: aborting due to 9 previous errors +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:80:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ****b).map(|(i, b)| i)` + +error: aborting due to 10 previous errors From 67f0ba4af8be42d361adc8d047121fe6c820c0d4 Mon Sep 17 00:00:00 2001 From: Chen Chen <0109chenchen@gmail.com> Date: Sat, 16 Sep 2023 22:12:40 +0800 Subject: [PATCH 037/149] Update clippy_lints/src/matches/redundant_guards.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix typo Co-authored-by: Alejandra González --- clippy_lints/src/matches/redundant_guards.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index d51d7edc8e353..d470c05f35110 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -123,7 +123,7 @@ fn get_pat_binding<'tcx>( if matches!(bind_annot.0, rustc_ast::ByRef::Yes) { let _ = byref_ident.insert(ident); } - // the second call of `replce()` returns a `Some(span)`, meaning a multi-binding pattern + // the second call of `replace()` returns a `Some(span)`, meaning a multi-binding pattern if span.replace(pat.span).is_some() { multiple_bindings = true; return false; From af2a8478ba3228ede92ff799b17ac2408fc6347f Mon Sep 17 00:00:00 2001 From: mojave2 Date: Sat, 16 Sep 2023 23:17:47 +0800 Subject: [PATCH 038/149] fix cast_lossless with macro call --- clippy_lints/src/casts/cast_lossless.rs | 9 ++++++++- tests/ui/cast_lossless_integer.fixed | 11 +++++++++++ tests/ui/cast_lossless_integer.rs | 11 +++++++++++ tests/ui/cast_lossless_integer.stderr | 14 +++++++++++++- 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index cf07e050ccce9..2f3ffea669344 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -25,7 +25,14 @@ pub(super) fn check( // The suggestion is to use a function call, so if the original expression // has parens on the outside, they are no longer needed. let mut applicability = Applicability::MachineApplicable; - let opt = snippet_opt(cx, cast_op.span); + let opt = snippet_opt( + cx, + if cast_op.span.from_expansion() { + cast_op.span.source_callsite() + } else { + cast_op.span + }, + ); let sugg = opt.as_ref().map_or_else( || { applicability = Applicability::HasPlaceholders; diff --git a/tests/ui/cast_lossless_integer.fixed b/tests/ui/cast_lossless_integer.fixed index 6547107f50060..5e7e545e764a5 100644 --- a/tests/ui/cast_lossless_integer.fixed +++ b/tests/ui/cast_lossless_integer.fixed @@ -49,3 +49,14 @@ mod cast_lossless_in_impl { enum Test { A = u32::MAX as i64 + 1, } + +fn issue11458() { + macro_rules! sign_cast { + ($var: ident, $src: ty, $dest: ty) => { + <$dest>::from_ne_bytes(($var as $src).to_ne_bytes()) + }; + } + let x = 10_u128; + let _ = i32::from(sign_cast!(x, u8, i8)); + let _ = i32::from(sign_cast!(x, u8, i8) + 1); +} diff --git a/tests/ui/cast_lossless_integer.rs b/tests/ui/cast_lossless_integer.rs index 79af9a83ca286..0d69ddbd586a5 100644 --- a/tests/ui/cast_lossless_integer.rs +++ b/tests/ui/cast_lossless_integer.rs @@ -49,3 +49,14 @@ mod cast_lossless_in_impl { enum Test { A = u32::MAX as i64 + 1, } + +fn issue11458() { + macro_rules! sign_cast { + ($var: ident, $src: ty, $dest: ty) => { + <$dest>::from_ne_bytes(($var as $src).to_ne_bytes()) + }; + } + let x = 10_u128; + let _ = sign_cast!(x, u8, i8) as i32; + let _ = (sign_cast!(x, u8, i8) + 1) as i32; +} diff --git a/tests/ui/cast_lossless_integer.stderr b/tests/ui/cast_lossless_integer.stderr index da75cb195ebae..f9f111a7c20f1 100644 --- a/tests/ui/cast_lossless_integer.stderr +++ b/tests/ui/cast_lossless_integer.stderr @@ -115,5 +115,17 @@ error: casting `u8` to `u16` may become silently lossy if you later change the t LL | let _ = (1u8 + 1u8) as u16; | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)` -error: aborting due to 19 previous errors +error: casting `i8` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:60:13 + | +LL | let _ = sign_cast!(x, u8, i8) as i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8))` + +error: casting `i8` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:61:13 + | +LL | let _ = (sign_cast!(x, u8, i8) + 1) as i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8) + 1)` + +error: aborting due to 21 previous errors From 3cad6237166dc71e2b75a9e60d5058f2876e1959 Mon Sep 17 00:00:00 2001 From: Chen Chen <0109chenchen@gmail.com> Date: Sun, 17 Sep 2023 02:42:12 +0800 Subject: [PATCH 039/149] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20cast=5Flossless.rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Timo <30553356+y21@users.noreply.github.com> --- clippy_lints/src/casts/cast_lossless.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 2f3ffea669344..c586b572be9a1 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -25,14 +25,7 @@ pub(super) fn check( // The suggestion is to use a function call, so if the original expression // has parens on the outside, they are no longer needed. let mut applicability = Applicability::MachineApplicable; - let opt = snippet_opt( - cx, - if cast_op.span.from_expansion() { - cast_op.span.source_callsite() - } else { - cast_op.span - }, - ); + let opt = snippet_opt(cx, cast_op.span.source_callsite()); let sugg = opt.as_ref().map_or_else( || { applicability = Applicability::HasPlaceholders; From 5b790ff9e0effa9f742ce8a7b896a35c767fb131 Mon Sep 17 00:00:00 2001 From: mojave2 Date: Sun, 17 Sep 2023 12:54:00 +0800 Subject: [PATCH 040/149] fix FP with needless_raw_string_hashes --- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/raw_strings.rs | 5 +++-- tests/ui/needless_raw_string_hashes.fixed | 3 +++ tests/ui/needless_raw_string_hashes.rs | 3 +++ tests/ui/needless_raw_string_hashes.stderr | 26 +++++++++++++++++++++- 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index a88f2b51c827e..0546807bac4fb 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -616,7 +616,7 @@ fn check_should_panic_reason(cx: &LateContext<'_>, attr: &Attribute) { attr.span, "#[should_panic] attribute without a reason", "consider specifying the expected panic", - r#"#[should_panic(expected = /* panic message */)]"#.into(), + "#[should_panic(expected = /* panic message */)]".into(), Applicability::HasPlaceholders, ); } diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index a4fe501f79cdc..3db9f7541e588 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -96,8 +96,9 @@ impl EarlyLintPass for RawStrings { ); }, ); - - return; + if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) { + return; + } } let req = { diff --git a/tests/ui/needless_raw_string_hashes.fixed b/tests/ui/needless_raw_string_hashes.fixed index e980adeeff4c1..c99c2f46532a3 100644 --- a/tests/ui/needless_raw_string_hashes.fixed +++ b/tests/ui/needless_raw_string_hashes.fixed @@ -21,4 +21,7 @@ fn main() { multiline string "; + + r"rust"; + r"hello world"; } diff --git a/tests/ui/needless_raw_string_hashes.rs b/tests/ui/needless_raw_string_hashes.rs index 6113c5f25ae64..dcc2af69f4e9c 100644 --- a/tests/ui/needless_raw_string_hashes.rs +++ b/tests/ui/needless_raw_string_hashes.rs @@ -21,4 +21,7 @@ fn main() { multiline string "#; + + r###"rust"###; + r#"hello world"#; } diff --git a/tests/ui/needless_raw_string_hashes.stderr b/tests/ui/needless_raw_string_hashes.stderr index 5a8e3d04543af..4399c6555c2c0 100644 --- a/tests/ui/needless_raw_string_hashes.stderr +++ b/tests/ui/needless_raw_string_hashes.stderr @@ -163,5 +163,29 @@ LL | string LL ~ "; | -error: aborting due to 13 previous errors +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:25:5 + | +LL | r###"rust"###; + | ^^^^^^^^^^^^^ + | +help: remove all the hashes around the literal + | +LL - r###"rust"###; +LL + r"rust"; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:26:5 + | +LL | r#"hello world"#; + | ^^^^^^^^^^^^^^^^ + | +help: remove all the hashes around the literal + | +LL - r#"hello world"#; +LL + r"hello world"; + | + +error: aborting due to 15 previous errors From 3665a4102bce2cd6b09a7aea1b405cc640e076d8 Mon Sep 17 00:00:00 2001 From: mojave2 Date: Sun, 17 Sep 2023 19:44:25 +0800 Subject: [PATCH 041/149] fix ICE by `u64::try_from()` --- clippy_lints/src/casts/cast_possible_truncation.rs | 2 +- tests/ui/cast.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 84b99ad5c243d..f99a51e2b88c5 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -44,7 +44,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b .unwrap_or(u64::max_value()) .min(apply_reductions(cx, nbits, left, signed)), BinOpKind::Shr => apply_reductions(cx, nbits, left, signed) - .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))), + .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).unwrap_or_default())), _ => nbits, }, ExprKind::MethodCall(method, left, [right], _) => { diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index d0a092093f3e0..1ca18170f8a0e 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -361,3 +361,7 @@ fn avoid_subtract_overflow(q: u32) { //~^ ERROR: casting `u32` to `u8` may truncate the value c as usize; } + +fn issue11426() { + (&42u8 >> 0xa9008fb6c9d81e42_0e25730562a601c8_u128) as usize; +} From 558ae4c6a8a5a81c5b0cb3c7828e622201ba33ee Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 17 Sep 2023 15:34:32 +0200 Subject: [PATCH 042/149] [`redundant_guards`]: lint if the pattern is on the LHS --- clippy_lints/src/matches/redundant_guards.rs | 9 +++- tests/ui/redundant_guards.fixed | 9 ++++ tests/ui/redundant_guards.rs | 9 ++++ tests/ui/redundant_guards.stderr | 56 ++++++++++++++++---- 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index d470c05f35110..0efeeacc9d97c 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -70,10 +70,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { ); } // `Some(x) if x == Some(2)` + // `Some(x) if Some(2) == x` else if let Guard::If(if_expr) = guard && let ExprKind::Binary(bin_op, local, pat) = if_expr.kind && matches!(bin_op.node, BinOpKind::Eq) - && expr_can_be_pat(cx, pat) // Ensure they have the same type. If they don't, we'd need deref coercion which isn't // possible (currently) in a pattern. In some cases, you can use something like // `as_deref` or similar but in general, we shouldn't lint this as it'd create an @@ -81,7 +81,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { // // This isn't necessary in the other two checks, as they must be a pattern already. && cx.typeck_results().expr_ty(local) == cx.typeck_results().expr_ty(pat) - && let Some(binding) = get_pat_binding(cx, local, outer_arm) + // Since we want to lint on both `x == Some(2)` and `Some(2) == x`, we might have to "swap" + // `local` and `pat`, depending on which side they are. + && let Some((binding, pat)) = get_pat_binding(cx, local, outer_arm) + .map(|binding| (binding, pat)) + .or_else(|| get_pat_binding(cx, pat, outer_arm).map(|binding| (binding, local))) + && expr_can_be_pat(cx, pat) { let pat_span = match (pat.kind, binding.byref_ident) { (ExprKind::AddrOf(BorrowKind::Ref, _, expr), Some(_)) => expr.span, diff --git a/tests/ui/redundant_guards.fixed b/tests/ui/redundant_guards.fixed index 20fcc1254a66d..f23116a7e1c5c 100644 --- a/tests/ui/redundant_guards.fixed +++ b/tests/ui/redundant_guards.fixed @@ -43,6 +43,7 @@ fn main() { }, Some(Some(1)) => .., Some(Some(2)) => .., + Some(Some(2)) => .., // Don't lint, since x is used in the body Some(x) if let Some(1) = x => { x; @@ -56,11 +57,13 @@ fn main() { Some(x) if matches!(y, 1 if true) => .., Some(x) if let 1 = y => .., Some(x) if y == 2 => .., + Some(x) if 2 == y => .., _ => todo!(), }; let a = A(1); match a { _ if a.0 == 1 => {}, + _ if 1 == a.0 => {}, _ => todo!(), } let b = B { e: Some(A(0)) }; @@ -119,6 +122,7 @@ fn h(v: Option) { fn f(s: Option) { match s { Some(x) if x == "a" => {}, + Some(x) if "a" == x => {}, _ => {}, } } @@ -140,6 +144,7 @@ static CONST_S: S = S { a: 1 }; fn g(opt_s: Option) { match opt_s { Some(x) if x == CONST_S => {}, + Some(x) if CONST_S == x => {}, _ => {}, } } @@ -157,6 +162,7 @@ mod issue11465 { fn issue11465() { let c = Some(1); match c { + Some(1) => {}, Some(1) => {}, Some(2) => {}, Some(3) => {}, @@ -166,6 +172,7 @@ mod issue11465 { let enum_a = A::Foo([98, 97, 114]); match enum_a { A::Foo(ref arr) if arr == b"foo" => {}, + A::Foo(ref arr) if b"foo" == arr => {}, A::Foo(ref arr) if let b"bar" = arr => {}, A::Foo(ref arr) if matches!(arr, b"baz") => {}, _ => {}, @@ -177,6 +184,8 @@ mod issue11465 { }; match struct_b { B { ref b, .. } if b == "bar" => {}, + B { ref b, .. } if "bar" == b => {}, + B { c: 1, .. } => {}, B { c: 1, .. } => {}, B { c: 1, .. } => {}, B { c: 1, .. } => {}, diff --git a/tests/ui/redundant_guards.rs b/tests/ui/redundant_guards.rs index e63cd29e8afe9..c0206b4cec75f 100644 --- a/tests/ui/redundant_guards.rs +++ b/tests/ui/redundant_guards.rs @@ -43,6 +43,7 @@ fn main() { }, Some(x) if let Some(1) = x => .., Some(x) if x == Some(2) => .., + Some(x) if Some(2) == x => .., // Don't lint, since x is used in the body Some(x) if let Some(1) = x => { x; @@ -56,11 +57,13 @@ fn main() { Some(x) if matches!(y, 1 if true) => .., Some(x) if let 1 = y => .., Some(x) if y == 2 => .., + Some(x) if 2 == y => .., _ => todo!(), }; let a = A(1); match a { _ if a.0 == 1 => {}, + _ if 1 == a.0 => {}, _ => todo!(), } let b = B { e: Some(A(0)) }; @@ -119,6 +122,7 @@ fn h(v: Option) { fn f(s: Option) { match s { Some(x) if x == "a" => {}, + Some(x) if "a" == x => {}, _ => {}, } } @@ -140,6 +144,7 @@ static CONST_S: S = S { a: 1 }; fn g(opt_s: Option) { match opt_s { Some(x) if x == CONST_S => {}, + Some(x) if CONST_S == x => {}, _ => {}, } } @@ -158,6 +163,7 @@ mod issue11465 { let c = Some(1); match c { Some(ref x) if x == &1 => {}, + Some(ref x) if &1 == x => {}, Some(ref x) if let &2 = x => {}, Some(ref x) if matches!(x, &3) => {}, _ => {}, @@ -166,6 +172,7 @@ mod issue11465 { let enum_a = A::Foo([98, 97, 114]); match enum_a { A::Foo(ref arr) if arr == b"foo" => {}, + A::Foo(ref arr) if b"foo" == arr => {}, A::Foo(ref arr) if let b"bar" = arr => {}, A::Foo(ref arr) if matches!(arr, b"baz") => {}, _ => {}, @@ -177,7 +184,9 @@ mod issue11465 { }; match struct_b { B { ref b, .. } if b == "bar" => {}, + B { ref b, .. } if "bar" == b => {}, B { ref c, .. } if c == &1 => {}, + B { ref c, .. } if &1 == c => {}, B { ref c, .. } if let &1 = c => {}, B { ref c, .. } if matches!(c, &1) => {}, _ => {}, diff --git a/tests/ui/redundant_guards.stderr b/tests/ui/redundant_guards.stderr index f208e556f2e90..b8d7834e358ca 100644 --- a/tests/ui/redundant_guards.stderr +++ b/tests/ui/redundant_guards.stderr @@ -60,7 +60,19 @@ LL + Some(Some(2)) => .., | error: redundant guard - --> $DIR/redundant_guards.rs:68:20 + --> $DIR/redundant_guards.rs:46:20 + | +LL | Some(x) if Some(2) == x => .., + | ^^^^^^^^^^^^ + | +help: try + | +LL - Some(x) if Some(2) == x => .., +LL + Some(Some(2)) => .., + | + +error: redundant guard + --> $DIR/redundant_guards.rs:71:20 | LL | B { e } if matches!(e, Some(A(2))) => .., | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +84,7 @@ LL + B { e: Some(A(2)) } => .., | error: redundant guard - --> $DIR/redundant_guards.rs:105:20 + --> $DIR/redundant_guards.rs:108:20 | LL | E::A(y) if y == "not from an or pattern" => {}, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +96,7 @@ LL + E::A("not from an or pattern") => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:112:14 + --> $DIR/redundant_guards.rs:115:14 | LL | x if matches!(x, Some(0)) => .., | ^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +108,7 @@ LL + Some(0) => .., | error: redundant guard - --> $DIR/redundant_guards.rs:160:28 + --> $DIR/redundant_guards.rs:165:28 | LL | Some(ref x) if x == &1 => {}, | ^^^^^^^ @@ -108,7 +120,19 @@ LL + Some(1) => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:161:28 + --> $DIR/redundant_guards.rs:166:28 + | +LL | Some(ref x) if &1 == x => {}, + | ^^^^^^^ + | +help: try + | +LL - Some(ref x) if &1 == x => {}, +LL + Some(1) => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:167:28 | LL | Some(ref x) if let &2 = x => {}, | ^^^^^^^^^^ @@ -120,7 +144,7 @@ LL + Some(2) => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:162:28 + --> $DIR/redundant_guards.rs:168:28 | LL | Some(ref x) if matches!(x, &3) => {}, | ^^^^^^^^^^^^^^^ @@ -132,7 +156,7 @@ LL + Some(3) => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:180:32 + --> $DIR/redundant_guards.rs:188:32 | LL | B { ref c, .. } if c == &1 => {}, | ^^^^^^^ @@ -144,7 +168,19 @@ LL + B { c: 1, .. } => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:181:32 + --> $DIR/redundant_guards.rs:189:32 + | +LL | B { ref c, .. } if &1 == c => {}, + | ^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if &1 == c => {}, +LL + B { c: 1, .. } => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:190:32 | LL | B { ref c, .. } if let &1 = c => {}, | ^^^^^^^^^^ @@ -156,7 +192,7 @@ LL + B { c: 1, .. } => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:182:32 + --> $DIR/redundant_guards.rs:191:32 | LL | B { ref c, .. } if matches!(c, &1) => {}, | ^^^^^^^^^^^^^^^ @@ -167,5 +203,5 @@ LL - B { ref c, .. } if matches!(c, &1) => {}, LL + B { c: 1, .. } => {}, | -error: aborting due to 14 previous errors +error: aborting due to 17 previous errors From 79247d95f7ec00019cc26db20a07d4f1c1f335aa Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 15 Sep 2023 07:07:34 -0400 Subject: [PATCH 043/149] Split part of `needless_borrow` into `needless_borrows_for_generic_args` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/dereference.rs | 371 +--------------- clippy_lints/src/lib.rs | 8 +- .../src/needless_borrows_for_generic_args.rs | 410 ++++++++++++++++++ tests/ui/needless_borrow.fixed | 290 ------------- tests/ui/needless_borrow.rs | 290 ------------- tests/ui/needless_borrow.stderr | 90 +--- .../needless_borrows_for_generic_args.fixed | 287 ++++++++++++ tests/ui/needless_borrows_for_generic_args.rs | 287 ++++++++++++ .../needless_borrows_for_generic_args.stderr | 77 ++++ tests/ui/regex.rs | 3 +- tests/ui/regex.stderr | 48 +- tests/ui/unnecessary_to_owned.fixed | 2 +- tests/ui/unnecessary_to_owned.rs | 2 +- 15 files changed, 1106 insertions(+), 1061 deletions(-) create mode 100644 clippy_lints/src/needless_borrows_for_generic_args.rs create mode 100644 tests/ui/needless_borrows_for_generic_args.fixed create mode 100644 tests/ui/needless_borrows_for_generic_args.rs create mode 100644 tests/ui/needless_borrows_for_generic_args.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aca13a6cf642..3717b15ac5c2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5171,6 +5171,7 @@ Released 2018-09-13 [`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference +[`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index ecf44e05920ae..c4d5bee389ed8 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -479,6 +479,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::needless_bool::NEEDLESS_BOOL_INFO, crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO, crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, + crate::needless_borrows_for_generic_args::NEEDLESS_BORROWS_FOR_GENERIC_ARGS_INFO, crate::needless_continue::NEEDLESS_CONTINUE_INFO, crate::needless_else::NEEDLESS_ELSE_INFO, crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index d0a898adff44d..aa80d60a4b1a1 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,40 +1,24 @@ 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::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{implements_trait, is_copy, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; use clippy_utils::{ expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, }; - -use hir::def::DefKind; -use hir::MatchSource; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; -use rustc_hir::def::Res; -use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, - Path, QPath, TyKind, UnOp, + self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, + Pat, PatKind, Path, QPath, 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, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamEnv, ParamTy, ProjectionPredicate, Ty, - TyCtxt, TypeVisitableExt, TypeckResults, -}; +use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{Obligation, ObligationCause}; -use std::collections::VecDeque; declare_clippy_lint! { /// ### What it does @@ -182,24 +166,6 @@ pub struct Dereferencing<'tcx> { /// /// 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: Msrv, -} - -impl<'tcx> Dereferencing<'tcx> { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { - msrv, - ..Dereferencing::default() - } - } } #[derive(Debug)] @@ -354,52 +320,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { )); }, (Some(use_cx), RefOp::AddrOf(mutability)) => { - let defined_ty = use_cx.node.defined_ty(cx); - - // Check needless_borrow for generic arguments. - if !use_cx.is_ty_unified - && let Some(DefinedTy::Mir(ty)) = defined_ty - && let ty::Param(ty) = *ty.value.skip_binder().kind() - && let Some((hir_id, fn_id, i)) = match use_cx.node { - ExprUseNode::MethodArg(_, _, 0) => None, - ExprUseNode::MethodArg(hir_id, None, i) => { - typeck.type_dependent_def_id(hir_id).map(|id| (hir_id, id, i)) - }, - ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i) - if !path_has_args(p) => match typeck.qpath_res(p, hir_id) { - Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => { - Some((hir_id, id, i)) - }, - _ => None, - }, - _ => None, - } && let count = needless_borrow_generic_arg_count( - cx, - &mut self.possible_borrowers, - fn_id, - typeck.node_args(hir_id), - i, - ty, - expr, - &self.msrv, - ) && count != 0 - { - self.state = Some(( - State::DerefedBorrow(DerefedBorrow { - count: count - 1, - msg: "the borrowed expression implements the required traits", - stability: TyCoercionStability::None, - for_field_access: None, - }), - StateData { - span: expr.span, - hir_id: expr.hir_id, - adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), - }, - )); - return; - } - // Find the number of times the borrow is auto-derefed. let mut iter = use_cx.adjustments.iter(); let mut deref_count = 0usize; @@ -418,7 +338,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { }; }; - let stability = defined_ty.map_or(TyCoercionStability::None, |ty| { + let stability = use_cx.node.defined_ty(cx).map_or(TyCoercionStability::None, |ty| { TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return()) }); let can_auto_borrow = match use_cx.node { @@ -699,12 +619,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } 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; @@ -728,8 +642,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { self.current_body = None; } } - - extract_msrv_attr!(LateContext); } fn try_parse_ref_op<'tcx>( @@ -787,13 +699,6 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { } } -fn path_has_args(p: &QPath<'_>) -> bool { - match *p { - QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(), - _ => false, - } -} - fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { if let Some(parent) = get_parent_expr(cx, e) && parent.span.ctxt() == e.span.ctxt() @@ -980,272 +885,6 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { v.0 } -/// Checks for the number of borrow expressions which can be removed from the given expression -/// where the expression is used as an argument to a function expecting a generic type. -/// -/// The following constraints will be checked: -/// * The borrowed expression meets all the generic type's constraints. -/// * The generic type appears only once in the functions signature. -/// * The borrowed value will not be moved if it is used later in the function. -#[expect(clippy::too_many_arguments)] -fn needless_borrow_generic_arg_count<'tcx>( - cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - fn_id: DefId, - callee_args: &'tcx List>, - arg_index: usize, - param_ty: ParamTy, - mut expr: &Expr<'tcx>, - msrv: &Msrv, -) -> usize { - let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); - let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); - - let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder(); - let predicates = cx.tcx.param_env(fn_id).caller_bounds(); - let projection_predicates = predicates - .iter() - .filter_map(|predicate| { - if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() { - Some(projection_predicate) - } else { - None - } - }) - .collect::>(); - - let mut trait_with_ref_mut_self_method = false; - - // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return. - if predicates - .iter() - .filter_map(|predicate| { - if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx) - { - Some(trait_predicate.trait_ref.def_id) - } else { - None - } - }) - .inspect(|trait_def_id| { - trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id); - }) - .all(|trait_def_id| { - Some(trait_def_id) == destruct_trait_def_id - || Some(trait_def_id) == sized_trait_def_id - || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id) - }) - { - return 0; - } - - // See: - // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201 - // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 - if projection_predicates - .iter() - .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate)) - { - return 0; - } - - // `args_with_referent_ty` can be constructed outside of `check_referent` because the same - // elements are modified each time `check_referent` is called. - let mut args_with_referent_ty = callee_args.to_vec(); - - let mut check_reference_and_referent = |reference, referent| { - let referent_ty = cx.typeck_results().expr_ty(referent); - - 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; - } - - // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 - if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) { - return false; - } - - if !replace_types( - cx, - param_ty, - referent_ty, - fn_sig, - arg_index, - &projection_predicates, - &mut args_with_referent_ty, - ) { - return false; - } - - predicates.iter().all(|predicate| { - if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) - && let ty::Param(param_ty) = trait_predicate.self_ty().kind() - && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack() - && ty.is_array() - && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) - { - return false; - } - - let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty); - let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); - let infcx = cx.tcx.infer_ctxt().build(); - infcx.predicate_must_hold_modulo_regions(&obligation) - }) - }; - - let mut count = 0; - while let ExprKind::AddrOf(_, _, referent) = expr.kind { - if !check_reference_and_referent(expr, referent) { - break; - } - expr = referent; - count += 1; - } - count -} - -fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { - cx.tcx - .associated_items(trait_def_id) - .in_definition_order() - .any(|assoc_item| { - if assoc_item.fn_has_self_parameter { - let self_ty = cx - .tcx - .fn_sig(assoc_item.def_id) - .instantiate_identity() - .skip_binder() - .inputs()[0]; - matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut)) - } else { - false - } - }) -} - -fn is_mixed_projection_predicate<'tcx>( - cx: &LateContext<'tcx>, - callee_def_id: DefId, - projection_predicate: &ProjectionPredicate<'tcx>, -) -> bool { - let generics = cx.tcx.generics_of(callee_def_id); - // The predicate requires the projected type to equal a type parameter from the parent context. - if let Some(term_ty) = projection_predicate.term.ty() - && let ty::Param(term_param_ty) = term_ty.kind() - && (term_param_ty.index as usize) < generics.parent_count - { - // The inner-most self type is a type parameter from the current function. - let mut projection_ty = projection_predicate.projection_ty; - loop { - match projection_ty.self_ty().kind() { - ty::Alias(ty::Projection, inner_projection_ty) => { - projection_ty = *inner_projection_ty; - } - ty::Param(param_ty) => { - return (param_ty.index as usize) >= generics.parent_count; - } - _ => { - return false; - } - } - } - } else { - false - } -} - -fn referent_used_exactly_once<'tcx>( - cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - reference: &Expr<'tcx>, -) -> bool { - if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) - && let Some(local) = expr_local(cx.tcx, reference) - && let [location] = *local_assignments(mir, local).as_slice() - && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) - && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind - && !place.is_indirect_first_projection() - { - 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 `args`, 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`. -// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757 -fn replace_types<'tcx>( - cx: &LateContext<'tcx>, - param_ty: ParamTy, - new_ty: Ty<'tcx>, - fn_sig: FnSig<'tcx>, - arg_index: usize, - projection_predicates: &[ProjectionPredicate<'tcx>], - args: &mut [ty::GenericArg<'tcx>], -) -> bool { - let mut replaced = BitSet::new_empty(args.len()); - - let mut deque = VecDeque::with_capacity(args.len()); - deque.push_back((param_ty, new_ty)); - - while let Some((param_ty, new_ty)) = deque.pop_front() { - // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in. - if !fn_sig - .inputs_and_output - .iter() - .enumerate() - .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx))) - { - return false; - } - - args[param_ty.index as usize] = ty::GenericArg::from(new_ty); - - // The `replaced.insert(...)` check provides some protection against infinite loops. - if replaced.insert(param_ty.index) { - for projection_predicate in projection_predicates { - if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx) - && let Some(term_ty) = projection_predicate.term.ty() - && let ty::Param(term_param_ty) = term_ty.kind() - { - let projection = cx.tcx.mk_ty_from_kind(ty::Alias( - ty::Projection, - projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty), - )); - - if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) - && args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) - { - deque.push_back((*term_param_ty, projected_ty)); - } - } - } - } - } - - true -} - fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool { if let ty::Adt(adt, _) = *ty.kind() { adt.is_struct() && adt.all_fields().any(|f| f.name == name) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3f6bdb2b62cc8..614ddcda88f17 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -228,6 +228,7 @@ mod mutex_atomic; mod needless_arbitrary_self_type; mod needless_bool; mod needless_borrowed_ref; +mod needless_borrows_for_generic_args; mod needless_continue; mod needless_else; mod needless_for_each; @@ -880,7 +881,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); 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::>::default()); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); let future_size_threshold = conf.future_size_threshold; @@ -1104,6 +1105,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)); store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)); store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)); + store.register_late_pass(move |_| { + Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new( + msrv(), + )) + }); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs new file mode 100644 index 0000000000000..d55c77a92b158 --- /dev/null +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -0,0 +1,410 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::is_copy; +use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; +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::{ + self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamTy, ProjectionPredicate, Ty, +}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::sym; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::{Obligation, ObligationCause}; +use std::collections::VecDeque; + +declare_clippy_lint! { + /// ### What it does + /// Checks for borrow operations (`&`) that used as a generic argument to a + /// function when the borrowed value could be used. + /// + /// ### Why is this bad? + /// Suggests that the receiver of the expression borrows + /// the expression. + /// + /// ### Known problems + /// The lint cannot tell when the implementation of a trait + /// for `&T` and `T` do different things. Removing a borrow + /// in such a case can change the semantics of the code. + /// + /// ### Example + /// ```rust + /// fn f(_: impl AsRef) {} + /// + /// let x = "foo"; + /// f(&x); + /// ``` + /// + /// Use instead: + /// ```rust + /// fn f(_: impl AsRef) {} + /// + /// let x = "foo"; + /// f(x); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub NEEDLESS_BORROWS_FOR_GENERIC_ARGS, + style, + "taking a reference that is going to be automatically dereferenced" +} + +pub struct NeedlessBorrowsForGenericArgs<'tcx> { + /// 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: Msrv, +} +impl_lint_pass!(NeedlessBorrowsForGenericArgs<'_> => [NEEDLESS_BORROWS_FOR_GENERIC_ARGS]); + +impl NeedlessBorrowsForGenericArgs<'_> { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { + possible_borrowers: Vec::new(), + msrv, + } + } +} + +impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if matches!(expr.kind, ExprKind::AddrOf(..)) + && !expr.span.from_expansion() + && let Some(use_cx) = expr_use_ctxt(cx, expr) + && !use_cx.is_ty_unified + && let Some(DefinedTy::Mir(ty)) = use_cx.node.defined_ty(cx) + && let ty::Param(ty) = *ty.value.skip_binder().kind() + && let Some((hir_id, fn_id, i)) = match use_cx.node { + ExprUseNode::MethodArg(_, _, 0) => None, + ExprUseNode::MethodArg(hir_id, None, i) => { + cx.typeck_results().type_dependent_def_id(hir_id).map(|id| (hir_id, id, i)) + }, + ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i) + if !path_has_args(p) => match cx.typeck_results().qpath_res(p, hir_id) { + Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => { + Some((hir_id, id, i)) + }, + _ => None, + }, + _ => None, + } && let count = needless_borrow_count( + cx, + &mut self.possible_borrowers, + fn_id, + cx.typeck_results().node_args(hir_id), + i, + ty, + expr, + &self.msrv, + ) && count != 0 + { + span_lint_and_then( + cx, + NEEDLESS_BORROWS_FOR_GENERIC_ARGS, + expr.span, + "the borrowed expression implements the required traits", + |diag| { + let mut app = Applicability::MachineApplicable; + let snip_span = peel_n_hir_expr_refs(expr, count).0.span; + let snip = snippet_with_context(cx, snip_span, expr.span.ctxt(), "..", &mut app).0; + diag.span_suggestion(expr.span, "change this to", snip.into_owned(), app); + } + ); + } + } + + 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(); + } + } + + extract_msrv_attr!(LateContext); +} + +fn path_has_args(p: &QPath<'_>) -> bool { + match *p { + QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(), + _ => false, + } +} + +/// Checks for the number of borrow expressions which can be removed from the given expression +/// where the expression is used as an argument to a function expecting a generic type. +/// +/// The following constraints will be checked: +/// * The borrowed expression meets all the generic type's constraints. +/// * The generic type appears only once in the functions signature. +/// * The borrowed value will not be moved if it is used later in the function. +#[expect(clippy::too_many_arguments)] +fn needless_borrow_count<'tcx>( + cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + fn_id: DefId, + callee_args: &'tcx List>, + arg_index: usize, + param_ty: ParamTy, + mut expr: &Expr<'tcx>, + msrv: &Msrv, +) -> usize { + let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); + let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); + + let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder(); + let predicates = cx.tcx.param_env(fn_id).caller_bounds(); + let projection_predicates = predicates + .iter() + .filter_map(|predicate| { + if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() { + Some(projection_predicate) + } else { + None + } + }) + .collect::>(); + + let mut trait_with_ref_mut_self_method = false; + + // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return. + if predicates + .iter() + .filter_map(|predicate| { + if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx) + { + Some(trait_predicate.trait_ref.def_id) + } else { + None + } + }) + .inspect(|trait_def_id| { + trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id); + }) + .all(|trait_def_id| { + Some(trait_def_id) == destruct_trait_def_id + || Some(trait_def_id) == sized_trait_def_id + || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id) + }) + { + return 0; + } + + // See: + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201 + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 + if projection_predicates + .iter() + .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate)) + { + return 0; + } + + // `args_with_referent_ty` can be constructed outside of `check_referent` because the same + // elements are modified each time `check_referent` is called. + let mut args_with_referent_ty = callee_args.to_vec(); + + let mut check_reference_and_referent = |reference, referent| { + let referent_ty = cx.typeck_results().expr_ty(referent); + + 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; + } + + // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 + if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + return false; + } + + if !replace_types( + cx, + param_ty, + referent_ty, + fn_sig, + arg_index, + &projection_predicates, + &mut args_with_referent_ty, + ) { + return false; + } + + predicates.iter().all(|predicate| { + if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) + && let ty::Param(param_ty) = trait_predicate.self_ty().kind() + && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack() + && ty.is_array() + && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) + { + return false; + } + + let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); + let infcx = cx.tcx.infer_ctxt().build(); + infcx.predicate_must_hold_modulo_regions(&obligation) + }) + }; + + let mut count = 0; + while let ExprKind::AddrOf(_, _, referent) = expr.kind { + if !check_reference_and_referent(expr, referent) { + break; + } + expr = referent; + count += 1; + } + count +} + +fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { + cx.tcx + .associated_items(trait_def_id) + .in_definition_order() + .any(|assoc_item| { + if assoc_item.fn_has_self_parameter { + let self_ty = cx + .tcx + .fn_sig(assoc_item.def_id) + .instantiate_identity() + .skip_binder() + .inputs()[0]; + matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut)) + } else { + false + } + }) +} + +fn is_mixed_projection_predicate<'tcx>( + cx: &LateContext<'tcx>, + callee_def_id: DefId, + projection_predicate: &ProjectionPredicate<'tcx>, +) -> bool { + let generics = cx.tcx.generics_of(callee_def_id); + // The predicate requires the projected type to equal a type parameter from the parent context. + if let Some(term_ty) = projection_predicate.term.ty() + && let ty::Param(term_param_ty) = term_ty.kind() + && (term_param_ty.index as usize) < generics.parent_count + { + // The inner-most self type is a type parameter from the current function. + let mut projection_ty = projection_predicate.projection_ty; + loop { + match projection_ty.self_ty().kind() { + ty::Alias(ty::Projection, inner_projection_ty) => { + projection_ty = *inner_projection_ty; + } + ty::Param(param_ty) => { + return (param_ty.index as usize) >= generics.parent_count; + } + _ => { + return false; + } + } + } + } else { + false + } +} + +fn referent_used_exactly_once<'tcx>( + cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + reference: &Expr<'tcx>, +) -> bool { + if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) + && let Some(local) = expr_local(cx.tcx, reference) + && let [location] = *local_assignments(mir, local).as_slice() + && let block_data = &mir.basic_blocks[location.block] + && let Some(statement) = block_data.statements.get(location.statement_index) + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind + && !place.is_indirect_first_projection() + { + 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 `args`, 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`. +// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757 +fn replace_types<'tcx>( + cx: &LateContext<'tcx>, + param_ty: ParamTy, + new_ty: Ty<'tcx>, + fn_sig: FnSig<'tcx>, + arg_index: usize, + projection_predicates: &[ProjectionPredicate<'tcx>], + args: &mut [ty::GenericArg<'tcx>], +) -> bool { + let mut replaced = BitSet::new_empty(args.len()); + + let mut deque = VecDeque::with_capacity(args.len()); + deque.push_back((param_ty, new_ty)); + + while let Some((param_ty, new_ty)) = deque.pop_front() { + // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in. + if !fn_sig + .inputs_and_output + .iter() + .enumerate() + .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx))) + { + return false; + } + + args[param_ty.index as usize] = ty::GenericArg::from(new_ty); + + // The `replaced.insert(...)` check provides some protection against infinite loops. + if replaced.insert(param_ty.index) { + for projection_predicate in projection_predicates { + if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx) + && let Some(term_ty) = projection_predicate.term.ty() + && let ty::Param(term_param_ty) = term_ty.kind() + { + let projection = cx.tcx.mk_ty_from_kind(ty::Alias( + ty::Projection, + projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty), + )); + + if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) + && args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) + { + deque.push_back((*term_param_ty, projected_ty)); + } + } + } + } + } + + true +} diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 0a52b25229d76..c2c5f765abfff 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -131,21 +131,6 @@ fn main() { 0 } } - - let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); - let _ = std::path::Path::new(".").join("."); - deref_target_is_x(X); - 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 - let _ = Box::new(&""); // Don't lint. Type parameter appears in return type - ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter - refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't - multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments } #[allow(clippy::needless_borrowed_reference)] @@ -201,103 +186,6 @@ mod issue9160 { } } -#[derive(Clone, Copy)] -struct X; - -impl std::ops::Deref for X { - type Target = X; - fn deref(&self) -> &Self::Target { - self - } -} - -fn deref_target_is_x(_: T) -where - T: std::ops::Deref, -{ -} - -fn multiple_constraints(_: T) -where - T: IntoIterator + IntoIterator, - U: IntoIterator, - V: AsRef, - X: IntoIterator, - Y: AsRef, -{ -} - -fn multiple_constraints_normalizes_to_same(_: T, _: V) -where - T: std::ops::Deref, - U: std::ops::Deref, -{ -} - -fn only_sized(_: T) {} - -fn ref_as_ref_path(_: &'static T) -where - &'static T: AsRef, -{ -} - -trait RefsOnly { - type Referent; -} - -impl RefsOnly for &T { - type Referent = T; -} - -fn refs_only(_: T) -where - T: RefsOnly, -{ -} - -fn multiple_constraints_normalizes_to_different(_: T, _: U) -where - T: IntoIterator, - U: IntoIterator, - V: AsRef, -{ -} - -// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -mod copyable_iterator { - #[derive(Clone, Copy)] - struct Iter; - impl Iterator for Iter { - type Item = (); - fn next(&mut self) -> Option { - None - } - } - fn takes_iter(_: impl Iterator) {} - fn dont_warn(mut x: Iter) { - takes_iter(&mut x); - } - #[allow(unused_mut)] - fn warn(mut x: &mut Iter) { - takes_iter(x) - } -} - -#[clippy::msrv = "1.52.0"] -mod under_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - } -} - -#[clippy::msrv = "1.53.0"] -mod meets_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); - } -} - fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -326,184 +214,6 @@ fn issue9383() { } } -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); -} - -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) {} -} - -mod used_exactly_once { - fn foo(x: String) { - use_x(x); - } - fn use_x(_: impl AsRef) {} -} - -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) {} -} - -// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 -mod issue_9111 { - struct A; - - impl Extend for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - impl<'a> Extend<&'a u8> for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - fn main() { - let mut a = A; - a.extend(&[]); // vs a.extend([]); - } -} - -mod issue_9710 { - fn main() { - let string = String::new(); - for _i in 0..10 { - f(&string); - } - } - - fn f>(_: T) {} -} - -mod issue_9739 { - fn foo(_it: impl IntoIterator) {} - - fn main() { - foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9739_method_variant { - struct S; - - impl S { - fn foo(&self, _it: impl IntoIterator) {} - } - - fn main() { - S.foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9782 { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - // 100 - foo::<[u8; 100]>(a); - foo(a); - - // 16 - foo::<&[u8]>(&a); - foo(a.as_slice()); - - // 8 - foo::<&[u8; 100]>(&a); - foo(a); - } -} - -mod issue_9782_type_relative_variant { - struct S; - - impl S { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S::foo::<&[u8; 100]>(&a); - } -} - -mod issue_9782_method_variant { - struct S; - - impl S { - fn foo>(&self, t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S.foo::<&[u8; 100]>(&a); - } -} - -mod issue_10535 { - static SOME_STATIC: String = String::new(); - - static UNIT: () = compute(&SOME_STATIC); - - pub const fn compute(_: T) - where - T: Copy, - { - } -} - mod issue_10253 { struct S; trait X { diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 34a95d184635d..0cd6e41b8a47a 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -131,21 +131,6 @@ fn main() { 0 } } - - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - let _ = std::path::Path::new(".").join(&&"."); - deref_target_is_x(&X); - 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 - let _ = Box::new(&""); // Don't lint. Type parameter appears in return type - ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter - refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't - multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments } #[allow(clippy::needless_borrowed_reference)] @@ -201,103 +186,6 @@ mod issue9160 { } } -#[derive(Clone, Copy)] -struct X; - -impl std::ops::Deref for X { - type Target = X; - fn deref(&self) -> &Self::Target { - self - } -} - -fn deref_target_is_x(_: T) -where - T: std::ops::Deref, -{ -} - -fn multiple_constraints(_: T) -where - T: IntoIterator + IntoIterator, - U: IntoIterator, - V: AsRef, - X: IntoIterator, - Y: AsRef, -{ -} - -fn multiple_constraints_normalizes_to_same(_: T, _: V) -where - T: std::ops::Deref, - U: std::ops::Deref, -{ -} - -fn only_sized(_: T) {} - -fn ref_as_ref_path(_: &'static T) -where - &'static T: AsRef, -{ -} - -trait RefsOnly { - type Referent; -} - -impl RefsOnly for &T { - type Referent = T; -} - -fn refs_only(_: T) -where - T: RefsOnly, -{ -} - -fn multiple_constraints_normalizes_to_different(_: T, _: U) -where - T: IntoIterator, - U: IntoIterator, - V: AsRef, -{ -} - -// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -mod copyable_iterator { - #[derive(Clone, Copy)] - struct Iter; - impl Iterator for Iter { - type Item = (); - fn next(&mut self) -> Option { - None - } - } - fn takes_iter(_: impl Iterator) {} - fn dont_warn(mut x: Iter) { - takes_iter(&mut x); - } - #[allow(unused_mut)] - fn warn(mut x: &mut Iter) { - takes_iter(&mut x) - } -} - -#[clippy::msrv = "1.52.0"] -mod under_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - } -} - -#[clippy::msrv = "1.53.0"] -mod meets_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - } -} - fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -326,184 +214,6 @@ fn issue9383() { } } -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); -} - -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) {} -} - -mod used_exactly_once { - fn foo(x: String) { - use_x(&x); - } - fn use_x(_: impl AsRef) {} -} - -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) {} -} - -// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 -mod issue_9111 { - struct A; - - impl Extend for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - impl<'a> Extend<&'a u8> for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - fn main() { - let mut a = A; - a.extend(&[]); // vs a.extend([]); - } -} - -mod issue_9710 { - fn main() { - let string = String::new(); - for _i in 0..10 { - f(&string); - } - } - - fn f>(_: T) {} -} - -mod issue_9739 { - fn foo(_it: impl IntoIterator) {} - - fn main() { - foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9739_method_variant { - struct S; - - impl S { - fn foo(&self, _it: impl IntoIterator) {} - } - - fn main() { - S.foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9782 { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - // 100 - foo::<[u8; 100]>(a); - foo(a); - - // 16 - foo::<&[u8]>(&a); - foo(a.as_slice()); - - // 8 - foo::<&[u8; 100]>(&a); - foo(&a); - } -} - -mod issue_9782_type_relative_variant { - struct S; - - impl S { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S::foo::<&[u8; 100]>(&a); - } -} - -mod issue_9782_method_variant { - struct S; - - impl S { - fn foo>(&self, t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S.foo::<&[u8; 100]>(&a); - } -} - -mod issue_10535 { - static SOME_STATIC: String = String::new(); - - static UNIT: () = compute(&SOME_STATIC); - - pub const fn compute(_: T) - where - T: Copy, - { - } -} - mod issue_10253 { struct S; trait X { diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 8e27014d53c3e..e91b78b0a1520 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -121,101 +121,17 @@ error: this expression creates a reference which is immediately dereferenced by LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` -error: the borrowed expression implements the required traits - --> $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: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: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:138:26 - | -LL | multiple_constraints(&[[""]]); - | ^^^^^^^ help: change this to: `[[""]]` - -error: the borrowed expression implements the required traits - --> $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: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:190:13 + --> $DIR/needless_borrow.rs:175: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:199:13 + --> $DIR/needless_borrow.rs:184:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:283:20 - | -LL | takes_iter(&mut x) - | ^^^^^^ help: change this to: `x` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:297:55 - | -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:335: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:336: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:354:15 - | -LL | debug(&x); - | ^^ help: change this to: `x` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:363:15 - | -LL | use_x(&x); - | ^^ help: change this to: `x` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:457:13 - | -LL | foo(&a); - | ^^ help: change this to: `a` - -error: aborting due to 36 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/needless_borrows_for_generic_args.fixed b/tests/ui/needless_borrows_for_generic_args.fixed new file mode 100644 index 0000000000000..2a335516f51c3 --- /dev/null +++ b/tests/ui/needless_borrows_for_generic_args.fixed @@ -0,0 +1,287 @@ +#![warn(clippy::needless_borrows_for_generic_args)] +#![allow( + clippy::unnecessary_to_owned, + clippy::unnecessary_literal_unwrap, + clippy::needless_borrow +)] + +use core::ops::Deref; +use std::any::Any; +use std::ffi::OsStr; +use std::fmt::{Debug, Display}; +use std::path::Path; +use std::process::Command; + +fn main() { + let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap(); + let _ = Path::new(".").join("."); + let _ = Any::type_id(&""); // Don't lint. `Any` is only bound + let _ = Box::new(&""); // Don't lint. Type parameter appears in return type + let _ = Some("").unwrap_or(&""); + let _ = std::fs::write("x", "".to_string()); + + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn deref_target_is_x>(_: T) {} + + deref_target_is_x(X); + } + { + fn multiple_constraints(_: T) + where + T: IntoIterator + IntoIterator, + U: IntoIterator, + V: AsRef, + X: IntoIterator, + Y: AsRef, + { + } + + multiple_constraints([[""]]); + } + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn multiple_constraints_normalizes_to_same(_: T, _: V) + where + T: Deref, + U: Deref, + { + } + + multiple_constraints_normalizes_to_same(X, X); + } + { + fn only_sized(_: T) {} + only_sized(&""); // Don't lint. `Sized` is only bound + } + { + fn ref_as_ref_path(_: &'static T) + where + &'static T: AsRef, + { + } + + ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter + } + { + trait RefsOnly { + type Referent; + } + + impl RefsOnly for &T { + type Referent = T; + } + + fn refs_only(_: T) + where + T: RefsOnly, + { + } + + refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't + } + { + fn multiple_constraints_normalizes_to_different(_: T, _: U) + where + T: IntoIterator, + U: IntoIterator, + V: AsRef, + { + } + multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments + } + // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 + { + #[derive(Clone, Copy)] + struct Iter; + impl Iterator for Iter { + type Item = (); + fn next(&mut self) -> Option { + None + } + } + fn takes_iter(_: impl Iterator) {} + fn dont_warn(mut x: Iter) { + takes_iter(&mut x); + } + #[allow(unused_mut)] + fn warn(mut x: &mut Iter) { + takes_iter(x) + } + } + #[clippy::msrv = "1.52.0"] + { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + }; + #[clippy::msrv = "1.53.0"] + { + let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap(); + }; + { + 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); + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + let x = X; + f(&x); // Don't lint. Has significant drop + } + { + fn f(_: impl AsRef) {} + + let x = String::new(); + f(x); + } + { + fn f(_: impl AsRef) {} + fn f2(_: impl AsRef) {} + + let x = String::new(); + f(&x); + f2(&x); + } + // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 + // issue 9111 + { + struct A; + + impl Extend for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + impl<'a> Extend<&'a u8> for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + let mut a = A; + a.extend(&[]); // vs a.extend([]); + } + // issue 9710 + { + fn f(_: impl AsRef) {} + + let x = String::new(); + for _ in 0..10 { + f(&x); + } + } + // issue 9739 + { + fn foo(_it: impl IntoIterator) {} + foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + { + struct S; + + impl S { + fn foo(&self, _it: impl IntoIterator) {} + } + + S.foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + // issue 9782 + { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + + let a: [u8; 100] = [0u8; 100]; + + // 100 + foo::<[u8; 100]>(a); + foo(a); + + // 16 + foo::<&[u8]>(&a); + foo(a.as_slice()); + + // 8 + foo::<&[u8; 100]>(&a); + foo(a); + } + { + struct S; + + impl S { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S::foo::<&[u8; 100]>(&a); + } + { + struct S; + + impl S { + fn foo>(&self, t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S.foo::<&[u8; 100]>(&a); + } + // issue 10535 + { + static SOME_STATIC: String = String::new(); + + static UNIT: () = compute(&SOME_STATIC); + + pub const fn compute(_: T) + where + T: Copy, + { + } + } +} diff --git a/tests/ui/needless_borrows_for_generic_args.rs b/tests/ui/needless_borrows_for_generic_args.rs new file mode 100644 index 0000000000000..f0567f486acca --- /dev/null +++ b/tests/ui/needless_borrows_for_generic_args.rs @@ -0,0 +1,287 @@ +#![warn(clippy::needless_borrows_for_generic_args)] +#![allow( + clippy::unnecessary_to_owned, + clippy::unnecessary_literal_unwrap, + clippy::needless_borrow +)] + +use core::ops::Deref; +use std::any::Any; +use std::ffi::OsStr; +use std::fmt::{Debug, Display}; +use std::path::Path; +use std::process::Command; + +fn main() { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + let _ = Path::new(".").join(&&"."); + let _ = Any::type_id(&""); // Don't lint. `Any` is only bound + let _ = Box::new(&""); // Don't lint. Type parameter appears in return type + let _ = Some("").unwrap_or(&""); + let _ = std::fs::write("x", &"".to_string()); + + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn deref_target_is_x>(_: T) {} + + deref_target_is_x(&X); + } + { + fn multiple_constraints(_: T) + where + T: IntoIterator + IntoIterator, + U: IntoIterator, + V: AsRef, + X: IntoIterator, + Y: AsRef, + { + } + + multiple_constraints(&[[""]]); + } + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn multiple_constraints_normalizes_to_same(_: T, _: V) + where + T: Deref, + U: Deref, + { + } + + multiple_constraints_normalizes_to_same(&X, X); + } + { + fn only_sized(_: T) {} + only_sized(&""); // Don't lint. `Sized` is only bound + } + { + fn ref_as_ref_path(_: &'static T) + where + &'static T: AsRef, + { + } + + ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter + } + { + trait RefsOnly { + type Referent; + } + + impl RefsOnly for &T { + type Referent = T; + } + + fn refs_only(_: T) + where + T: RefsOnly, + { + } + + refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't + } + { + fn multiple_constraints_normalizes_to_different(_: T, _: U) + where + T: IntoIterator, + U: IntoIterator, + V: AsRef, + { + } + multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments + } + // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 + { + #[derive(Clone, Copy)] + struct Iter; + impl Iterator for Iter { + type Item = (); + fn next(&mut self) -> Option { + None + } + } + fn takes_iter(_: impl Iterator) {} + fn dont_warn(mut x: Iter) { + takes_iter(&mut x); + } + #[allow(unused_mut)] + fn warn(mut x: &mut Iter) { + takes_iter(&mut x) + } + } + #[clippy::msrv = "1.52.0"] + { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + }; + #[clippy::msrv = "1.53.0"] + { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + }; + { + 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); + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + let x = X; + f(&x); // Don't lint. Has significant drop + } + { + fn f(_: impl AsRef) {} + + let x = String::new(); + f(&x); + } + { + fn f(_: impl AsRef) {} + fn f2(_: impl AsRef) {} + + let x = String::new(); + f(&x); + f2(&x); + } + // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 + // issue 9111 + { + struct A; + + impl Extend for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + impl<'a> Extend<&'a u8> for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + let mut a = A; + a.extend(&[]); // vs a.extend([]); + } + // issue 9710 + { + fn f(_: impl AsRef) {} + + let x = String::new(); + for _ in 0..10 { + f(&x); + } + } + // issue 9739 + { + fn foo(_it: impl IntoIterator) {} + foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + { + struct S; + + impl S { + fn foo(&self, _it: impl IntoIterator) {} + } + + S.foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + // issue 9782 + { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + + let a: [u8; 100] = [0u8; 100]; + + // 100 + foo::<[u8; 100]>(a); + foo(a); + + // 16 + foo::<&[u8]>(&a); + foo(a.as_slice()); + + // 8 + foo::<&[u8; 100]>(&a); + foo(&a); + } + { + struct S; + + impl S { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S::foo::<&[u8; 100]>(&a); + } + { + struct S; + + impl S { + fn foo>(&self, t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S.foo::<&[u8; 100]>(&a); + } + // issue 10535 + { + static SOME_STATIC: String = String::new(); + + static UNIT: () = compute(&SOME_STATIC); + + pub const fn compute(_: T) + where + T: Copy, + { + } + } +} diff --git a/tests/ui/needless_borrows_for_generic_args.stderr b/tests/ui/needless_borrows_for_generic_args.stderr new file mode 100644 index 0000000000000..e2cde2c59a6e0 --- /dev/null +++ b/tests/ui/needless_borrows_for_generic_args.stderr @@ -0,0 +1,77 @@ +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:16:37 + | +LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` + | + = note: `-D clippy::needless-borrows-for-generic-args` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_borrows_for_generic_args)]` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:17:33 + | +LL | let _ = Path::new(".").join(&&"."); + | ^^^^^ help: change this to: `"."` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:21:33 + | +LL | let _ = std::fs::write("x", &"".to_string()); + | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:36:27 + | +LL | deref_target_is_x(&X); + | ^^ help: change this to: `X` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:49:30 + | +LL | multiple_constraints(&[[""]]); + | ^^^^^^^ help: change this to: `[[""]]` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:69:49 + | +LL | multiple_constraints_normalizes_to_same(&X, X); + | ^^ help: change this to: `X` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:127:24 + | +LL | takes_iter(&mut x) + | ^^^^^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:136:41 + | +LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:144:41 + | +LL | let _ = std::fs::write("x", &arg); + | ^^^^ help: change this to: `arg` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:145:41 + | +LL | let _ = std::fs::write("x", &loc); + | ^^^^ help: change this to: `loc` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:167:11 + | +LL | f(&x); + | ^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:247:13 + | +LL | foo(&a); + | ^^ help: change this to: `a` + +error: aborting due to 12 previous errors + diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index 5259d9ce04bbf..094d9574ae987 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -2,7 +2,8 @@ unused, clippy::needless_raw_strings, clippy::needless_raw_string_hashes, - clippy::needless_borrow + clippy::needless_borrow, + clippy::needless_borrows_for_generic_args )] #![warn(clippy::invalid_regex, clippy::trivial_regex)] diff --git a/tests/ui/regex.stderr b/tests/ui/regex.stderr index 91f90157e6847..6d98d691d6f0a 100644 --- a/tests/ui/regex.stderr +++ b/tests/ui/regex.stderr @@ -1,5 +1,5 @@ error: trivial regex - --> $DIR/regex.rs:18:45 + --> $DIR/regex.rs:19:45 | LL | let pipe_in_wrong_position = Regex::new("|"); | ^^^ @@ -9,7 +9,7 @@ LL | let pipe_in_wrong_position = Regex::new("|"); = help: to override `-D warnings` add `#[allow(clippy::trivial_regex)]` error: trivial regex - --> $DIR/regex.rs:20:60 + --> $DIR/regex.rs:21:60 | LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); | ^^^ @@ -17,7 +17,7 @@ LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); = help: the regex is unlikely to be useful as it is error: regex syntax error: invalid character class range, the start must be <= the end - --> $DIR/regex.rs:22:42 + --> $DIR/regex.rs:23:42 | LL | let wrong_char_ranice = Regex::new("[z-a]"); | ^^^ @@ -26,7 +26,7 @@ LL | let wrong_char_ranice = Regex::new("[z-a]"); = help: to override `-D warnings` add `#[allow(clippy::invalid_regex)]` error: regex syntax error: invalid character class range, the start must be <= the end - --> $DIR/regex.rs:25:37 + --> $DIR/regex.rs:26:37 | LL | let some_unicode = Regex::new("[é-è]"); | ^^^ @@ -35,13 +35,13 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:28:33 + --> $DIR/regex.rs:29:33 | LL | let some_regex = Regex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ error: trivial regex - --> $DIR/regex.rs:30:53 + --> $DIR/regex.rs:31:53 | LL | let binary_pipe_in_wrong_position = BRegex::new("|"); | ^^^ @@ -52,7 +52,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:32:41 + --> $DIR/regex.rs:33:41 | LL | let some_binary_regex = BRegex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:33:56 + --> $DIR/regex.rs:34:56 | LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); | ^^^^^^^^^^^^^ @@ -70,7 +70,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:45:37 + --> $DIR/regex.rs:46:37 | LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); | ^^^^^^^^^^^^^ @@ -79,7 +79,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:46:39 + --> $DIR/regex.rs:47:39 | LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); | ^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ error: regex parse error: \b\c ^^ error: unrecognized escape sequence - --> $DIR/regex.rs:53:42 + --> $DIR/regex.rs:54:42 | LL | let escaped_string_span = Regex::new("\\b\\c"); | ^^^^^^^^ @@ -96,19 +96,19 @@ LL | let escaped_string_span = Regex::new("\\b\\c"); = help: consider using a raw string literal: `r".."` error: regex syntax error: duplicate flag - --> $DIR/regex.rs:55:34 + --> $DIR/regex.rs:56:34 | LL | let aux_span = Regex::new("(?ixi)"); | ^ ^ error: regex syntax error: pattern can match invalid UTF-8 - --> $DIR/regex.rs:61:53 + --> $DIR/regex.rs:62:53 | LL | let invalid_utf8_should_lint = Regex::new("(?-u)."); | ^ error: trivial regex - --> $DIR/regex.rs:66:33 + --> $DIR/regex.rs:67:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -116,7 +116,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:69:48 + --> $DIR/regex.rs:70:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -124,7 +124,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:72:42 + --> $DIR/regex.rs:73:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -132,7 +132,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:75:40 + --> $DIR/regex.rs:76:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -140,7 +140,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:78:39 + --> $DIR/regex.rs:79:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -148,7 +148,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:81:39 + --> $DIR/regex.rs:82:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:84:40 + --> $DIR/regex.rs:85:40 | LL | let trivial_backslash = Regex::new("a\\.b"); | ^^^^^^^ @@ -164,7 +164,7 @@ LL | let trivial_backslash = Regex::new("a\\.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:88:36 + --> $DIR/regex.rs:89:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -172,7 +172,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:91:36 + --> $DIR/regex.rs:92:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -180,7 +180,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:94:36 + --> $DIR/regex.rs:95:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -188,7 +188,7 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:97:44 + --> $DIR/regex.rs:98:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 7b662ca92d2ae..67faabc53cb5e 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_borrow, clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] use std::borrow::Cow; diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index d79778a6a2ed4..99f9136427d4d 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_borrow, clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] use std::borrow::Cow; From 32d3387c80b59ad85756aa66237aa6d2a0eca884 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 17 Sep 2023 14:33:06 +0000 Subject: [PATCH 044/149] used_underscore_bindings: respect lint levels on the binding definition --- clippy_lints/src/misc.rs | 104 +++++++++++------------- clippy_utils/src/lib.rs | 27 ++++++ tests/ui/used_underscore_binding.rs | 28 ++++++- tests/ui/used_underscore_binding.stderr | 47 +++++++++-- 4 files changed, 140 insertions(+), 66 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 303f012569087..f8e76a5833e50 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,24 +1,23 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_context}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{ + any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, in_constant, is_integer_literal, + is_lint_allowed, is_no_std_crate, iter_input_pats, last_path_segment, SpanlessEq, +}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - self as hir, def, BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, Stmt, - StmtKind, TyKind, + BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, Ty, + TyKind, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::LocalDefId; -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, is_integer_literal, is_lint_allowed, is_no_std_crate, iter_input_pats, - last_path_segment, SpanlessEq, -}; +use rustc_span::source_map::Span; use crate::ref_patterns::REF_PATTERNS; @@ -257,46 +256,56 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { self.check_cast(cx, expr.span, e, ty); return; } - if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) { - // Don't lint things expanded by #[derive(...)], etc or `await` desugaring + if in_external_macro(cx.sess(), expr.span) + || expr.span.desugaring_kind().is_some() + || any_parent_is_automatically_derived(cx.tcx, expr.hir_id) + { return; } - let sym; - let binding = match expr.kind { - ExprKind::Path(ref qpath) if !matches!(qpath, hir::QPath::LangItem(..)) => { - let binding = last_path_segment(qpath).ident.as_str(); - if binding.starts_with('_') && - !binding.starts_with("__") && - binding != "_result" && // FIXME: #944 - is_used(cx, expr) && - // don't lint if the declaration is in a macro - non_macro_local(cx, cx.qpath_res(qpath, expr.hir_id)) + let (definition_hir_id, ident) = match expr.kind { + ExprKind::Path(ref qpath) => { + if let QPath::Resolved(None, path) = qpath + && let Res::Local(id) = path.res + && is_used(cx, expr) { - Some(binding) + (id, last_path_segment(qpath).ident) } else { - None + return; } }, - ExprKind::Field(_, ident) => { - sym = ident.name; - let name = sym.as_str(); - if name.starts_with('_') && !name.starts_with("__") { - Some(name) + ExprKind::Field(recv, ident) => { + if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def() + && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name) + && let Some(local_did) = field.did.as_local() + && let Some(hir_id) = cx.tcx.opt_local_def_id_to_hir_id(local_did) + && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data() + { + (hir_id, ident) } else { - None + return; } }, - _ => None, + _ => return, }; - if let Some(binding) = binding { - span_lint( + + let name = ident.name.as_str(); + if name.starts_with('_') + && !name.starts_with("__") + && let definition_span = cx.tcx.hir().span(definition_hir_id) + && !definition_span.from_expansion() + && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id]) + { + span_lint_and_then( cx, USED_UNDERSCORE_BINDING, expr.span, &format!( - "used binding `{binding}` which is prefixed with an underscore. A leading \ + "used binding `{name}` which is prefixed with an underscore. A leading \ underscore signals that a binding will not be used" ), + |diag| { + diag.span_note(definition_span, format!("`{name}` is defined here")); + } ); } } @@ -312,29 +321,8 @@ fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } -/// Tests whether an expression is in a macro expansion (e.g., something -/// generated by `#[derive(...)]` or the like). -fn in_attributes_expansion(expr: &Expr<'_>) -> bool { - use rustc_span::hygiene::MacroKind; - if expr.span.from_expansion() { - let data = expr.span.ctxt().outer_expn_data(); - matches!(data.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, _)) - } else { - false - } -} - -/// Tests whether `res` is a variable defined outside a macro. -fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool { - if let def::Res::Local(id) = res { - !cx.tcx.hir().span(id).from_expansion() - } else { - false - } -} - impl LintPass { - fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { + fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) { if_chain! { if let TyKind::Ptr(ref mut_ty) = ty.kind; if is_integer_literal(e, 0); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 4ef3ec19647e9..eb5baa98f62f3 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1785,6 +1785,33 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc None } +/// Returns `true` if the lint is `#[allow]`ed or `#[expect]`ed at any of the `ids`, fulfilling all +/// of the expectations in `ids` +/// +/// This should only be used when the lint would otherwise be emitted, for a way to check if a lint +/// is allowed early to skip work see [`is_lint_allowed`] +/// +/// To emit at a lint at a different context than the one current see +/// [`span_lint_hir`](diagnostics::span_lint_hir) or +/// [`span_lint_hir_and_then`](diagnostics::span_lint_hir_and_then) +pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator) -> bool { + let mut suppress_lint = false; + + for id in ids { + let (level, _) = cx.tcx.lint_level_at_node(lint, id); + if let Some(expectation) = level.get_expectation_id() { + cx.fulfill_expectation(expectation); + } + + match level { + Level::Allow | Level::Expect(_) => suppress_lint = true, + Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {}, + } + } + + suppress_lint +} + /// Returns `true` if the lint is allowed in the current context. This is useful for /// skipping long running code when it's unnecessary /// diff --git a/tests/ui/used_underscore_binding.rs b/tests/ui/used_underscore_binding.rs index c672eff1c2710..a8f404b1400c9 100644 --- a/tests/ui/used_underscore_binding.rs +++ b/tests/ui/used_underscore_binding.rs @@ -1,6 +1,5 @@ //@aux-build:proc_macro_derive.rs -#![feature(rustc_private)] -#![warn(clippy::all)] +#![feature(rustc_private, lint_reasons)] #![warn(clippy::used_underscore_binding)] #![allow(clippy::disallowed_names, clippy::eq_op, clippy::uninlined_format_args)] @@ -107,6 +106,31 @@ async fn await_desugaring() { .await } +struct PhantomField { + _marker: std::marker::PhantomData, +} + +impl std::fmt::Debug for PhantomField { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("PhantomField").field("_marker", &self._marker).finish() + } +} + +struct AllowedField { + #[allow(clippy::used_underscore_binding)] + _allowed: usize, +} + +struct ExpectedField { + #[expect(clippy::used_underscore_binding)] + _expected: usize, +} + +fn lint_levels(allowed: AllowedField, expected: ExpectedField) { + let _ = allowed._allowed; + let _ = expected._expected; +} + fn main() { let foo = 0u32; // tests of unused_underscore lint diff --git a/tests/ui/used_underscore_binding.stderr b/tests/ui/used_underscore_binding.stderr index 289519b172eeb..78d8279810c1f 100644 --- a/tests/ui/used_underscore_binding.stderr +++ b/tests/ui/used_underscore_binding.stderr @@ -1,41 +1,76 @@ 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:24:5 + --> $DIR/used_underscore_binding.rs:23:5 | LL | _foo + 1 | ^^^^ | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:22:22 + | +LL | fn prefix_underscore(_foo: u32) -> u32 { + | ^^^^ = note: `-D clippy::used-underscore-binding` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::used_underscore_binding)]` 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:29:20 + --> $DIR/used_underscore_binding.rs:28:20 | LL | println!("{}", _foo); | ^^^^ + | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:27:24 + | +LL | fn in_macro_or_desugar(_foo: u32) { + | ^^^^ 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:16 + --> $DIR/used_underscore_binding.rs:29:16 | LL | assert_eq!(_foo, _foo); | ^^^^ + | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:27:24 + | +LL | fn in_macro_or_desugar(_foo: u32) { + | ^^^^ 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:22 + --> $DIR/used_underscore_binding.rs:29:22 | LL | assert_eq!(_foo, _foo); | ^^^^ + | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:27:24 + | +LL | fn in_macro_or_desugar(_foo: u32) { + | ^^^^ 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:43:5 + --> $DIR/used_underscore_binding.rs:42:5 | LL | s._underscore_field += 1; | ^^^^^^^^^^^^^^^^^^^ + | +note: `_underscore_field` is defined here + --> $DIR/used_underscore_binding.rs:36:5 + | +LL | _underscore_field: u32, + | ^^^^^^^^^^^^^^^^^^^^^^ 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:104:16 + --> $DIR/used_underscore_binding.rs:103:16 | LL | uses_i(_i); | ^^ + | +note: `_i` is defined here + --> $DIR/used_underscore_binding.rs:102:13 + | +LL | let _i = 5; + | ^^ error: aborting due to 6 previous errors From 1c9f3bef8bb80dc8706e4595a262fe8825ccf74a Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:50:45 -0400 Subject: [PATCH 045/149] Add redundant_as_str lint This lint checks for `as_str` on a `String` immediately followed by `as_bytes` or `is_empty` as those methods are available on `String` too. This could possibly also be extended to `&[u8]` in the future. --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/mod.rs | 29 +++++++++++++++++ clippy_lints/src/methods/redundant_as_str.rs | 34 ++++++++++++++++++++ tests/ui/redundant_as_str.fixed | 6 ++++ tests/ui/redundant_as_str.rs | 6 ++++ tests/ui/redundant_as_str.stderr | 17 ++++++++++ 7 files changed, 94 insertions(+) create mode 100644 clippy_lints/src/methods/redundant_as_str.rs create mode 100644 tests/ui/redundant_as_str.fixed create mode 100644 tests/ui/redundant_as_str.rs create mode 100644 tests/ui/redundant_as_str.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index a2484203e9e89..8c9ab1e2402ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5281,6 +5281,7 @@ Released 2018-09-13 [`readonly_write_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#readonly_write_lock [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation +[`redundant_as_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_as_str [`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block [`redundant_at_rest_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_at_rest_pattern [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index b1ffc34781246..aa2ca0e11494b 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -403,6 +403,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::RANGE_ZIP_WITH_LEN_INFO, crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, + crate::methods::REDUNDANT_AS_STR_INFO, crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, crate::methods::SEARCH_IS_SOME_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f468236480684..0b0a90bac0cb3 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -78,6 +78,7 @@ mod path_ends_with_ext; mod range_zip_with_len; mod read_line_without_trim; mod readonly_write_lock; +mod redundant_as_str; mod repeat_once; mod search_is_some; mod seek_from_current; @@ -3605,6 +3606,32 @@ declare_clippy_lint! { "attempting to compare file extensions using `Path::ends_with`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `as_str()` on a `String`` chained with a method available on the `String` itself. + /// + /// ### Why is this bad? + /// The `as_str()` conversion is pointless and can be removed for simplicity and cleanliness. + /// + /// ### Example + /// ```rust + /// # #![allow(unused)] + /// let owned_string = "This is a string".to_owned(); + /// owned_string.as_str().as_bytes() + /// ``` + /// + /// Use instead: + /// ```rust + /// # #![allow(unused)] + /// let owned_string = "This is a string".to_owned(); + /// owned_string.as_bytes() + /// ``` + #[clippy::version = "1.74.0"] + pub REDUNDANT_AS_STR, + complexity, + "`as_str` used to call a method on `str` that is also available on `String`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3749,6 +3776,7 @@ impl_lint_pass!(Methods => [ READONLY_WRITE_LOCK, ITER_OUT_OF_BOUNDS, PATH_ENDS_WITH_EXT, + REDUNDANT_AS_STR, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3975,6 +4003,7 @@ impl Methods { ("as_deref" | "as_deref_mut", []) => { needless_option_as_deref::check(cx, expr, recv, name); }, + ("as_bytes" | "is_empty", []) => if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); }, ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), diff --git a/clippy_lints/src/methods/redundant_as_str.rs b/clippy_lints/src/methods/redundant_as_str.rs new file mode 100644 index 0000000000000..98cd6afc2b79f --- /dev/null +++ b/clippy_lints/src/methods/redundant_as_str.rs @@ -0,0 +1,34 @@ +use super::REDUNDANT_AS_STR; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::query::Key; +use rustc_span::Span; + +pub(super) fn check( + cx: &LateContext<'_>, + _expr: &Expr<'_>, + recv: &Expr<'_>, + as_str_span: Span, + other_method_span: Span, +) { + if cx + .tcx + .lang_items() + .string() + .is_some_and(|id| Some(id) == cx.typeck_results().expr_ty(recv).ty_adt_id()) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + REDUNDANT_AS_STR, + as_str_span.to(other_method_span), + "this `as_str` is redundant and can be removed as the method immediately following exists on `String` too", + "try", + snippet_with_applicability(cx, other_method_span, "..", &mut applicability).into_owned(), + applicability, + ); + } +} diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed new file mode 100644 index 0000000000000..f339787e68943 --- /dev/null +++ b/tests/ui/redundant_as_str.fixed @@ -0,0 +1,6 @@ +#![warn(clippy::redundant_as_str)] + +fn main() { + let _redundant = "Hello, world!".to_owned().as_bytes(); + let _redundant = "Hello, world!".to_owned().is_empty(); +} diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs new file mode 100644 index 0000000000000..66074df83a865 --- /dev/null +++ b/tests/ui/redundant_as_str.rs @@ -0,0 +1,6 @@ +#![warn(clippy::redundant_as_str)] + +fn main() { + let _redundant = "Hello, world!".to_owned().as_str().as_bytes(); + let _redundant = "Hello, world!".to_owned().as_str().is_empty(); +} diff --git a/tests/ui/redundant_as_str.stderr b/tests/ui/redundant_as_str.stderr new file mode 100644 index 0000000000000..32b5232385dda --- /dev/null +++ b/tests/ui/redundant_as_str.stderr @@ -0,0 +1,17 @@ +error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too + --> $DIR/redundant_as_str.rs:4:49 + | +LL | let _redundant = "Hello, world!".to_owned().as_str().as_bytes(); + | ^^^^^^^^^^^^^^^^^ help: try: `as_bytes` + | + = note: `-D clippy::redundant-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_as_str)]` + +error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too + --> $DIR/redundant_as_str.rs:5:49 + | +LL | let _redundant = "Hello, world!".to_owned().as_str().is_empty(); + | ^^^^^^^^^^^^^^^^^ help: try: `is_empty` + +error: aborting due to 2 previous errors + From 01056c5aae63c546c3d988a0b46e502a3a2a7baf Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Sun, 17 Sep 2023 18:10:21 -0400 Subject: [PATCH 046/149] Fix missing semicolon in redundant_as_str docstring example --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 0b0a90bac0cb3..bd07e6cbf2e2c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3617,14 +3617,14 @@ declare_clippy_lint! { /// ```rust /// # #![allow(unused)] /// let owned_string = "This is a string".to_owned(); - /// owned_string.as_str().as_bytes() + /// owned_string.as_str().as_bytes(); /// ``` /// /// Use instead: /// ```rust /// # #![allow(unused)] /// let owned_string = "This is a string".to_owned(); - /// owned_string.as_bytes() + /// owned_string.as_bytes(); /// ``` #[clippy::version = "1.74.0"] pub REDUNDANT_AS_STR, From b06b915dc04a577cfe599d7e44137124ba12c877 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 18 Sep 2023 12:10:35 +0000 Subject: [PATCH 047/149] Move zero_ptr to the casts module --- clippy_lints/src/casts/mod.rs | 26 +++++++++ clippy_lints/src/casts/zero_ptr.rs | 39 ++++++++++++++ clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/misc.rs | 84 +++--------------------------- 5 files changed, 75 insertions(+), 78 deletions(-) create mode 100644 clippy_lints/src/casts/zero_ptr.rs diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 88ffbb55486bd..b00130ffd76db 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -20,6 +20,7 @@ mod ptr_as_ptr; mod ptr_cast_constness; mod unnecessary_cast; mod utils; +mod zero_ptr; use clippy_utils::is_hir_ty_cfg_dependant; use clippy_utils::msrvs::{self, Msrv}; @@ -665,6 +666,29 @@ declare_clippy_lint! { "casting a known floating-point NaN into an integer" } +declare_clippy_lint! { + /// ### What it does + /// Catch casts from `0` to some pointer type + /// + /// ### Why is this bad? + /// This generally means `null` and is better expressed as + /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}. + /// + /// ### Example + /// ```rust + /// let a = 0 as *const u32; + /// ``` + /// + /// Use instead: + /// ```rust + /// let a = std::ptr::null::(); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub ZERO_PTR, + style, + "using `0 as *{const, mut} T`" +} + pub struct Casts { msrv: Msrv, } @@ -699,6 +723,7 @@ impl_lint_pass!(Casts => [ CAST_SLICE_FROM_RAW_PARTS, AS_PTR_CAST_MUT, CAST_NAN_TO_INT, + ZERO_PTR, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -729,6 +754,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { 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); + zero_ptr::check(cx, expr, cast_expr, cast_to_hir); if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) { cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span); diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs new file mode 100644 index 0000000000000..5071af5ecb986 --- /dev/null +++ b/clippy_lints/src/casts/zero_ptr.rs @@ -0,0 +1,39 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{in_constant, is_integer_literal, std_or_core}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, Mutability, Ty, TyKind}; +use rustc_lint::LateContext; + +use super::ZERO_PTR; + +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { + if let TyKind::Ptr(ref mut_ty) = to.kind + && is_integer_literal(from, 0) + && !in_constant(cx, from.hir_id) + && let Some(std_or_core) = std_or_core(cx) + { + let (msg, sugg_fn) = match mut_ty.mutbl { + Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"), + Mutability::Not => ("`0 as *const _` detected", "ptr::null"), + }; + + let sugg = if let TyKind::Infer = mut_ty.ty.kind { + format!("{std_or_core}::{sugg_fn}()") + } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { + format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") + } else { + return; + }; + + span_lint_and_sugg( + cx, + ZERO_PTR, + expr.span, + msg, + "try", + sugg, + Applicability::MachineApplicable, + ); + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index b1ffc34781246..d45f4013816e9 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -97,6 +97,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::UNNECESSARY_CAST_INFO, + crate::casts::ZERO_PTR_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, @@ -442,7 +443,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, crate::misc::TOPLEVEL_REF_ARG_INFO, crate::misc::USED_UNDERSCORE_BINDING_INFO, - crate::misc::ZERO_PTR_INFO, crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, crate::misc_early::DOUBLE_NEG_INFO, crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dcc6ee68e1b46..fa009c8b2d6c8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -637,7 +637,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool)); store.register_late_pass(|_| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); - store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); store.register_late_pass(|_| Box::new(mut_mut::MutMut)); store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index f8e76a5833e50..9c8b47fb30327 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,21 +1,20 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::source::{snippet, snippet_opt, snippet_with_context}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, in_constant, is_integer_literal, - is_lint_allowed, is_no_std_crate, iter_input_pats, last_path_segment, SpanlessEq, + any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, is_lint_allowed, iter_input_pats, + last_path_segment, SpanlessEq, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, Ty, - TyKind, + BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -55,6 +54,7 @@ declare_clippy_lint! { style, "an entire binding declared as `ref`, in a function argument or a `let` statement" } + declare_clippy_lint! { /// ### What it does /// Checks for the use of bindings with a single leading @@ -102,51 +102,13 @@ declare_clippy_lint! { "using a short circuit boolean condition as a statement" } -declare_clippy_lint! { - /// ### What it does - /// Catch casts from `0` to some pointer type - /// - /// ### Why is this bad? - /// This generally means `null` and is better expressed as - /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}. - /// - /// ### Example - /// ```rust - /// let a = 0 as *const u32; - /// ``` - /// - /// Use instead: - /// ```rust - /// let a = std::ptr::null::(); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub ZERO_PTR, - style, - "using `0 as *{const, mut} T`" -} - -pub struct LintPass { - std_or_core: &'static str, -} -impl Default for LintPass { - fn default() -> Self { - Self { std_or_core: "std" } - } -} -impl_lint_pass!(LintPass => [ +declare_lint_pass!(LintPass => [ TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, SHORT_CIRCUIT_STATEMENT, - ZERO_PTR, ]); impl<'tcx> LateLintPass<'tcx> for LintPass { - fn check_crate(&mut self, cx: &LateContext<'_>) { - if is_no_std_crate(cx) { - self.std_or_core = "core"; - } - } - fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -252,10 +214,6 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Cast(e, ty) = expr.kind { - self.check_cast(cx, expr.span, e, ty); - return; - } if in_external_macro(cx.sess(), expr.span) || expr.span.desugaring_kind().is_some() || any_parent_is_automatically_derived(cx.tcx, expr.hir_id) @@ -320,29 +278,3 @@ fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => is_used(cx, parent), }) } - -impl LintPass { - fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) { - if_chain! { - if let TyKind::Ptr(ref mut_ty) = ty.kind; - if is_integer_literal(e, 0); - if !in_constant(cx, e.hir_id); - then { - let (msg, sugg_fn) = match mut_ty.mutbl { - Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"), - Mutability::Not => ("`0 as *const _` detected", "ptr::null"), - }; - - let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { - (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MachineApplicable) - } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { - (format!("{}::{sugg_fn}::<{mut_ty_snip}>()", self.std_or_core), Applicability::MachineApplicable) - } else { - // `MaybeIncorrect` as type inference may not work with the suggested code - (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MaybeIncorrect) - }; - span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); - } - } - } -} From 32519528dfe8743d006e337c8105868761922d03 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 18 Sep 2023 12:42:37 +0000 Subject: [PATCH 048/149] Update remark CI deps --- .github/workflows/remark.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 7d25b6a2b79e7..30bd476332f75 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -21,7 +21,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v3 with: - node-version: '14.x' + node-version: '18.x' - name: Install remark run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm @@ -29,19 +29,19 @@ jobs: - name: Install mdbook run: | mkdir mdbook - curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.28/mdbook-v0.4.28-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.34/mdbook-v0.4.34-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook echo `pwd`/mdbook >> $GITHUB_PATH # Run - name: Check *.md files - run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null + run: ./node_modules/.bin/remark -u lint -f . - name: Linkcheck book run: | rustup toolchain install nightly --component rust-docs curl https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh -o linkcheck.sh sh linkcheck.sh clippy --path ./book - + - name: Build mdbook run: mdbook build book From b8b420cc79dc18423a9bf59ee3397a398e9aac2a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 18 Sep 2023 15:47:24 +0200 Subject: [PATCH 049/149] Improve code readability by moving the retrieval of closures inside async functions right besides other closures handling. Add doc comment explaining what `MutablyUsedVariablesCtxt::prev_move_to_closure` is about. --- clippy_lints/src/needless_pass_by_ref_mut.rs | 57 +++++++------------- 1 file changed, 19 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index bea44ed92ef38..36ca61c0b44d3 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,13 +1,13 @@ use super::needless_pass_by_value::requires_exact_signature; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; +use clippy_utils::visitors::for_each_expr_with_closures; use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_fn, walk_qpath, FnKind, Visitor}; +use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor}; use rustc_hir::{ - Body, BodyId, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, - PatKind, QPath, + Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath, }; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -22,6 +22,8 @@ use rustc_span::symbol::kw; use rustc_span::Span; use rustc_target::spec::abi::Abi; +use core::ops::ControlFlow; + declare_clippy_lint! { /// ### What it does /// Check if a `&mut` function argument is actually used mutably. @@ -189,17 +191,19 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // We retrieve all the closures declared in the async function because they will // not be found by `euv::Delegate`. - let mut closures_retriever = ClosuresRetriever { - cx, - closures: FxHashSet::default(), - }; - walk_fn(&mut closures_retriever, kind, decl, body.id(), fn_def_id); - check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures_retriever.closures); + let mut closures: FxHashSet = FxHashSet::default(); + for_each_expr_with_closures(cx, body, |expr| { + if let ExprKind::Closure(closure) = expr.kind { + closures.insert(closure.def_id); + } + ControlFlow::<()>::Continue(()) + }); + check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures); while !ctx.async_closures.is_empty() { - let closures = ctx.async_closures.clone(); + let async_closures = ctx.async_closures.clone(); ctx.async_closures.clear(); - check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures); + check_closures(&mut ctx, cx, &infcx, &mut checked_closures, async_closures); } } ctx @@ -264,6 +268,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { struct MutablyUsedVariablesCtxt<'tcx> { mutably_used_vars: HirIdSet, prev_bind: Option, + /// In async functions, the inner AST is composed of multiple layers until we reach the code + /// defined by the user. Because of that, some variables are marked as mutably borrowed even + /// though they're not. This field lists the `HirId` that should not be considered as mutable + /// use of a variable. prev_move_to_closure: HirIdSet, aliases: HirIdMap, async_closures: FxHashSet, @@ -459,30 +467,3 @@ impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> { } } } - -struct ClosuresRetriever<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - closures: FxHashSet, -} - -impl<'a, 'tcx> Visitor<'tcx> for ClosuresRetriever<'a, 'tcx> { - type NestedFilter = OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } - - fn visit_fn( - &mut self, - kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'tcx>, - body_id: BodyId, - _span: Span, - fn_def_id: LocalDefId, - ) { - if matches!(kind, FnKind::Closure) { - self.closures.insert(fn_def_id); - } - walk_fn(self, kind, decl, body_id, fn_def_id); - } -} From f2ab16eac139522ac5a9c6606c81e5967aec6d79 Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:08:02 -0400 Subject: [PATCH 050/149] Add not triggering examples to redundant_as_str test --- tests/ui/redundant_as_str.fixed | 3 +++ tests/ui/redundant_as_str.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed index f339787e68943..c8e44d1c39e97 100644 --- a/tests/ui/redundant_as_str.fixed +++ b/tests/ui/redundant_as_str.fixed @@ -3,4 +3,7 @@ fn main() { let _redundant = "Hello, world!".to_owned().as_bytes(); let _redundant = "Hello, world!".to_owned().is_empty(); + + let _ok = "Hello, world!".to_owned().as_bytes(); + let _ok = "Hello, world!".to_owned().is_empty(); } diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index 66074df83a865..e9aa84419130c 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -3,4 +3,7 @@ fn main() { let _redundant = "Hello, world!".to_owned().as_str().as_bytes(); let _redundant = "Hello, world!".to_owned().as_str().is_empty(); + + let _ok = "Hello, world!".to_owned().as_bytes(); + let _ok = "Hello, world!".to_owned().is_empty(); } From 00ca47b97d59ab3384c9786e5b38f0262f413384 Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:32:22 -0400 Subject: [PATCH 051/149] Add more examples and comments to redundant_as_str test --- tests/ui/redundant_as_str.fixed | 9 --------- tests/ui/redundant_as_str.rs | 19 +++++++++++++++---- tests/ui/redundant_as_str.stderr | 19 +++++-------------- 3 files changed, 20 insertions(+), 27 deletions(-) delete mode 100644 tests/ui/redundant_as_str.fixed diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed deleted file mode 100644 index c8e44d1c39e97..0000000000000 --- a/tests/ui/redundant_as_str.fixed +++ /dev/null @@ -1,9 +0,0 @@ -#![warn(clippy::redundant_as_str)] - -fn main() { - let _redundant = "Hello, world!".to_owned().as_bytes(); - let _redundant = "Hello, world!".to_owned().is_empty(); - - let _ok = "Hello, world!".to_owned().as_bytes(); - let _ok = "Hello, world!".to_owned().is_empty(); -} diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index e9aa84419130c..35b6bdb3c5e85 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -1,9 +1,20 @@ #![warn(clippy::redundant_as_str)] fn main() { - let _redundant = "Hello, world!".to_owned().as_str().as_bytes(); - let _redundant = "Hello, world!".to_owned().as_str().is_empty(); + let string = "Hello, world!".to_owned(); - let _ok = "Hello, world!".to_owned().as_bytes(); - let _ok = "Hello, world!".to_owned().is_empty(); + // These methods are redundant and the `as_str` can be removed. + let _redundant = string.as_str().as_bytes(); + let _redundant = string.as_str().is_empty(); + + // These methods are not redundant, and are equivelant to + // doing dereferencing the string and applying the method. + let _not_redundant = string.as_str().escape_unicode(); + let _not_redundant = string.as_str().trim(); + let _not_redundant = string.as_str().split_whitespace(); + + // These methods don't use `as_str` and are applied on a `str` directly. + let borrowed_str = "Hello, world"! + let _no_as_str = borrowed_str.as_bytes(); + let _no_as_str = borrowed_str.is_empty(); } diff --git a/tests/ui/redundant_as_str.stderr b/tests/ui/redundant_as_str.stderr index 32b5232385dda..e7da34650f2cf 100644 --- a/tests/ui/redundant_as_str.stderr +++ b/tests/ui/redundant_as_str.stderr @@ -1,17 +1,8 @@ -error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too - --> $DIR/redundant_as_str.rs:4:49 +error: expected one of `.`, `;`, `?`, `else`, or an operator, found `!` + --> $DIR/redundant_as_str.rs:17:35 | -LL | let _redundant = "Hello, world!".to_owned().as_str().as_bytes(); - | ^^^^^^^^^^^^^^^^^ help: try: `as_bytes` - | - = note: `-D clippy::redundant-as-str` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::redundant_as_str)]` - -error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too - --> $DIR/redundant_as_str.rs:5:49 - | -LL | let _redundant = "Hello, world!".to_owned().as_str().is_empty(); - | ^^^^^^^^^^^^^^^^^ help: try: `is_empty` +LL | let borrowed_str = "Hello, world"! + | ^ expected one of `.`, `;`, `?`, `else`, or an operator -error: aborting due to 2 previous errors +error: aborting due to previous error From 367ba9cd003e271dc4d7e354b1212fe3c60d3329 Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:36:10 -0400 Subject: [PATCH 052/149] Add not redundant examples for redundant_as_str --- tests/ui/redundant_as_str.fixed | 24 ++++++++++++++++++++++++ tests/ui/redundant_as_str.rs | 10 +++++++--- tests/ui/redundant_as_str.stderr | 19 ++++++++++++++----- 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 tests/ui/redundant_as_str.fixed diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed new file mode 100644 index 0000000000000..a95ab2d25a2c6 --- /dev/null +++ b/tests/ui/redundant_as_str.fixed @@ -0,0 +1,24 @@ +#![warn(clippy::redundant_as_str)] + +fn main() { + let string = "Hello, world!".to_owned(); + + // These methods are redundant and the `as_str` can be removed. + let _redundant = string.as_bytes(); + let _redundant = string.is_empty(); + + // These methods don't use `as_str` when they are redundant. + let _no_as_str = string.as_bytes(); + let _no_as_str = string.is_empty(); + + // These methods are not redundant, and are equivelant to + // doing dereferencing the string and applying the method. + let _not_redundant = string.as_str().escape_unicode(); + let _not_redundant = string.as_str().trim(); + let _not_redundant = string.as_str().split_whitespace(); + + // These methods don't use `as_str` and are applied on a `str` directly. + let borrowed_str = "Hello, world!"; + let _is_str = borrowed_str.as_bytes(); + let _is_str = borrowed_str.is_empty(); +} diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index 35b6bdb3c5e85..d5ccfe2effeff 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -7,6 +7,10 @@ fn main() { let _redundant = string.as_str().as_bytes(); let _redundant = string.as_str().is_empty(); + // These methods don't use `as_str` when they are redundant. + let _no_as_str = string.as_bytes(); + let _no_as_str = string.is_empty(); + // These methods are not redundant, and are equivelant to // doing dereferencing the string and applying the method. let _not_redundant = string.as_str().escape_unicode(); @@ -14,7 +18,7 @@ fn main() { let _not_redundant = string.as_str().split_whitespace(); // These methods don't use `as_str` and are applied on a `str` directly. - let borrowed_str = "Hello, world"! - let _no_as_str = borrowed_str.as_bytes(); - let _no_as_str = borrowed_str.is_empty(); + let borrowed_str = "Hello, world!"; + let _is_str = borrowed_str.as_bytes(); + let _is_str = borrowed_str.is_empty(); } diff --git a/tests/ui/redundant_as_str.stderr b/tests/ui/redundant_as_str.stderr index e7da34650f2cf..0ea42a94a81ed 100644 --- a/tests/ui/redundant_as_str.stderr +++ b/tests/ui/redundant_as_str.stderr @@ -1,8 +1,17 @@ -error: expected one of `.`, `;`, `?`, `else`, or an operator, found `!` - --> $DIR/redundant_as_str.rs:17:35 +error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too + --> $DIR/redundant_as_str.rs:7:29 | -LL | let borrowed_str = "Hello, world"! - | ^ expected one of `.`, `;`, `?`, `else`, or an operator +LL | let _redundant = string.as_str().as_bytes(); + | ^^^^^^^^^^^^^^^^^ help: try: `as_bytes` + | + = note: `-D clippy::redundant-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_as_str)]` + +error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too + --> $DIR/redundant_as_str.rs:8:29 + | +LL | let _redundant = string.as_str().is_empty(); + | ^^^^^^^^^^^^^^^^^ help: try: `is_empty` -error: aborting due to previous error +error: aborting due to 2 previous errors From 522485343466bf81b9dedcb340d24f5287669c28 Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:37:27 -0400 Subject: [PATCH 053/149] Remove periods from end of sentences in redundant_as_str comments --- tests/ui/redundant_as_str.fixed | 8 ++++---- tests/ui/redundant_as_str.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed index a95ab2d25a2c6..c4962e7e00002 100644 --- a/tests/ui/redundant_as_str.fixed +++ b/tests/ui/redundant_as_str.fixed @@ -3,21 +3,21 @@ fn main() { let string = "Hello, world!".to_owned(); - // These methods are redundant and the `as_str` can be removed. + // These methods are redundant and the `as_str` can be removed let _redundant = string.as_bytes(); let _redundant = string.is_empty(); - // These methods don't use `as_str` when they are redundant. + // These methods don't use `as_str` when they are redundant let _no_as_str = string.as_bytes(); let _no_as_str = string.is_empty(); // These methods are not redundant, and are equivelant to - // doing dereferencing the string and applying the method. + // doing dereferencing the string and applying the method let _not_redundant = string.as_str().escape_unicode(); let _not_redundant = string.as_str().trim(); let _not_redundant = string.as_str().split_whitespace(); - // These methods don't use `as_str` and are applied on a `str` directly. + // These methods don't use `as_str` and are applied on a `str` directly let borrowed_str = "Hello, world!"; let _is_str = borrowed_str.as_bytes(); let _is_str = borrowed_str.is_empty(); diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index d5ccfe2effeff..7357d7f0eeaf0 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -3,21 +3,21 @@ fn main() { let string = "Hello, world!".to_owned(); - // These methods are redundant and the `as_str` can be removed. + // These methods are redundant and the `as_str` can be removed let _redundant = string.as_str().as_bytes(); let _redundant = string.as_str().is_empty(); - // These methods don't use `as_str` when they are redundant. + // These methods don't use `as_str` when they are redundant let _no_as_str = string.as_bytes(); let _no_as_str = string.is_empty(); // These methods are not redundant, and are equivelant to - // doing dereferencing the string and applying the method. + // doing dereferencing the string and applying the method let _not_redundant = string.as_str().escape_unicode(); let _not_redundant = string.as_str().trim(); let _not_redundant = string.as_str().split_whitespace(); - // These methods don't use `as_str` and are applied on a `str` directly. + // These methods don't use `as_str` and are applied on a `str` directly let borrowed_str = "Hello, world!"; let _is_str = borrowed_str.as_bytes(); let _is_str = borrowed_str.is_empty(); From 17d174d113fda8a21680a103d062d6fd589b4dc7 Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:39:09 -0400 Subject: [PATCH 054/149] Reformat redundant_as_str ui test --- tests/ui/redundant_as_str.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index 7357d7f0eeaf0..33adb60999641 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -1,9 +1,9 @@ #![warn(clippy::redundant_as_str)] fn main() { - let string = "Hello, world!".to_owned(); + let string = "Hello, world!".to_owned(); - // These methods are redundant and the `as_str` can be removed + // These methods are redundant and the `as_str` can be removed let _redundant = string.as_str().as_bytes(); let _redundant = string.as_str().is_empty(); @@ -11,14 +11,14 @@ fn main() { let _no_as_str = string.as_bytes(); let _no_as_str = string.is_empty(); - // These methods are not redundant, and are equivelant to - // doing dereferencing the string and applying the method + // These methods are not redundant, and are equivelant to + // doing dereferencing the string and applying the method let _not_redundant = string.as_str().escape_unicode(); let _not_redundant = string.as_str().trim(); let _not_redundant = string.as_str().split_whitespace(); - // These methods don't use `as_str` and are applied on a `str` directly - let borrowed_str = "Hello, world!"; + // These methods don't use `as_str` and are applied on a `str` directly + let borrowed_str = "Hello, world!"; let _is_str = borrowed_str.as_bytes(); let _is_str = borrowed_str.is_empty(); } From d9d25e98fca4085cfe062700f6ca465abc20a52d Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:46:00 -0400 Subject: [PATCH 055/149] Fix redundant_as_str .fixed file not being consistent --- tests/ui/redundant_as_str.fixed | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed index c4962e7e00002..a38523a7c79e7 100644 --- a/tests/ui/redundant_as_str.fixed +++ b/tests/ui/redundant_as_str.fixed @@ -1,9 +1,9 @@ #![warn(clippy::redundant_as_str)] fn main() { - let string = "Hello, world!".to_owned(); + let string = "Hello, world!".to_owned(); - // These methods are redundant and the `as_str` can be removed + // These methods are redundant and the `as_str` can be removed let _redundant = string.as_bytes(); let _redundant = string.is_empty(); @@ -11,14 +11,14 @@ fn main() { let _no_as_str = string.as_bytes(); let _no_as_str = string.is_empty(); - // These methods are not redundant, and are equivelant to - // doing dereferencing the string and applying the method + // These methods are not redundant, and are equivelant to + // doing dereferencing the string and applying the method let _not_redundant = string.as_str().escape_unicode(); let _not_redundant = string.as_str().trim(); let _not_redundant = string.as_str().split_whitespace(); - // These methods don't use `as_str` and are applied on a `str` directly - let borrowed_str = "Hello, world!"; + // These methods don't use `as_str` and are applied on a `str` directly + let borrowed_str = "Hello, world!"; let _is_str = borrowed_str.as_bytes(); let _is_str = borrowed_str.is_empty(); } From b30cefc775e92892f8d49a649bb09510bbbb79af Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 Sep 2023 09:36:22 +0200 Subject: [PATCH 056/149] move ConstValue into mir this way we have mir::ConstValue and ty::ValTree as reasonably parallel --- clippy_utils/src/consts.rs | 2 +- clippy_utils/src/ty.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index fcb90c63a6fc6..c88fce22f7fb6 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -656,7 +656,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'tcx>) -> Option> { - use rustc_middle::mir::interpret::ConstValue; + use rustc_middle::mir::ConstValue; match result { mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index f0b4ede35fbc5..9e25d97f5a6bc 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,7 @@ use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; -use rustc_middle::mir::interpret::{ConstValue, Scalar}; +use rustc_middle::mir::{ConstValue, interpret::Scalar}; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ From 1b3e5dd0fcae6056e8ab435b795eb4d73f69230d Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 17 Jul 2023 00:30:24 -0400 Subject: [PATCH 057/149] Change default configuration of `undocumented_unsafe_blocks` This patch sets the two configuration options for `undocumented_unsafe_blocks` to `true` by default: these are `accept-comment-above-statement` and `accept-comment-above-attributes`. Having these values `false` by default prevents what many users would consider clean code, e.g. placing the `// SAFETY:` comment above a single-line functino call, rather than directly next to the argument. changelog: [`undocumented_unsafe_blocks`]: set `accept-comment-above-statement` and `accept-comment-above-attributes` to `true` by default. --- book/src/lint_configuration.md | 4 +- clippy_lints/src/utils/conf.rs | 4 +- .../undocumented_unsafe_blocks/clippy.toml | 2 - .../default/clippy.toml | 2 + .../disabled/clippy.toml | 3 + ...undocumented_unsafe_blocks.default.stderr} | 74 +-- ...ndocumented_unsafe_blocks.disabled.stderr} | 132 +++-- .../undocumented_unsafe_blocks.rs | 30 +- tests/ui/undocumented_unsafe_blocks.rs | 534 ------------------ 9 files changed, 153 insertions(+), 632 deletions(-) delete mode 100644 tests/ui-toml/undocumented_unsafe_blocks/clippy.toml create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml rename tests/ui-toml/undocumented_unsafe_blocks/{undocumented_unsafe_blocks.stderr => undocumented_unsafe_blocks.default.stderr} (83%) rename tests/{ui/undocumented_unsafe_blocks.stderr => ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr} (74%) delete mode 100644 tests/ui/undocumented_unsafe_blocks.rs diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 3aeb6206fca93..b980083f1f52a 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -703,7 +703,7 @@ Minimum chars an ident can have, anything below or equal to this will be linted. ## `accept-comment-above-statement` Whether to accept a safety comment to be placed above the statement containing the `unsafe` block -**Default Value:** `false` (`bool`) +**Default Value:** `true` (`bool`) --- **Affected lints:** @@ -713,7 +713,7 @@ Whether to accept a safety comment to be placed above the statement containing t ## `accept-comment-above-attributes` Whether to accept a safety comment to be placed above the attributes for the `unsafe` block -**Default Value:** `false` (`bool`) +**Default Value:** `true` (`bool`) --- **Affected lints:** diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index e1280ef38ef71..75c3c7a958a21 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -542,11 +542,11 @@ define_Conf! { /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. /// /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block - (accept_comment_above_statement: bool = false), + (accept_comment_above_statement: bool = true), /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. /// /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block - (accept_comment_above_attributes: bool = false), + (accept_comment_above_attributes: bool = true), /// Lint: UNNECESSARY_RAW_STRING_HASHES. /// /// Whether to allow `r#""#` when `r""` can be used diff --git a/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml b/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml deleted file mode 100644 index e6dbb3d37841b..0000000000000 --- a/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml +++ /dev/null @@ -1,2 +0,0 @@ -accept-comment-above-statement = true -accept-comment-above-attributes = true diff --git a/tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml b/tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml new file mode 100644 index 0000000000000..3b205d536f2ed --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml @@ -0,0 +1,2 @@ +# default configuration has `accept-comment-above-statement` and +# `accept-comment-above-attributes` true diff --git a/tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml b/tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml new file mode 100644 index 0000000000000..57ecb902d6545 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml @@ -0,0 +1,3 @@ +# test with these options disabled +accept-comment-above-statement = false +accept-comment-above-attributes = false diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr similarity index 83% rename from tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr rename to tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr index 183c07fe786ce..15edf2a7dae47 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:263:19 + --> $DIR/undocumented_unsafe_blocks.rs:266:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -9,7 +9,7 @@ LL | /* Safety: */ unsafe {} = help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]` error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:267:5 + --> $DIR/undocumented_unsafe_blocks.rs:270:5 | LL | unsafe {} | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:271:14 + --> $DIR/undocumented_unsafe_blocks.rs:274:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:271:29 + --> $DIR/undocumented_unsafe_blocks.rs:274:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:271:48 + --> $DIR/undocumented_unsafe_blocks.rs:274:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:275:18 + --> $DIR/undocumented_unsafe_blocks.rs:278:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -49,7 +49,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:275:37 + --> $DIR/undocumented_unsafe_blocks.rs:278:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -57,7 +57,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:279:14 + --> $DIR/undocumented_unsafe_blocks.rs:282:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:284:19 + --> $DIR/undocumented_unsafe_blocks.rs:287:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -73,7 +73,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:290:14 + --> $DIR/undocumented_unsafe_blocks.rs:293:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -81,7 +81,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:294:14 + --> $DIR/undocumented_unsafe_blocks.rs:297:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:298:13 + --> $DIR/undocumented_unsafe_blocks.rs:301:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -97,7 +97,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:308:8 + --> $DIR/undocumented_unsafe_blocks.rs:311:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -105,7 +105,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:314:13 + --> $DIR/undocumented_unsafe_blocks.rs:317:13 | LL | unsafe {} | ^^^^^^^^^ @@ -117,7 +117,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:322:5 + --> $DIR/undocumented_unsafe_blocks.rs:325:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -125,7 +125,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:326:5 + --> $DIR/undocumented_unsafe_blocks.rs:329:5 | LL | unsafe { | ^^^^^^^^ @@ -133,7 +133,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:336:5 + --> $DIR/undocumented_unsafe_blocks.rs:339:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -141,7 +141,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:340:20 + --> $DIR/undocumented_unsafe_blocks.rs:343:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:347:5 + --> $DIR/undocumented_unsafe_blocks.rs:350:5 | LL | unsafe impl A for () {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL | unsafe impl A for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:354:9 + --> $DIR/undocumented_unsafe_blocks.rs:357:9 | LL | unsafe impl B for (u32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | unsafe impl B for (u32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:375:13 + --> $DIR/undocumented_unsafe_blocks.rs:378:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:400:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:408:5 + --> $DIR/undocumented_unsafe_blocks.rs:411:5 | LL | unsafe impl T for (i32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | unsafe impl T for (i32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:400:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +209,7 @@ LL | no_safety_comment!(u32); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:414:5 + --> $DIR/undocumented_unsafe_blocks.rs:417:5 | LL | unsafe impl T for (bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL | unsafe impl T for (bool) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:460:5 + --> $DIR/undocumented_unsafe_blocks.rs:463:5 | LL | unsafe impl NoComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -225,7 +225,7 @@ LL | unsafe impl NoComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:464:19 + --> $DIR/undocumented_unsafe_blocks.rs:467:19 | LL | /* SAFETY: */ unsafe impl InlineComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:468:5 + --> $DIR/undocumented_unsafe_blocks.rs:471:5 | LL | unsafe impl TrailingComment for () {} // SAFETY: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,13 +241,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: = help: consider adding a safety comment on the preceding line error: constant item has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:472:5 + --> $DIR/undocumented_unsafe_blocks.rs:475:5 | LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:471:5 + --> $DIR/undocumented_unsafe_blocks.rs:474:5 | LL | // SAFETY: | ^^^^^^^^^^ @@ -255,7 +255,7 @@ LL | // SAFETY: = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:473:5 + --> $DIR/undocumented_unsafe_blocks.rs:476:5 | LL | unsafe impl Interference for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | unsafe impl Interference for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:480:5 + --> $DIR/undocumented_unsafe_blocks.rs:483:5 | LL | unsafe impl ImplInFn for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | unsafe impl ImplInFn for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:489:1 + --> $DIR/undocumented_unsafe_blocks.rs:492:1 | LL | unsafe impl CrateRoot for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,7 +279,7 @@ LL | unsafe impl CrateRoot for () {} = help: consider adding a safety comment on the preceding line error: statement has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:502:5 + --> $DIR/undocumented_unsafe_blocks.rs:505:5 | LL | / let _ = { LL | | if unsafe { true } { @@ -291,13 +291,13 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:501:5 + --> $DIR/undocumented_unsafe_blocks.rs:504:5 | LL | // SAFETY: this is more than one level away, so it should warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:503:12 + --> $DIR/undocumented_unsafe_blocks.rs:506:12 | LL | if unsafe { true } { | ^^^^^^^^^^^^^^^ @@ -305,7 +305,7 @@ LL | if unsafe { true } { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:506:23 + --> $DIR/undocumented_unsafe_blocks.rs:509:23 | LL | let bar = unsafe {}; | ^^^^^^^^^ diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr similarity index 74% rename from tests/ui/undocumented_unsafe_blocks.stderr rename to tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index 77f6aea2e0d07..cc9530f79b67c 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:262:19 + --> $DIR/undocumented_unsafe_blocks.rs:266:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -9,7 +9,7 @@ LL | /* Safety: */ unsafe {} = help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]` error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:266:5 + --> $DIR/undocumented_unsafe_blocks.rs:270:5 | LL | unsafe {} | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:14 + --> $DIR/undocumented_unsafe_blocks.rs:274:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:29 + --> $DIR/undocumented_unsafe_blocks.rs:274:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:48 + --> $DIR/undocumented_unsafe_blocks.rs:274:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:274:18 + --> $DIR/undocumented_unsafe_blocks.rs:278:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -49,7 +49,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:274:37 + --> $DIR/undocumented_unsafe_blocks.rs:278:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -57,7 +57,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:278:14 + --> $DIR/undocumented_unsafe_blocks.rs:282:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:283:19 + --> $DIR/undocumented_unsafe_blocks.rs:287:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -73,7 +73,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:289:14 + --> $DIR/undocumented_unsafe_blocks.rs:293:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -81,7 +81,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:293:14 + --> $DIR/undocumented_unsafe_blocks.rs:297:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:297:13 + --> $DIR/undocumented_unsafe_blocks.rs:301:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -97,7 +97,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:307:8 + --> $DIR/undocumented_unsafe_blocks.rs:311:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -105,7 +105,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:313:13 + --> $DIR/undocumented_unsafe_blocks.rs:317:13 | LL | unsafe {} | ^^^^^^^^^ @@ -117,7 +117,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:321:5 + --> $DIR/undocumented_unsafe_blocks.rs:325:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -125,7 +125,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:325:5 + --> $DIR/undocumented_unsafe_blocks.rs:329:5 | LL | unsafe { | ^^^^^^^^ @@ -133,7 +133,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:335:5 + --> $DIR/undocumented_unsafe_blocks.rs:339:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -141,7 +141,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:339:20 + --> $DIR/undocumented_unsafe_blocks.rs:343:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:346:5 + --> $DIR/undocumented_unsafe_blocks.rs:350:5 | LL | unsafe impl A for () {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL | unsafe impl A for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:353:9 + --> $DIR/undocumented_unsafe_blocks.rs:357:9 | LL | unsafe impl B for (u32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | unsafe impl B for (u32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:374:13 + --> $DIR/undocumented_unsafe_blocks.rs:378:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:399:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:407:5 + --> $DIR/undocumented_unsafe_blocks.rs:411:5 | LL | unsafe impl T for (i32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | unsafe impl T for (i32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:399:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +209,7 @@ LL | no_safety_comment!(u32); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:413:5 + --> $DIR/undocumented_unsafe_blocks.rs:417:5 | LL | unsafe impl T for (bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL | unsafe impl T for (bool) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:459:5 + --> $DIR/undocumented_unsafe_blocks.rs:463:5 | LL | unsafe impl NoComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -225,7 +225,7 @@ LL | unsafe impl NoComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:463:19 + --> $DIR/undocumented_unsafe_blocks.rs:467:19 | LL | /* SAFETY: */ unsafe impl InlineComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:467:5 + --> $DIR/undocumented_unsafe_blocks.rs:471:5 | LL | unsafe impl TrailingComment for () {} // SAFETY: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,13 +241,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: = help: consider adding a safety comment on the preceding line error: constant item has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:471:5 + --> $DIR/undocumented_unsafe_blocks.rs:475:5 | LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:470:5 + --> $DIR/undocumented_unsafe_blocks.rs:474:5 | LL | // SAFETY: | ^^^^^^^^^^ @@ -255,7 +255,7 @@ LL | // SAFETY: = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:472:5 + --> $DIR/undocumented_unsafe_blocks.rs:476:5 | LL | unsafe impl Interference for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | unsafe impl Interference for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:479:5 + --> $DIR/undocumented_unsafe_blocks.rs:483:5 | LL | unsafe impl ImplInFn for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | unsafe impl ImplInFn for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:488:1 + --> $DIR/undocumented_unsafe_blocks.rs:492:1 | LL | unsafe impl CrateRoot for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,7 +279,7 @@ LL | unsafe impl CrateRoot for () {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:498:9 + --> $DIR/undocumented_unsafe_blocks.rs:502:9 | LL | unsafe {}; | ^^^^^^^^^ @@ -287,7 +287,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: statement has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:501:5 + --> $DIR/undocumented_unsafe_blocks.rs:505:5 | LL | / let _ = { LL | | if unsafe { true } { @@ -299,13 +299,13 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:500:5 + --> $DIR/undocumented_unsafe_blocks.rs:504:5 | LL | // SAFETY: this is more than one level away, so it should warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:502:12 + --> $DIR/undocumented_unsafe_blocks.rs:506:12 | LL | if unsafe { true } { | ^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL | if unsafe { true } { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:505:23 + --> $DIR/undocumented_unsafe_blocks.rs:509:23 | LL | let bar = unsafe {}; | ^^^^^^^^^ @@ -321,7 +321,7 @@ LL | let bar = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:523:9 + --> $DIR/undocumented_unsafe_blocks.rs:527:9 | LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -329,7 +329,7 @@ LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:527:9 + --> $DIR/undocumented_unsafe_blocks.rs:531:9 | LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -337,12 +337,60 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:531:9 + --> $DIR/undocumented_unsafe_blocks.rs:535:9 + | +LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:541:5 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:545:5 + | +LL | unsafe { + | ^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:552:9 + | +LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:557:9 + | +LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:563:9 | LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: aborting due to 39 previous errors +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:568:5 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 45 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index b28e1b7d1802d..a278139876064 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -1,4 +1,7 @@ //@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled #![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] #![allow(deref_nullptr, clippy::let_unit_value, clippy::missing_safety_doc)] @@ -491,7 +494,7 @@ unsafe impl CrateRoot for () {} // SAFETY: ok unsafe impl CrateRoot for (i32) {} -fn issue_9142() { +fn nested_block_separation_issue_9142() { // SAFETY: ok let _ = // we need this comment to avoid rustfmt putting @@ -518,49 +521,50 @@ pub const unsafe fn a_const_function_with_a_very_long_name_to_break_the_line() - 2 } -fn issue_10832() { - // Safety: A safety comment +fn separate_line_from_let_issue_10832() { + // SAFETY: fail ONLY if `accept-comment-above-statement = false` let _some_variable_with_a_very_long_name_to_break_the_line = unsafe { a_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Another safety comment + // SAFETY: fail ONLY if `accept-comment-above-statement = false` const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Yet another safety comment + // SAFETY: fail ONLY if `accept-comment-above-statement = false` static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; } -fn issue_8679() { - // SAFETY: +fn above_expr_attribute_issue_8679() { + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] unsafe {} - // SAFETY: + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[expect(unsafe_code, reason = "totally safe")] unsafe { *std::ptr::null::() }; - // Safety: A safety comment + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] let _some_variable_with_a_very_long_name_to_break_the_line = unsafe { a_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Another safety comment + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Yet another safety comment + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] - static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = + #[allow(non_upper_case_globals)] + static _some_static_with_a_very_long_name_to_break_the_line: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; // SAFETY: #[allow(unsafe_code)] - // This also works I guess + // This shouldn't work either unsafe {} } diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs deleted file mode 100644 index f4e7f1943ae6d..0000000000000 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ /dev/null @@ -1,534 +0,0 @@ -//@aux-build:proc_macro_unsafe.rs - -#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] -#![allow(clippy::let_unit_value, clippy::missing_safety_doc)] - -extern crate proc_macro_unsafe; - -// Valid comments - -fn nested_local() { - let _ = { - let _ = { - // SAFETY: - let _ = unsafe {}; - }; - }; -} - -fn deep_nest() { - let _ = { - let _ = { - // SAFETY: - let _ = unsafe {}; - - // Safety: - unsafe {}; - - let _ = { - let _ = { - let _ = { - let _ = { - let _ = { - // Safety: - let _ = unsafe {}; - - // SAFETY: - unsafe {}; - }; - }; - }; - - // Safety: - unsafe {}; - }; - }; - }; - - // Safety: - unsafe {}; - }; - - // SAFETY: - unsafe {}; -} - -fn local_tuple_expression() { - // Safety: - let _ = (42, unsafe {}); -} - -fn line_comment() { - // Safety: - unsafe {} -} - -fn line_comment_newlines() { - // SAFETY: - - unsafe {} -} - -fn line_comment_empty() { - // Safety: - // - // - // - unsafe {} -} - -fn line_comment_with_extras() { - // This is a description - // Safety: - unsafe {} -} - -fn block_comment() { - /* Safety: */ - unsafe {} -} - -fn block_comment_newlines() { - /* SAFETY: */ - - unsafe {} -} - -fn block_comment_with_extras() { - /* This is a description - * SAFETY: - */ - unsafe {} -} - -fn block_comment_terminator_same_line() { - /* This is a description - * Safety: */ - unsafe {} -} - -fn buried_safety() { - // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation - // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in - // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint - // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est - // laborum. Safety: - // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi - // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio - // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl - // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. - unsafe {} -} - -fn safety_with_prepended_text() { - // This is a test. safety: - unsafe {} -} - -fn local_line_comment() { - // Safety: - let _ = unsafe {}; -} - -fn local_block_comment() { - /* SAFETY: */ - let _ = unsafe {}; -} - -fn comment_array() { - // Safety: - let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; -} - -fn comment_tuple() { - // sAFETY: - let _ = (42, unsafe {}, "test", unsafe {}); -} - -fn comment_unary() { - // SAFETY: - let _ = *unsafe { &42 }; -} - -#[allow(clippy::match_single_binding)] -fn comment_match() { - // SAFETY: - let _ = match unsafe {} { - _ => {}, - }; -} - -fn comment_addr_of() { - // Safety: - let _ = &unsafe {}; -} - -fn comment_repeat() { - // Safety: - let _ = [unsafe {}; 5]; -} - -fn comment_macro_call() { - macro_rules! t { - ($b:expr) => { - $b - }; - } - - t!( - // SAFETY: - unsafe {} - ); -} - -fn comment_macro_def() { - macro_rules! t { - () => { - // Safety: - unsafe {} - }; - } - - t!(); -} - -fn non_ascii_comment() { - // ॐ᧻໒ SaFeTy: ௵∰ - unsafe {}; -} - -fn local_commented_block() { - let _ = - // safety: - unsafe {}; -} - -fn local_nest() { - // safety: - let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})]; -} - -fn in_fn_call(x: *const u32) { - fn f(x: u32) {} - - // Safety: reason - f(unsafe { *x }); -} - -fn multi_in_fn_call(x: *const u32) { - fn f(x: u32, y: u32) {} - - // Safety: reason - f(unsafe { *x }, unsafe { *x }); -} - -fn in_multiline_fn_call(x: *const u32) { - fn f(x: u32, y: u32) {} - - f( - // Safety: reason - unsafe { *x }, - 0, - ); -} - -fn in_macro_call(x: *const u32) { - // Safety: reason - println!("{}", unsafe { *x }); -} - -fn in_multiline_macro_call(x: *const u32) { - println!( - "{}", - // Safety: reason - unsafe { *x }, - ); -} - -fn from_proc_macro() { - proc_macro_unsafe::unsafe_block!(token); -} - -fn in_closure(x: *const u32) { - // Safety: reason - let _ = || unsafe { *x }; -} - -// Invalid comments - -#[rustfmt::skip] -fn inline_block_comment() { - /* Safety: */ unsafe {} -} - -fn no_comment() { - unsafe {} -} - -fn no_comment_array() { - let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; -} - -fn no_comment_tuple() { - let _ = (42, unsafe {}, "test", unsafe {}); -} - -fn no_comment_unary() { - let _ = *unsafe { &42 }; -} - -#[allow(clippy::match_single_binding)] -fn no_comment_match() { - let _ = match unsafe {} { - _ => {}, - }; -} - -fn no_comment_addr_of() { - let _ = &unsafe {}; -} - -fn no_comment_repeat() { - let _ = [unsafe {}; 5]; -} - -fn local_no_comment() { - let _ = unsafe {}; -} - -fn no_comment_macro_call() { - macro_rules! t { - ($b:expr) => { - $b - }; - } - - t!(unsafe {}); -} - -fn no_comment_macro_def() { - macro_rules! t { - () => { - unsafe {} - }; - } - - t!(); -} - -fn trailing_comment() { - unsafe {} // SAFETY: -} - -fn internal_comment() { - unsafe { - // SAFETY: - } -} - -fn interference() { - // SAFETY - - let _ = 42; - - unsafe {}; -} - -pub fn print_binary_tree() { - println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); -} - -mod unsafe_impl_smoke_test { - unsafe trait A {} - - // error: no safety comment - unsafe impl A for () {} - - // Safety: ok - unsafe impl A for (i32) {} - - mod sub_mod { - // error: - unsafe impl B for (u32) {} - unsafe trait B {} - } - - #[rustfmt::skip] - mod sub_mod2 { - // - // SAFETY: ok - // - - unsafe impl B for (u32) {} - unsafe trait B {} - } -} - -mod unsafe_impl_from_macro { - unsafe trait T {} - - // error - macro_rules! no_safety_comment { - ($t:ty) => { - unsafe impl T for $t {} - }; - } - - // ok - no_safety_comment!(()); - - // ok - macro_rules! with_safety_comment { - ($t:ty) => { - // SAFETY: - unsafe impl T for $t {} - }; - } - - // ok - with_safety_comment!((i32)); -} - -mod unsafe_impl_macro_and_not_macro { - unsafe trait T {} - - // error - macro_rules! no_safety_comment { - ($t:ty) => { - unsafe impl T for $t {} - }; - } - - // ok - no_safety_comment!(()); - - // error - unsafe impl T for (i32) {} - - // ok - no_safety_comment!(u32); - - // error - unsafe impl T for (bool) {} -} - -#[rustfmt::skip] -mod unsafe_impl_valid_comment { - unsafe trait SaFety {} - // SaFety: - unsafe impl SaFety for () {} - - unsafe trait MultiLineComment {} - // The following impl is safe - // ... - // Safety: reason - unsafe impl MultiLineComment for () {} - - unsafe trait NoAscii {} - // 安全 SAFETY: 以下のコードは安全です - unsafe impl NoAscii for () {} - - unsafe trait InlineAndPrecedingComment {} - // SAFETY: - /* comment */ unsafe impl InlineAndPrecedingComment for () {} - - unsafe trait BuriedSafety {} - // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation - // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in - // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint - // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est - // laborum. Safety: - // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi - // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio - // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl - // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. - unsafe impl BuriedSafety for () {} - - unsafe trait MultiLineBlockComment {} - /* This is a description - * Safety: */ - unsafe impl MultiLineBlockComment for () {} -} - -#[rustfmt::skip] -mod unsafe_impl_invalid_comment { - unsafe trait NoComment {} - - unsafe impl NoComment for () {} - - unsafe trait InlineComment {} - - /* SAFETY: */ unsafe impl InlineComment for () {} - - unsafe trait TrailingComment {} - - unsafe impl TrailingComment for () {} // SAFETY: - - unsafe trait Interference {} - // SAFETY: - const BIG_NUMBER: i32 = 1000000; - unsafe impl Interference for () {} -} - -unsafe trait ImplInFn {} - -fn impl_in_fn() { - // error - unsafe impl ImplInFn for () {} - - // SAFETY: ok - unsafe impl ImplInFn for (i32) {} -} - -unsafe trait CrateRoot {} - -// error -unsafe impl CrateRoot for () {} - -// SAFETY: ok -unsafe impl CrateRoot for (i32) {} - -fn issue_9142() { - // SAFETY: ok - let _ = - // we need this comment to avoid rustfmt putting - // it all on one line - unsafe {}; - - // SAFETY: this is more than one level away, so it should warn - let _ = { - if unsafe { true } { - todo!(); - } else { - let bar = unsafe {}; - todo!(); - bar - } - }; -} - -pub unsafe fn a_function_with_a_very_long_name_to_break_the_line() -> u32 { - 1 -} - -pub const unsafe fn a_const_function_with_a_very_long_name_to_break_the_line() -> u32 { - 2 -} - -fn issue_10832() { - // Safety: A safety comment. But it will warn anyways - let _some_variable_with_a_very_long_name_to_break_the_line = - unsafe { a_function_with_a_very_long_name_to_break_the_line() }; - - // Safety: Another safety comment. But it will warn anyways - const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = - unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; - - // Safety: Yet another safety comment. But it will warn anyways - static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = - unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; -} - -fn main() {} From 01c25a8eb63f8da0453d1aedca782f477342ef2d Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 20 Sep 2023 13:41:20 +0000 Subject: [PATCH 058/149] Remove most usage of `hir_ty_to_ty` --- .../src/default_union_representation.rs | 22 ++++++++++--------- clippy_lints/src/error_impl_error.rs | 8 +++---- clippy_lints/src/large_const_arrays.rs | 5 ++--- clippy_lints/src/loops/utils.rs | 3 +-- clippy_lints/src/matches/needless_match.rs | 17 ++++++++------ clippy_lints/src/methods/mod.rs | 13 ++++++----- clippy_lints/src/missing_const_for_fn.rs | 12 +++++----- clippy_lints/src/no_effect.rs | 11 ++++------ clippy_lints/src/non_canonical_impls.rs | 14 +++--------- clippy_lints/src/non_copy_const.rs | 15 ++++++------- clippy_lints/src/ptr.rs | 21 ++++++------------ .../src/utils/internal_lints/invalid_paths.rs | 18 +++++---------- .../utils/internal_lints/msrv_attr_impl.rs | 15 +++++-------- tests/ui/no_effect_return.rs | 1 + tests/ui/no_effect_return.stderr | 8 +++---- 15 files changed, 78 insertions(+), 105 deletions(-) diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index bbce6e1dd8f2b..63ec819502088 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -1,8 +1,8 @@ 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_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, FieldDef, GenericArg, List}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -52,7 +52,10 @@ declare_lint_pass!(DefaultUnionRepresentation => [DEFAULT_UNION_REPRESENTATION]) impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if is_union_with_two_non_zst_fields(cx, item) && !has_c_repr_attr(cx, item.hir_id()) { + if !item.span.from_expansion() + && is_union_with_two_non_zst_fields(cx, item) + && !has_c_repr_attr(cx, item.hir_id()) + { span_lint_and_help( cx, DEFAULT_UNION_REPRESENTATION, @@ -73,18 +76,17 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { /// if there is only one field left after ignoring ZST fields then the offset /// of that field does not matter either.) fn is_union_with_two_non_zst_fields(cx: &LateContext<'_>, item: &Item<'_>) -> bool { - if let ItemKind::Union(data, _) = &item.kind { - data.fields().iter().filter(|f| !is_zst(cx, f.ty)).count() >= 2 + if let ItemKind::Union(..) = &item.kind + && let ty::Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind() + { + adt_def.all_fields().filter(|f| !is_zst(cx, f, args)).count() >= 2 } else { false } } -fn is_zst(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) -> bool { - if hir_ty.span.from_expansion() { - return false; - } - let ty = hir_ty_to_ty(cx.tcx, hir_ty); +fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: &'tcx List>) -> bool { + let ty = field.ty(cx.tcx, args); if let Ok(layout) = cx.layout_of(ty) { layout.is_zst() } else { diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs index 379af9b2234c2..f24577c738229 100644 --- a/clippy_lints/src/error_impl_error.rs +++ b/clippy_lints/src/error_impl_error.rs @@ -3,7 +3,6 @@ use clippy_utils::path_res; use clippy_utils::ty::implements_trait; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{Item, ItemKind}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Visibility; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -42,9 +41,10 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { }; match item.kind { - ItemKind::TyAlias(ty, _) if implements_trait(cx, hir_ty_to_ty(cx.tcx, ty), error_def_id, &[]) - && item.ident.name == sym::Error - && is_visible_outside_module(cx, item.owner_id.def_id) => + ItemKind::TyAlias(..) if item.ident.name == sym::Error + && is_visible_outside_module(cx, item.owner_id.def_id) + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && implements_trait(cx, ty, error_def_id, &[]) => { span_lint( cx, diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 9b26c3573e18f..a4f3d49834531 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -2,7 +2,6 @@ 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}; @@ -50,12 +49,12 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { if !item.span.from_expansion(); - if let ItemKind::Const(hir_ty, generics, _) = &item.kind; + if let ItemKind::Const(_, generics, _) = &item.kind; // Since static items may not have generics, skip generic const items. // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it // doesn't account for empty where-clauses that only consist of keyword `where` IINM. if generics.params.is_empty() && !generics.has_where_clause_predicates; - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); if let ty::Array(element_type, cst) = ty.kind(); if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); if let Ok(element_count) = element_count.try_to_target_usize(cx.tcx); diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 6edca2d55f649..0a2bd89eb3cd3 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -5,7 +5,6 @@ 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}; @@ -150,7 +149,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if l.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = l.pat.kind; then { - let ty = l.ty.map(|ty| hir_ty_to_ty(self.cx.tcx, ty)); + let ty = l.ty.map(|_| self.cx.typeck_results().pat_ty(l.pat)); self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| { InitializeVisitorState::Initialized { diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index c4f6852aedc3d..44dc29c36a6ba 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -8,8 +8,7 @@ use clippy_utils::{ }; 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_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, Guard, ItemKind, Node, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -141,11 +140,15 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` - Node::Item(..) => { - if let Some(fn_decl) = p_node.fn_decl() { - if let FnRetTy::Return(ret_ty) = fn_decl.output { - return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr)); - } + Node::Item(item) => { + if let ItemKind::Fn(..) = item.kind { + let output = cx + .tcx + .fn_sig(item.owner_id) + .instantiate_identity() + .output() + .skip_binder(); + return same_type_and_consts(output, cx.typeck_results().expr_ty(expr)); } }, // check the parent expr for this whole block `{ match match_expr {..} }` diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bd07e6cbf2e2c..e7fcef9e9de27 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -126,7 +126,6 @@ pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, 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}; @@ -3926,18 +3925,20 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if_chain! { if let TraitItemKind::Fn(ref sig, _) = item.kind; if sig.decl.implicit_self.has_implicit_self(); - if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); - + if let Some(first_arg_hir_ty) = sig.decl.inputs.first(); + if let Some(&first_arg_ty) = cx.tcx.fn_sig(item.owner_id) + .instantiate_identity() + .inputs() + .skip_binder() + .first(); then { - let first_arg_span = first_arg_ty.span; - let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); wrong_self_convention::check( cx, item.ident.name.as_str(), self_ty, first_arg_ty, - first_arg_span, + first_arg_hir_ty.span, false, true, ); diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 3b7eccad79df8..f598a65d2e488 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -7,7 +7,6 @@ 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}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -124,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { FnKind::Method(_, sig, ..) => { if trait_ref_of_method(cx, def_id).is_some() || already_const(sig.header) - || method_accepts_droppable(cx, sig.decl.inputs) + || method_accepts_droppable(cx, def_id) { return; } @@ -165,12 +164,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { /// Returns true if any of the method parameters is a type that implements `Drop`. The method /// can't be made const then, because `drop` can't be const-evaluated. -fn method_accepts_droppable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool { +fn method_accepts_droppable(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + // If any of the params are droppable, return true - param_tys.iter().any(|hir_ty| { - let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty); - has_drop(cx, ty_ty) - }) + sig.inputs().iter().any(|&ty| has_drop(cx, ty)) } // We don't have to lint on something that's already `const` diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 5f2a324b05fb9..aee184252fbfd 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -5,10 +5,8 @@ use clippy_utils::{get_parent_node, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, FnRetTy, ItemKind, Node, PatKind, Stmt, StmtKind, - UnsafeSource, + is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, ItemKind, Node, PatKind, Stmt, StmtKind, UnsafeSource, }; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -99,14 +97,13 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { |diag| { for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { if let Node::Item(item) = parent.1 - && let ItemKind::Fn(sig, ..) = item.kind - && let FnRetTy::Return(ret_ty) = sig.decl.output + && let ItemKind::Fn(..) = item.kind && let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) && let [.., final_stmt] = block.stmts && final_stmt.hir_id == stmt.hir_id { let expr_ty = cx.typeck_results().expr_ty(expr); - let mut ret_ty = hir_ty_to_ty(cx.tcx, ret_ty); + let mut ret_ty = cx.tcx.fn_sig(item.owner_id).instantiate_identity().output().skip_binder(); // Remove `impl Future` to get `T` if cx.tcx.ty_is_opaque_future(ret_ty) && @@ -115,7 +112,7 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { ret_ty = true_ret_ty; } - if ret_ty == expr_ty { + if !ret_ty.is_unit() && ret_ty == expr_ty { diag.span_suggestion( stmt.span.shrink_to_lo(), "did you mean to return it?", diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index c390e6913ffb0..20b4b4f03ed47 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -4,8 +4,7 @@ use clippy_utils::ty::implements_trait; use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, match_def_path, path_res, std_or_core}; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, ItemKind, LangItem, Node, UnOp}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::EarlyBinder; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -123,9 +122,6 @@ impl LateLintPass<'_> for NonCanonicalImpls { if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) { return; } - let ItemKind::Impl(imp) = item.kind else { - return; - }; let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else { return; }; @@ -181,12 +177,8 @@ impl LateLintPass<'_> for NonCanonicalImpls { if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id) && impl_item.ident.name == sym::partial_cmp - && let Some(ord_def_id) = cx - .tcx - .diagnostic_items(trait_impl.def_id.krate) - .name_to_id - .get(&sym::Ord) - && implements_trait(cx, hir_ty_to_ty(cx.tcx, imp.self_ty), *ord_def_id, &[]) + && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) { // If the `cmp` call likely needs to be fully qualified in the suggestion // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 243192385c256..e106a959165ef 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -13,7 +13,6 @@ 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::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId}; use rustc_middle::ty::adjustment::Adjust; @@ -297,8 +296,8 @@ declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTER impl<'tcx> LateLintPass<'tcx> for NonCopyConst { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { - if let ItemKind::Const(hir_ty, _generics, body_id) = it.kind { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + if let ItemKind::Const(.., body_id) = it.kind { + let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) { lint(cx, Source::Item { item: it.span }); } @@ -306,8 +305,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Const(hir_ty, body_id_opt) = &trait_item.kind { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + if let TraitItemKind::Const(_, body_id_opt) = &trait_item.kind { + let ty = cx.tcx.type_of(trait_item.owner_id).instantiate_identity(); // Normalize assoc types because ones originated from generic params // bounded other traits could have their bound. @@ -333,7 +332,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Const(hir_ty, body_id) = &impl_item.kind { + if let ImplItemKind::Const(_, body_id) = &impl_item.kind { let item_def_id = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id; let item = cx.tcx.hir().expect_item(item_def_id); @@ -366,7 +365,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // we should use here as a frozen variant is a potential to be frozen // similar to unknown layouts. // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); if is_unfrozen(cx, normalized); if is_value_unfrozen_poly(cx, *body_id, normalized); @@ -381,7 +380,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } }, ItemKind::Impl(Impl { of_trait: None, .. }) => { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); // Normalize assoc types originated from generic params. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index bf031ac84549d..7dabdcd58ec2c 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -16,7 +16,6 @@ use rustc_hir::{ ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, Unsafety, }; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_lint::{LateContext, LateLintPass}; @@ -172,13 +171,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { for arg in check_fn_args( cx, - cx.tcx - .fn_sig(item.owner_id) - .instantiate_identity() - .skip_binder() - .inputs(), + cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder(), sig.decl.inputs, - &sig.decl.output, &[], ) .filter(|arg| arg.mutability() == Mutability::Not) @@ -237,7 +231,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { let decl = sig.decl; let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder(); - let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, &decl.output, body.params) + let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params) .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not) .collect(); let results = check_ptr_arg_usage(cx, body, &lint_args); @@ -443,12 +437,13 @@ impl<'tcx> DerefTy<'tcx> { #[expect(clippy::too_many_lines)] fn check_fn_args<'cx, 'tcx: 'cx>( cx: &'cx LateContext<'tcx>, - tys: &'tcx [Ty<'tcx>], + fn_sig: ty::FnSig<'tcx>, hir_tys: &'tcx [hir::Ty<'tcx>], - ret_ty: &'tcx FnRetTy<'tcx>, params: &'tcx [Param<'tcx>], ) -> impl Iterator> + 'cx { - tys.iter() + fn_sig + .inputs() + .iter() .zip(hir_tys.iter()) .enumerate() .filter_map(move |(i, (ty, hir_ty))| { @@ -494,9 +489,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( }) { if !lifetime.is_anonymous() - && let FnRetTy::Return(ret_ty) = ret_ty - && let ret_ty = hir_ty_to_ty(cx.tcx, ret_ty) - && ret_ty + && fn_sig.output() .walk() .filter_map(|arg| { arg.as_region().and_then(|lifetime| { diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 4ed985f54d0e2..250772238853b 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -5,10 +5,9 @@ use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::Item; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, FloatTy}; +use rustc_middle::ty::FloatTy; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; @@ -34,25 +33,20 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); if_chain! { if mod_name.as_str() == "paths"; - if let hir::ItemKind::Const(ty, _, body_id) = item.kind; - let ty = hir_ty_to_ty(cx.tcx, ty); - if let ty::Array(el_ty, _) = &ty.kind(); - if let ty::Ref(_, el_ty, _) = &el_ty.kind(); - if el_ty.is_str(); + if let hir::ItemKind::Const(.., body_id) = item.kind; let body = cx.tcx.hir().body(body_id); let typeck_results = cx.tcx.typeck_body(body_id); if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); - let path: Vec<&str> = path + if let Some(path) = path .iter() .map(|x| { if let Constant::Str(s) = x { - s.as_str() + Some(s.as_str()) } else { - // We checked the type of the constant above - unreachable!() + None } }) - .collect(); + .collect::>>(); if !check_path(cx, &path[..]); then { span_lint(cx, INVALID_PATHS, item.span, "invalid path"); diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index bf835f89cfc7f..86b77a77f1730 100644 --- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -5,9 +5,8 @@ use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{self, GenericArgKind}; +use rustc_middle::ty::{self, EarlyBinder, GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -25,16 +24,14 @@ impl LateLintPass<'_> for MsrvAttrImpl { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { if_chain! { if let hir::ItemKind::Impl(hir::Impl { - of_trait: Some(lint_pass_trait_ref), - self_ty, + of_trait: Some(_), items, .. }) = &item.kind; - if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); - let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); - if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); - let self_ty = hir_ty_to_ty(cx.tcx, self_ty); - if let ty::Adt(self_ty_def, _) = self_ty.kind(); + if let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::instantiate_identity); + let is_late_pass = match_def_path(cx, trait_ref.def_id, &paths::LATE_LINT_PASS); + if is_late_pass || match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS); + if let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind(); if self_ty_def.is_struct(); if self_ty_def.all_fields().any(|f| { cx.tcx diff --git a/tests/ui/no_effect_return.rs b/tests/ui/no_effect_return.rs index f6585aa30a6f0..e46c0d73518b9 100644 --- a/tests/ui/no_effect_return.rs +++ b/tests/ui/no_effect_return.rs @@ -76,6 +76,7 @@ fn h() -> Vec { fn i() -> () { { + // does not suggest on function with explicit unit return type (); //~^ ERROR: statement with no effect } diff --git a/tests/ui/no_effect_return.stderr b/tests/ui/no_effect_return.stderr index b036e63420474..aed079f09b986 100644 --- a/tests/ui/no_effect_return.stderr +++ b/tests/ui/no_effect_return.stderr @@ -54,15 +54,13 @@ LL | ControlFlow::Break::<()>(()); | help: did you mean to return it?: `return` error: statement with no effect - --> $DIR/no_effect_return.rs:79:9 + --> $DIR/no_effect_return.rs:80:9 | LL | (); - | -^^ - | | - | help: did you mean to return it?: `return` + | ^^^ error: statement with no effect - --> $DIR/no_effect_return.rs:88:9 + --> $DIR/no_effect_return.rs:89:9 | LL | (); | ^^^ From e76b7f226aa5e463007b85767452cb9c561c418f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 12 Sep 2023 17:29:32 +0000 Subject: [PATCH 059/149] Do not create a DerefLen place for `Box<[T]>`. --- .../rustc_mir_dataflow/src/value_analysis.rs | 4 +- ...lice.main.ConstProp.32bit.panic-abort.diff | 119 +++++++++++++++++ ...ice.main.ConstProp.32bit.panic-unwind.diff | 123 ++++++++++++++++++ ...lice.main.ConstProp.64bit.panic-abort.diff | 119 +++++++++++++++++ ...ice.main.ConstProp.64bit.panic-unwind.diff | 123 ++++++++++++++++++ ...n.DataflowConstProp.32bit.panic-abort.diff | 111 ++++++++++++++++ ....DataflowConstProp.32bit.panic-unwind.diff | 115 ++++++++++++++++ ...n.DataflowConstProp.64bit.panic-abort.diff | 111 ++++++++++++++++ ....DataflowConstProp.64bit.panic-unwind.diff | 115 ++++++++++++++++ .../default_boxed_slice.rs | 16 +++ 10 files changed, 955 insertions(+), 1 deletion(-) create mode 100644 tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.32bit.panic-abort.diff create mode 100644 tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.32bit.panic-unwind.diff create mode 100644 tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.64bit.panic-abort.diff create mode 100644 tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.64bit.panic-unwind.diff create mode 100644 tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff create mode 100644 tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff create mode 100644 tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff create mode 100644 tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff create mode 100644 tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 514146b503013..560417d99e1a1 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -758,7 +758,9 @@ impl Map { self.value_count += 1; } - if let Some(ref_ty) = ty.builtin_deref(true) && let ty::Slice(..) = ref_ty.ty.kind() { + if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ty::TypeAndMut { ty: ref_ty, .. }) = ty.kind() + && let ty::Slice(..) = ref_ty.kind() + { assert!(self.places[place].value_index.is_none(), "slices are not scalars"); // Prepend new child to the linked list. diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.32bit.panic-abort.diff new file mode 100644 index 0000000000000..b9b46f16a8b7d --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.32bit.panic-abort.diff @@ -0,0 +1,119 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); + let _1: A; + let mut _2: std::boxed::Box<[bool]>; + scope 1 { + debug a => _1; + } + scope 2 (inlined as Default>::default) { + let _3: std::ptr::Unique<[bool]>; + let mut _4: std::ptr::Unique<[bool; 0]>; + scope 3 { + debug ptr => _3; + } + scope 4 (inlined Unique::<[bool; 0]>::dangling) { + let mut _5: std::ptr::NonNull<[bool; 0]>; + scope 5 (inlined NonNull::<[bool; 0]>::dangling) { + let mut _7: usize; + scope 6 { + let _6: *mut [bool; 0]; + scope 7 { + debug ptr => _6; + scope 11 (inlined NonNull::<[bool; 0]>::new_unchecked) { + debug ptr => _6; + let mut _8: *const [bool; 0]; + scope 12 { + scope 13 (inlined NonNull::::new_unchecked::runtime::<[bool; 0]>) { + debug ptr => _6; + scope 14 (inlined ptr::mut_ptr::::is_null) { + debug self => _6; + let mut _9: *mut u8; + scope 15 { + scope 16 (inlined ptr::mut_ptr::::is_null::runtime_impl) { + debug ptr => _9; + scope 17 (inlined ptr::mut_ptr::::addr) { + debug self => _9; + scope 18 { + scope 19 (inlined ptr::mut_ptr::::cast::<()>) { + debug self => _9; + } + } + } + } + } + } + } + } + } + } + scope 8 (inlined align_of::<[bool; 0]>) { + } + scope 9 (inlined invalid_mut::<[bool; 0]>) { + debug addr => _7; + scope 10 { + } + } + } + } + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + StorageLive(_7); +- _7 = AlignOf([bool; 0]); +- _6 = _7 as *mut [bool; 0] (Transmute); ++ _7 = const 1_usize; ++ _6 = const {0x1 as *mut [bool; 0]}; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); +- _8 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _5 = NonNull::<[bool; 0]> { pointer: _8 }; ++ _8 = const {0x1 as *const [bool; 0]}; ++ _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; + StorageDead(_9); + StorageDead(_8); + StorageDead(_6); +- _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; ++ _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; + StorageDead(_5); +- _3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize)); ++ _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc7, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; + StorageDead(_4); +- _2 = Box::<[bool]>(_3, const std::alloc::Global); ++ _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc10, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); + StorageDead(_3); +- _1 = A { foo: move _2 }; ++ _1 = A { foo: const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc11, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }; + StorageDead(_2); + _0 = const (); + drop(_1) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_1); + return; + } ++ } ++ ++ alloc11 (size: 8, align: 4) { ++ 01 00 00 00 00 00 00 00 │ ........ ++ } ++ ++ alloc10 (size: 8, align: 4) { ++ 01 00 00 00 00 00 00 00 │ ........ ++ } ++ ++ alloc7 (size: 8, align: 4) { ++ 01 00 00 00 00 00 00 00 │ ........ + } + diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.32bit.panic-unwind.diff new file mode 100644 index 0000000000000..93b18f23e61a8 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.32bit.panic-unwind.diff @@ -0,0 +1,123 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); + let _1: A; + let mut _2: std::boxed::Box<[bool]>; + scope 1 { + debug a => _1; + } + scope 2 (inlined as Default>::default) { + let _3: std::ptr::Unique<[bool]>; + let mut _4: std::ptr::Unique<[bool; 0]>; + scope 3 { + debug ptr => _3; + } + scope 4 (inlined Unique::<[bool; 0]>::dangling) { + let mut _5: std::ptr::NonNull<[bool; 0]>; + scope 5 (inlined NonNull::<[bool; 0]>::dangling) { + let mut _7: usize; + scope 6 { + let _6: *mut [bool; 0]; + scope 7 { + debug ptr => _6; + scope 11 (inlined NonNull::<[bool; 0]>::new_unchecked) { + debug ptr => _6; + let mut _8: *const [bool; 0]; + scope 12 { + scope 13 (inlined NonNull::::new_unchecked::runtime::<[bool; 0]>) { + debug ptr => _6; + scope 14 (inlined ptr::mut_ptr::::is_null) { + debug self => _6; + let mut _9: *mut u8; + scope 15 { + scope 16 (inlined ptr::mut_ptr::::is_null::runtime_impl) { + debug ptr => _9; + scope 17 (inlined ptr::mut_ptr::::addr) { + debug self => _9; + scope 18 { + scope 19 (inlined ptr::mut_ptr::::cast::<()>) { + debug self => _9; + } + } + } + } + } + } + } + } + } + } + scope 8 (inlined align_of::<[bool; 0]>) { + } + scope 9 (inlined invalid_mut::<[bool; 0]>) { + debug addr => _7; + scope 10 { + } + } + } + } + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + StorageLive(_7); +- _7 = AlignOf([bool; 0]); +- _6 = _7 as *mut [bool; 0] (Transmute); ++ _7 = const 1_usize; ++ _6 = const {0x1 as *mut [bool; 0]}; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); +- _8 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _5 = NonNull::<[bool; 0]> { pointer: _8 }; ++ _8 = const {0x1 as *const [bool; 0]}; ++ _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; + StorageDead(_9); + StorageDead(_8); + StorageDead(_6); +- _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; ++ _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; + StorageDead(_5); +- _3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize)); ++ _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc7, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; + StorageDead(_4); +- _2 = Box::<[bool]>(_3, const std::alloc::Global); ++ _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc10, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); + StorageDead(_3); +- _1 = A { foo: move _2 }; ++ _1 = A { foo: const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc11, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }; + StorageDead(_2); + _0 = const (); + drop(_1) -> [return: bb1, unwind: bb2]; + } + + bb1: { + StorageDead(_1); + return; + } + + bb2 (cleanup): { + resume; + } ++ } ++ ++ alloc11 (size: 8, align: 4) { ++ 01 00 00 00 00 00 00 00 │ ........ ++ } ++ ++ alloc10 (size: 8, align: 4) { ++ 01 00 00 00 00 00 00 00 │ ........ ++ } ++ ++ alloc7 (size: 8, align: 4) { ++ 01 00 00 00 00 00 00 00 │ ........ + } + diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.64bit.panic-abort.diff new file mode 100644 index 0000000000000..3d3af62856bdd --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.64bit.panic-abort.diff @@ -0,0 +1,119 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); + let _1: A; + let mut _2: std::boxed::Box<[bool]>; + scope 1 { + debug a => _1; + } + scope 2 (inlined as Default>::default) { + let _3: std::ptr::Unique<[bool]>; + let mut _4: std::ptr::Unique<[bool; 0]>; + scope 3 { + debug ptr => _3; + } + scope 4 (inlined Unique::<[bool; 0]>::dangling) { + let mut _5: std::ptr::NonNull<[bool; 0]>; + scope 5 (inlined NonNull::<[bool; 0]>::dangling) { + let mut _7: usize; + scope 6 { + let _6: *mut [bool; 0]; + scope 7 { + debug ptr => _6; + scope 11 (inlined NonNull::<[bool; 0]>::new_unchecked) { + debug ptr => _6; + let mut _8: *const [bool; 0]; + scope 12 { + scope 13 (inlined NonNull::::new_unchecked::runtime::<[bool; 0]>) { + debug ptr => _6; + scope 14 (inlined ptr::mut_ptr::::is_null) { + debug self => _6; + let mut _9: *mut u8; + scope 15 { + scope 16 (inlined ptr::mut_ptr::::is_null::runtime_impl) { + debug ptr => _9; + scope 17 (inlined ptr::mut_ptr::::addr) { + debug self => _9; + scope 18 { + scope 19 (inlined ptr::mut_ptr::::cast::<()>) { + debug self => _9; + } + } + } + } + } + } + } + } + } + } + scope 8 (inlined align_of::<[bool; 0]>) { + } + scope 9 (inlined invalid_mut::<[bool; 0]>) { + debug addr => _7; + scope 10 { + } + } + } + } + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + StorageLive(_7); +- _7 = AlignOf([bool; 0]); +- _6 = _7 as *mut [bool; 0] (Transmute); ++ _7 = const 1_usize; ++ _6 = const {0x1 as *mut [bool; 0]}; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); +- _8 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _5 = NonNull::<[bool; 0]> { pointer: _8 }; ++ _8 = const {0x1 as *const [bool; 0]}; ++ _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; + StorageDead(_9); + StorageDead(_8); + StorageDead(_6); +- _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; ++ _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; + StorageDead(_5); +- _3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize)); ++ _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc7, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; + StorageDead(_4); +- _2 = Box::<[bool]>(_3, const std::alloc::Global); ++ _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc10, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); + StorageDead(_3); +- _1 = A { foo: move _2 }; ++ _1 = A { foo: const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc11, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }; + StorageDead(_2); + _0 = const (); + drop(_1) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_1); + return; + } ++ } ++ ++ alloc11 (size: 16, align: 8) { ++ 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ ++ } ++ ++ alloc10 (size: 16, align: 8) { ++ 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ ++ } ++ ++ alloc7 (size: 16, align: 8) { ++ 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + } + diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.64bit.panic-unwind.diff new file mode 100644 index 0000000000000..1933f9bafb369 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.ConstProp.64bit.panic-unwind.diff @@ -0,0 +1,123 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); + let _1: A; + let mut _2: std::boxed::Box<[bool]>; + scope 1 { + debug a => _1; + } + scope 2 (inlined as Default>::default) { + let _3: std::ptr::Unique<[bool]>; + let mut _4: std::ptr::Unique<[bool; 0]>; + scope 3 { + debug ptr => _3; + } + scope 4 (inlined Unique::<[bool; 0]>::dangling) { + let mut _5: std::ptr::NonNull<[bool; 0]>; + scope 5 (inlined NonNull::<[bool; 0]>::dangling) { + let mut _7: usize; + scope 6 { + let _6: *mut [bool; 0]; + scope 7 { + debug ptr => _6; + scope 11 (inlined NonNull::<[bool; 0]>::new_unchecked) { + debug ptr => _6; + let mut _8: *const [bool; 0]; + scope 12 { + scope 13 (inlined NonNull::::new_unchecked::runtime::<[bool; 0]>) { + debug ptr => _6; + scope 14 (inlined ptr::mut_ptr::::is_null) { + debug self => _6; + let mut _9: *mut u8; + scope 15 { + scope 16 (inlined ptr::mut_ptr::::is_null::runtime_impl) { + debug ptr => _9; + scope 17 (inlined ptr::mut_ptr::::addr) { + debug self => _9; + scope 18 { + scope 19 (inlined ptr::mut_ptr::::cast::<()>) { + debug self => _9; + } + } + } + } + } + } + } + } + } + } + scope 8 (inlined align_of::<[bool; 0]>) { + } + scope 9 (inlined invalid_mut::<[bool; 0]>) { + debug addr => _7; + scope 10 { + } + } + } + } + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + StorageLive(_7); +- _7 = AlignOf([bool; 0]); +- _6 = _7 as *mut [bool; 0] (Transmute); ++ _7 = const 1_usize; ++ _6 = const {0x1 as *mut [bool; 0]}; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); +- _8 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _5 = NonNull::<[bool; 0]> { pointer: _8 }; ++ _8 = const {0x1 as *const [bool; 0]}; ++ _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; + StorageDead(_9); + StorageDead(_8); + StorageDead(_6); +- _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; ++ _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; + StorageDead(_5); +- _3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize)); ++ _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc7, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; + StorageDead(_4); +- _2 = Box::<[bool]>(_3, const std::alloc::Global); ++ _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc10, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); + StorageDead(_3); +- _1 = A { foo: move _2 }; ++ _1 = A { foo: const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc11, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }; + StorageDead(_2); + _0 = const (); + drop(_1) -> [return: bb1, unwind: bb2]; + } + + bb1: { + StorageDead(_1); + return; + } + + bb2 (cleanup): { + resume; + } ++ } ++ ++ alloc11 (size: 16, align: 8) { ++ 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ ++ } ++ ++ alloc10 (size: 16, align: 8) { ++ 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ ++ } ++ ++ alloc7 (size: 16, align: 8) { ++ 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + } + diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff new file mode 100644 index 0000000000000..7862c23da809f --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff @@ -0,0 +1,111 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: A; + let mut _2: std::boxed::Box<[bool]>; + scope 1 { + debug a => _1; + } + scope 2 (inlined as Default>::default) { + let _3: std::ptr::Unique<[bool]>; + let mut _4: std::ptr::Unique<[bool; 0]>; + scope 3 { + debug ptr => _3; + } + scope 4 (inlined Unique::<[bool; 0]>::dangling) { + let mut _5: std::ptr::NonNull<[bool; 0]>; + scope 5 (inlined NonNull::<[bool; 0]>::dangling) { + let mut _7: usize; + scope 6 { + let _6: *mut [bool; 0]; + scope 7 { + debug ptr => _6; + scope 11 (inlined NonNull::<[bool; 0]>::new_unchecked) { + debug ptr => _6; + let mut _8: *const [bool; 0]; + scope 12 { + scope 13 (inlined NonNull::::new_unchecked::runtime::<[bool; 0]>) { + debug ptr => _6; + scope 14 (inlined ptr::mut_ptr::::is_null) { + debug self => _6; + let mut _9: *mut u8; + scope 15 { + scope 16 (inlined ptr::mut_ptr::::is_null::runtime_impl) { + debug ptr => _9; + scope 17 (inlined ptr::mut_ptr::::addr) { + debug self => _9; + scope 18 { + scope 19 (inlined ptr::mut_ptr::::cast::<()>) { + debug self => _9; + } + } + } + } + } + } + } + } + } + } + scope 8 (inlined align_of::<[bool; 0]>) { + } + scope 9 (inlined invalid_mut::<[bool; 0]>) { + debug addr => _7; + scope 10 { + } + } + } + } + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + StorageLive(_7); + _7 = const 1_usize; + _6 = const {0x1 as *mut [bool; 0]}; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _8 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; + StorageDead(_9); + StorageDead(_8); + StorageDead(_6); + _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; + StorageDead(_5); + _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc7, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; + StorageDead(_4); + _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc10, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); + StorageDead(_3); + _1 = A { foo: const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc11, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }; + StorageDead(_2); + _0 = const (); + drop(_1) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_1); + return; + } + } + + alloc11 (size: 8, align: 4) { + 01 00 00 00 00 00 00 00 │ ........ + } + + alloc10 (size: 8, align: 4) { + 01 00 00 00 00 00 00 00 │ ........ + } + + alloc7 (size: 8, align: 4) { + 01 00 00 00 00 00 00 00 │ ........ + } + diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff new file mode 100644 index 0000000000000..bd4150ebb4520 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff @@ -0,0 +1,115 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: A; + let mut _2: std::boxed::Box<[bool]>; + scope 1 { + debug a => _1; + } + scope 2 (inlined as Default>::default) { + let _3: std::ptr::Unique<[bool]>; + let mut _4: std::ptr::Unique<[bool; 0]>; + scope 3 { + debug ptr => _3; + } + scope 4 (inlined Unique::<[bool; 0]>::dangling) { + let mut _5: std::ptr::NonNull<[bool; 0]>; + scope 5 (inlined NonNull::<[bool; 0]>::dangling) { + let mut _7: usize; + scope 6 { + let _6: *mut [bool; 0]; + scope 7 { + debug ptr => _6; + scope 11 (inlined NonNull::<[bool; 0]>::new_unchecked) { + debug ptr => _6; + let mut _8: *const [bool; 0]; + scope 12 { + scope 13 (inlined NonNull::::new_unchecked::runtime::<[bool; 0]>) { + debug ptr => _6; + scope 14 (inlined ptr::mut_ptr::::is_null) { + debug self => _6; + let mut _9: *mut u8; + scope 15 { + scope 16 (inlined ptr::mut_ptr::::is_null::runtime_impl) { + debug ptr => _9; + scope 17 (inlined ptr::mut_ptr::::addr) { + debug self => _9; + scope 18 { + scope 19 (inlined ptr::mut_ptr::::cast::<()>) { + debug self => _9; + } + } + } + } + } + } + } + } + } + } + scope 8 (inlined align_of::<[bool; 0]>) { + } + scope 9 (inlined invalid_mut::<[bool; 0]>) { + debug addr => _7; + scope 10 { + } + } + } + } + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + StorageLive(_7); + _7 = const 1_usize; + _6 = const {0x1 as *mut [bool; 0]}; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _8 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; + StorageDead(_9); + StorageDead(_8); + StorageDead(_6); + _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; + StorageDead(_5); + _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc7, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; + StorageDead(_4); + _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc10, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); + StorageDead(_3); + _1 = A { foo: const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc11, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }; + StorageDead(_2); + _0 = const (); + drop(_1) -> [return: bb1, unwind: bb2]; + } + + bb1: { + StorageDead(_1); + return; + } + + bb2 (cleanup): { + resume; + } + } + + alloc11 (size: 8, align: 4) { + 01 00 00 00 00 00 00 00 │ ........ + } + + alloc10 (size: 8, align: 4) { + 01 00 00 00 00 00 00 00 │ ........ + } + + alloc7 (size: 8, align: 4) { + 01 00 00 00 00 00 00 00 │ ........ + } + diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff new file mode 100644 index 0000000000000..312fc7b7a82b5 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff @@ -0,0 +1,111 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: A; + let mut _2: std::boxed::Box<[bool]>; + scope 1 { + debug a => _1; + } + scope 2 (inlined as Default>::default) { + let _3: std::ptr::Unique<[bool]>; + let mut _4: std::ptr::Unique<[bool; 0]>; + scope 3 { + debug ptr => _3; + } + scope 4 (inlined Unique::<[bool; 0]>::dangling) { + let mut _5: std::ptr::NonNull<[bool; 0]>; + scope 5 (inlined NonNull::<[bool; 0]>::dangling) { + let mut _7: usize; + scope 6 { + let _6: *mut [bool; 0]; + scope 7 { + debug ptr => _6; + scope 11 (inlined NonNull::<[bool; 0]>::new_unchecked) { + debug ptr => _6; + let mut _8: *const [bool; 0]; + scope 12 { + scope 13 (inlined NonNull::::new_unchecked::runtime::<[bool; 0]>) { + debug ptr => _6; + scope 14 (inlined ptr::mut_ptr::::is_null) { + debug self => _6; + let mut _9: *mut u8; + scope 15 { + scope 16 (inlined ptr::mut_ptr::::is_null::runtime_impl) { + debug ptr => _9; + scope 17 (inlined ptr::mut_ptr::::addr) { + debug self => _9; + scope 18 { + scope 19 (inlined ptr::mut_ptr::::cast::<()>) { + debug self => _9; + } + } + } + } + } + } + } + } + } + } + scope 8 (inlined align_of::<[bool; 0]>) { + } + scope 9 (inlined invalid_mut::<[bool; 0]>) { + debug addr => _7; + scope 10 { + } + } + } + } + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + StorageLive(_7); + _7 = const 1_usize; + _6 = const {0x1 as *mut [bool; 0]}; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _8 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; + StorageDead(_9); + StorageDead(_8); + StorageDead(_6); + _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; + StorageDead(_5); + _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc7, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; + StorageDead(_4); + _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc10, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); + StorageDead(_3); + _1 = A { foo: const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc11, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }; + StorageDead(_2); + _0 = const (); + drop(_1) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_1); + return; + } + } + + alloc11 (size: 16, align: 8) { + 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + } + + alloc10 (size: 16, align: 8) { + 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + } + + alloc7 (size: 16, align: 8) { + 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + } + diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff new file mode 100644 index 0000000000000..3227d8b84353a --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff @@ -0,0 +1,115 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: A; + let mut _2: std::boxed::Box<[bool]>; + scope 1 { + debug a => _1; + } + scope 2 (inlined as Default>::default) { + let _3: std::ptr::Unique<[bool]>; + let mut _4: std::ptr::Unique<[bool; 0]>; + scope 3 { + debug ptr => _3; + } + scope 4 (inlined Unique::<[bool; 0]>::dangling) { + let mut _5: std::ptr::NonNull<[bool; 0]>; + scope 5 (inlined NonNull::<[bool; 0]>::dangling) { + let mut _7: usize; + scope 6 { + let _6: *mut [bool; 0]; + scope 7 { + debug ptr => _6; + scope 11 (inlined NonNull::<[bool; 0]>::new_unchecked) { + debug ptr => _6; + let mut _8: *const [bool; 0]; + scope 12 { + scope 13 (inlined NonNull::::new_unchecked::runtime::<[bool; 0]>) { + debug ptr => _6; + scope 14 (inlined ptr::mut_ptr::::is_null) { + debug self => _6; + let mut _9: *mut u8; + scope 15 { + scope 16 (inlined ptr::mut_ptr::::is_null::runtime_impl) { + debug ptr => _9; + scope 17 (inlined ptr::mut_ptr::::addr) { + debug self => _9; + scope 18 { + scope 19 (inlined ptr::mut_ptr::::cast::<()>) { + debug self => _9; + } + } + } + } + } + } + } + } + } + } + scope 8 (inlined align_of::<[bool; 0]>) { + } + scope 9 (inlined invalid_mut::<[bool; 0]>) { + debug addr => _7; + scope 10 { + } + } + } + } + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + StorageLive(_7); + _7 = const 1_usize; + _6 = const {0x1 as *mut [bool; 0]}; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _8 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; + StorageDead(_9); + StorageDead(_8); + StorageDead(_6); + _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; + StorageDead(_5); + _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc7, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; + StorageDead(_4); + _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc10, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); + StorageDead(_3); + _1 = A { foo: const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: alloc11, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }; + StorageDead(_2); + _0 = const (); + drop(_1) -> [return: bb1, unwind: bb2]; + } + + bb1: { + StorageDead(_1); + return; + } + + bb2 (cleanup): { + resume; + } + } + + alloc11 (size: 16, align: 8) { + 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + } + + alloc10 (size: 16, align: 8) { + 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + } + + alloc7 (size: 16, align: 8) { + 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + } + diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs new file mode 100644 index 0000000000000..b6be01aff7e12 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs @@ -0,0 +1,16 @@ +// unit-test: DataflowConstProp +// compile-flags: -Zmir-enable-passes=+ConstProp,+Inline +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY + +struct A { + foo: Box<[bool]>, +} + +// EMIT_MIR default_boxed_slice.main.ConstProp.diff +// EMIT_MIR default_boxed_slice.main.DataflowConstProp.diff +fn main() { + // ConstProp will create a constant of type `Box<[bool]>`. + // Verify that `DataflowConstProp` does not ICE trying to dereference it directly. + let a: A = A { foo: Box::default() }; +} From b8c85207250cb90d658fd767137c4b63ecc43d01 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 20 Sep 2023 18:18:44 +0000 Subject: [PATCH 060/149] Ignore debug-assertions in test. --- tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs index b6be01aff7e12..dfeccd3eb94ee 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs @@ -1,5 +1,6 @@ // unit-test: DataflowConstProp // compile-flags: -Zmir-enable-passes=+ConstProp,+Inline +// ignore-debug assertions change the output MIR // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR_FOR_EACH_PANIC_STRATEGY From 2ea6ac5673931cd0804507ebe57452914d76f552 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 20 Sep 2023 20:51:14 +0200 Subject: [PATCH 061/149] rename mir::Constant -> mir::ConstOperand, mir::ConstKind -> mir::Const --- clippy_lints/src/enum_clike.rs | 2 +- clippy_lints/src/matches/overlapping_arms.rs | 4 ++-- clippy_utils/src/consts.rs | 20 ++++++++++---------- clippy_utils/src/lib.rs | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 96c5c7fc50931..3f60e5a7c4d64 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -50,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .tcx .const_eval_poly(def_id.to_def_id()) .ok() - .map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty)); + .map(|val| rustc_middle::mir::Const::from_value(val, ty)); if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx, c)) { if let ty::Adt(adt, _) = ty.kind() { if adt.is_enum() { diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 6e13148f2fc18..7c0485914b83b 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -37,14 +37,14 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, None => { let min_val_const = ty.numeric_min_val(cx.tcx)?; - miri_to_const(cx, mir::ConstantKind::from_ty_const(min_val_const, cx.tcx))? + miri_to_const(cx, mir::Const::from_ty_const(min_val_const, cx.tcx))? }, }; let rhs_const = match rhs { Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, None => { let max_val_const = ty.numeric_max_val(cx.tcx)?; - miri_to_const(cx, mir::ConstantKind::from_ty_const(max_val_const, cx.tcx))? + miri_to_const(cx, mir::Const::from_ty_const(max_val_const, cx.tcx))? }, }; let lhs_val = lhs_const.int_value(cx, ty)?; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index c88fce22f7fb6..a136de8624089 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -21,7 +21,7 @@ use std::iter; /// A `LitKind`-like enum to fold constant `Expr`s into. #[derive(Debug, Clone)] pub enum Constant<'tcx> { - Adt(rustc_middle::mir::ConstantKind<'tcx>), + Adt(rustc_middle::mir::Const<'tcx>), /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). @@ -482,7 +482,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .tcx .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None) .ok() - .map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?; + .map(|val| rustc_middle::mir::Const::from_value(val, ty))?; let result = miri_to_const(self.lcx, result)?; self.source = ConstantSource::Constant; Some(result) @@ -655,10 +655,10 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } -pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'tcx>) -> Option> { +pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option> { use rustc_middle::mir::ConstValue; match result { - mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { + mir::Const::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))), @@ -671,11 +671,11 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), _ => None, }, - mir::ConstantKind::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => { + mir::Const::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => { let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; String::from_utf8(data.to_owned()).ok().map(Constant::Str) } - mir::ConstantKind::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => { + mir::Const::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => { let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory(); match result.ty().kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), @@ -714,17 +714,17 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t fn field_of_struct<'tcx>( adt_def: ty::AdtDef<'tcx>, lcx: &LateContext<'tcx>, - result: mir::ConstantKind<'tcx>, + result: mir::Const<'tcx>, field: &Ident, -) -> Option> { - if let mir::ConstantKind::Val(result, ty) = result +) -> Option> { + if let mir::Const::Val(result, ty) = result && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_diagnostics((result, ty)) && let Some(dc_variant) = dc.variant && let Some(variant) = adt_def.variants().get(dc_variant) && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) && let Some(&(val, ty)) = dc.fields.get(field_idx) { - Some(mir::ConstantKind::Val(val, ty)) + Some(mir::Const::Val(val, ty)) } else { None diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 4ef3ec19647e9..1e464db8087e5 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -97,7 +97,7 @@ use rustc_hir::{ use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; -use rustc_middle::mir::ConstantKind; +use rustc_middle::mir::Const; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; use rustc_middle::ty::fast_reject::SimplifiedType; @@ -1510,7 +1510,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let bnd_ty = subst.type_at(0) && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree())) - && let min_const_kind = ConstantKind::from_value(const_val, bnd_ty) + && let min_const_kind = Const::from_value(const_val, bnd_ty) && let Some(min_const) = miri_to_const(cx, min_const_kind) && let Some(start_const) = constant(cx, cx.typeck_results(), start) { @@ -1526,7 +1526,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let bnd_ty = subst.type_at(0) && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree())) - && let max_const_kind = ConstantKind::from_value(const_val, bnd_ty) + && let max_const_kind = Const::from_value(const_val, bnd_ty) && let Some(max_const) = miri_to_const(cx, max_const_kind) && let Some(end_const) = constant(cx, cx.typeck_results(), end) { From 238dc2828ed4b2ce49a95b8093486cd01c528af9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 18 Sep 2023 15:30:07 +0000 Subject: [PATCH 062/149] Prevent promotion of const fn calls in inline consts --- clippy_lints/src/operators/arithmetic_side_effects.rs | 2 +- clippy_lints/src/operators/numeric_arithmetic.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 8072aded8513b..a10aa65e59482 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -315,7 +315,7 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { let body_owner_def_id = cx.tcx.hir().body_owner_def_id(body.id()); let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id); - if let hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) = body_owner_kind { + if let hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_) = body_owner_kind { let body_span = cx.tcx.hir().span_with_body(body_owner); if let Some(span) = self.const_span && span.contains(body_span) { return; diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index 102845ceed095..80389cbf84be2 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -72,7 +72,7 @@ impl Context { let body_owner_def_id = cx.tcx.hir().body_owner_def_id(body.id()); match cx.tcx.hir().body_owner_kind(body_owner_def_id) { - hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => { + hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const { .. } => { let body_span = cx.tcx.hir().span_with_body(body_owner); if let Some(span) = self.const_span { From 823bcb478bc55cacee0dcc1a9cc2b70decfa1d39 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 14 Sep 2023 22:38:07 +0000 Subject: [PATCH 063/149] Record asyncness span in HIR --- clippy_lints/src/redundant_closure_call.rs | 7 ++++--- clippy_utils/src/lib.rs | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index fc49b58e0a777..f42836611ca53 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -10,6 +10,7 @@ use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -84,7 +85,7 @@ fn find_innermost_closure<'tcx>( cx: &LateContext<'tcx>, mut expr: &'tcx hir::Expr<'tcx>, mut steps: usize, -) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, hir::IsAsync)> { +) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, ty::Asyncness)> { let mut data = None; while let hir::ExprKind::Closure(closure) = expr.kind @@ -98,9 +99,9 @@ fn find_innermost_closure<'tcx>( { expr = body.value; data = Some((body.value, closure.fn_decl, if is_async_closure(body) { - hir::IsAsync::Async + ty::Asyncness::Yes } else { - hir::IsAsync::NotAsync + ty::Asyncness::No })); steps -= 1; } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1e464db8087e5..be1c46319c2b6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -90,7 +90,7 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr, - ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, + ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, }; @@ -1958,8 +1958,8 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, /// Checks if the given function kind is an async function. pub fn is_async_fn(kind: FnKind<'_>) -> bool { match kind { - FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async, - FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async, + FnKind::ItemFn(_, _, header) => header.asyncness .is_async(), + FnKind::Method(_, sig) => sig.header.asyncness.is_async(), FnKind::Closure => false, } } From 1fac304bf53f35f2088fad9b40cd919cd7a485d9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Sep 2023 08:36:50 +0200 Subject: [PATCH 064/149] adjust how closure/generator types and rvalues are printed --- tests/ui/box_default.fixed | 2 +- tests/ui/box_default.rs | 2 +- tests/ui/crashes/ice-6251.stderr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed index 22c034c88ab1c..69cabcb32d333 100644 --- a/tests/ui/box_default.fixed +++ b/tests/ui/box_default.fixed @@ -36,7 +36,7 @@ fn main() { issue_10381(); // `Box::>::default()` would be valid here, but not `Box::default()` or - // `Box::::default()` + // `Box::::default()` // // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 let mut unnameable = Box::new(Option::default()); diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index 89e3888ba211a..48fa8bc33bc2d 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -36,7 +36,7 @@ fn main() { issue_10381(); // `Box::>::default()` would be valid here, but not `Box::default()` or - // `Box::::default()` + // `Box::::default()` // // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 let mut unnameable = Box::new(Option::default()); diff --git a/tests/ui/crashes/ice-6251.stderr b/tests/ui/crashes/ice-6251.stderr index 68a5766c90c0e..11081dc8087e2 100644 --- a/tests/ui/crashes/ice-6251.stderr +++ b/tests/ui/crashes/ice-6251.stderr @@ -27,7 +27,7 @@ LL | fn bug() -> impl Iterator { | ^^^^^^^^^^^ expected `usize`, found closure | = note: expected type `usize` - found closure `[closure@$DIR/ice-6251.rs:4:44: 4:53]` + found closure `{closure@$DIR/ice-6251.rs:4:44: 4:53}` error: aborting due to 3 previous errors From 74573fbab98fe3eeaef63ada418613bff0a77c62 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 22 Sep 2023 00:05:45 +0000 Subject: [PATCH 065/149] fix clippy errors (ignore effects in certainty) --- clippy_utils/src/ty/type_certainty/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index dc7756533acb0..4b06b12fb94d0 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -207,7 +207,8 @@ fn path_segment_certainty( // Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE. if cx.tcx.res_generics_def_id(path_segment.res).is_some() { let generics = cx.tcx.generics_of(def_id); - let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && generics.params.is_empty() + let count = generics.params.len() - generics.host_effect_index.is_some() as usize; + let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 { Certainty::Certain(None) } else { @@ -299,7 +300,7 @@ fn type_is_inferrable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> b // Check that all type parameters appear in the functions input types. (0..(generics.parent_count + generics.params.len()) as u32).all(|index| { - fn_sig + Some(index as usize) == generics.host_effect_index || fn_sig .inputs() .iter() .any(|input_ty| contains_param(*input_ty.skip_binder(), index)) From 5ee167e00d0c0563976239416ec344e7914455bd Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 22 Sep 2023 09:14:39 +0000 Subject: [PATCH 066/149] Add a way to decouple the implementation and the declaration of a TyCtxt method. --- clippy_utils/src/consts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index a136de8624089..6b1a738aaa94c 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -718,7 +718,7 @@ fn field_of_struct<'tcx>( field: &Ident, ) -> Option> { if let mir::Const::Val(result, ty) = result - && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_diagnostics((result, ty)) + && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_diagnostics(result, ty) && let Some(dc_variant) = dc.variant && let Some(variant) = adt_def.variants().get(dc_variant) && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) From 813fed2904fde5d5238e89e2e87fc9830b39a67d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 22 Sep 2023 13:18:46 +0200 Subject: [PATCH 067/149] fix OS-specific I/O safety docs since the io_safety feature is stable --- library/std/src/os/unix/io/mod.rs | 11 +++++++---- library/std/src/os/windows/io/mod.rs | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs index c12d89ed63710..827278f8b2605 100644 --- a/library/std/src/os/unix/io/mod.rs +++ b/library/std/src/os/unix/io/mod.rs @@ -6,7 +6,8 @@ //! //! This module provides three types for representing file descriptors, //! with different ownership properties: raw, borrowed, and owned, which are -//! analogous to types used for representing pointers. These types reflect the Unix version of [I/O safety]. +//! analogous to types used for representing pointers. These types reflect concepts of [I/O +//! safety][io-safety] on Unix. //! //! | Type | Analogous to | //! | ------------------ | ------------ | @@ -17,8 +18,8 @@ //! Like raw pointers, `RawFd` values are primitive values. And in new code, //! they should be considered unsafe to do I/O on (analogous to dereferencing //! them). Rust did not always provide this guidance, so existing code in the -//! Rust ecosystem often doesn't mark `RawFd` usage as unsafe. Once the -//! `io_safety` feature is stable, libraries will be encouraged to migrate, +//! Rust ecosystem often doesn't mark `RawFd` usage as unsafe. +//! Libraries are encouraged to migrate, //! either by adding `unsafe` to APIs that dereference `RawFd` values, or by //! using to `BorrowedFd` or `OwnedFd` instead. //! @@ -54,6 +55,8 @@ //! Like boxes, `OwnedFd` values conceptually own the resource they point to, //! and free (close) it when they are dropped. //! +//! See the [`io` module docs][io-safety] for a general explanation of I/O safety. +//! //! ## `/proc/self/mem` and similar OS features //! //! Some platforms have special files, such as `/proc/self/mem`, which @@ -74,7 +77,7 @@ //! necessary to use *sandboxing*, which is outside the scope of `std`. //! //! [`BorrowedFd<'a>`]: crate::os::unix::io::BorrowedFd -//! [I/O safety]: crate::io#io-safety +//! [io-safety]: crate::io#io-safety #![stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/os/windows/io/mod.rs b/library/std/src/os/windows/io/mod.rs index 3d4bb96d45802..3d3ae38788639 100644 --- a/library/std/src/os/windows/io/mod.rs +++ b/library/std/src/os/windows/io/mod.rs @@ -6,7 +6,8 @@ //! //! This module provides three types for representing raw handles and sockets //! with different ownership properties: raw, borrowed, and owned, which are -//! analogous to types used for representing pointers. These types reflect the Windows version of [I/O safety]. +//! analogous to types used for representing pointers. These types reflect concepts of [I/O +//! safety][io-safety] on Windows. //! //! | Type | Analogous to | //! | ---------------------- | ------------ | @@ -23,8 +24,8 @@ //! And in new code, they should be considered unsafe to do I/O on (analogous //! to dereferencing them). Rust did not always provide this guidance, so //! existing code in the Rust ecosystem often doesn't mark `RawHandle` and -//! `RawSocket` usage as unsafe. Once the `io_safety` feature is stable, -//! libraries will be encouraged to migrate, either by adding `unsafe` to APIs +//! `RawSocket` usage as unsafe. +//! Libraries are encouraged to migrate, either by adding `unsafe` to APIs //! that dereference `RawHandle` and `RawSocket` values, or by using to //! `BorrowedHandle`, `BorrowedSocket`, `OwnedHandle`, or `OwnedSocket`. //! @@ -45,9 +46,11 @@ //! Like boxes, `OwnedHandle` and `OwnedSocket` values conceptually own the //! resource they point to, and free (close) it when they are dropped. //! +//! See the [`io` module docs][io-safety] for a general explanation of I/O safety. +//! //! [`BorrowedHandle<'a>`]: crate::os::windows::io::BorrowedHandle //! [`BorrowedSocket<'a>`]: crate::os::windows::io::BorrowedSocket -//! [I/O safety]: crate::io#io-safety +//! [io-safety]: crate::io#io-safety #![stable(feature = "rust1", since = "1.0.0")] From 48c6ae0611ec6fb4094a24610982ddefde16e20f Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sun, 18 Dec 2022 09:54:54 +0530 Subject: [PATCH 068/149] Add Minimal Std implementation for UEFI Implemented modules: 1. alloc 2. os_str 3. env 4. math Tracking Issue: https://github.com/rust-lang/rust/issues/100499 API Change Proposal: https://github.com/rust-lang/libs-team/issues/87 This was originally part of https://github.com/rust-lang/rust/pull/100316. Since that PR was becoming too unwieldy and cluttered, and with suggestion from @dvdhrm, I have extracted a minimal std implementation to this PR. Signed-off-by: Ayush Singh --- .nlsp-settings/rust_analyzer.json | 3 + Cargo.lock | 23 ++ compiler/rustc_codegen_ssa/src/base.rs | 35 ++- .../rustc_target/src/spec/uefi_msvc_base.rs | 1 + .../src/spec/x86_64_unknown_uefi.rs | 3 +- library/panic_abort/src/lib.rs | 1 + library/std/Cargo.toml | 4 + library/std/build.rs | 3 +- library/std/src/lib.rs | 1 - library/std/src/os/mod.rs | 2 + library/std/src/os/uefi/env.rs | 54 ++++ library/std/src/os/uefi/mod.rs | 7 + .../std/src/sys/common/thread_local/mod.rs | 2 +- library/std/src/sys/mod.rs | 3 + library/std/src/sys/uefi/alloc.rs | 32 +++ library/std/src/sys/uefi/common.rs | 269 ++++++++++++++++++ library/std/src/sys/uefi/env.rs | 9 + library/std/src/sys/uefi/mod.rs | 155 ++++++++++ library/std/src/sys/uefi/path.rs | 25 ++ library/std/src/sys/uefi/tests.rs | 21 ++ library/std/src/sys_common/mod.rs | 2 + src/bootstrap/config.rs | 6 +- .../src/platform-support/unknown-uefi.md | 72 ++++- src/tools/tidy/src/deps.rs | 3 + 24 files changed, 718 insertions(+), 18 deletions(-) create mode 100644 .nlsp-settings/rust_analyzer.json create mode 100644 library/std/src/os/uefi/env.rs create mode 100644 library/std/src/os/uefi/mod.rs create mode 100644 library/std/src/sys/uefi/alloc.rs create mode 100644 library/std/src/sys/uefi/common.rs create mode 100644 library/std/src/sys/uefi/env.rs create mode 100644 library/std/src/sys/uefi/mod.rs create mode 100644 library/std/src/sys/uefi/path.rs create mode 100644 library/std/src/sys/uefi/tests.rs diff --git a/.nlsp-settings/rust_analyzer.json b/.nlsp-settings/rust_analyzer.json new file mode 100644 index 0000000000000..eddf84afb2a3d --- /dev/null +++ b/.nlsp-settings/rust_analyzer.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.cargo.target": "x86_64-unknown-uefi" +} diff --git a/Cargo.lock b/Cargo.lock index d9aaedb85443f..f84387f30be61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3006,6 +3006,27 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e7345c622833c6745e7b027a28aa95618813dc1f3c3de396206410267dce6f3" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "r-efi-alloc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7" +dependencies = [ + "compiler_builtins", + "r-efi", + "rustc-std-workspace-core", +] + [[package]] name = "rand" version = "0.8.5" @@ -5012,6 +5033,8 @@ dependencies = [ "panic_abort", "panic_unwind", "profiler_builtins", + "r-efi", + "r-efi-alloc", "rand", "rand_xorshift", "rustc-demangle", diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index cd19885faa0e1..4753f12554065 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -420,9 +420,11 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( rust_main_def_id: DefId, entry_type: EntryFnType, ) -> Bx::Function { - // The entry function is either `int main(void)` or `int main(int argc, char **argv)`, - // depending on whether the target needs `argc` and `argv` to be passed in. - let llfty = if cx.sess().target.main_needs_argc_argv { + // The entry function is either `int main(void)` or `int main(int argc, char **argv)`, or + // `Status efi_main(Handle hd, SystemTable *st)` depending on the target. + let llfty = if cx.sess().target.os.contains("uefi") { + cx.type_func(&[cx.type_ptr(), cx.type_ptr()], cx.type_isize()) + } else if cx.sess().target.main_needs_argc_argv { cx.type_func(&[cx.type_int(), cx.type_ptr()], cx.type_int()) } else { cx.type_func(&[], cx.type_int()) @@ -485,8 +487,12 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( }; let result = bx.call(start_ty, None, None, start_fn, &args, None); - let cast = bx.intcast(result, cx.type_int(), true); - bx.ret(cast); + if cx.sess().target.os.contains("uefi") { + bx.ret(result); + } else { + let cast = bx.intcast(result, cx.type_int(), true); + bx.ret(cast); + } llfn } @@ -497,7 +503,18 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx: &'a Bx::CodegenCx, bx: &mut Bx, ) -> (Bx::Value, Bx::Value) { - if cx.sess().target.main_needs_argc_argv { + if cx.sess().target.os.contains("uefi") { + // Params for UEFI + let param_handle = bx.get_param(0); + let param_system_table = bx.get_param(1); + let arg_argc = bx.const_int(cx.type_isize(), 2); + let arg_argv = bx.alloca(cx.type_array(cx.type_i8p(), 2), Align::ONE); + bx.store(param_handle, arg_argv, Align::ONE); + let arg_argv_el1 = + bx.gep(cx.type_ptr_to(cx.type_i8()), arg_argv, &[bx.const_int(cx.type_int(), 1)]); + bx.store(param_system_table, arg_argv_el1, Align::ONE); + (arg_argc, arg_argv) + } else if cx.sess().target.main_needs_argc_argv { // Params from native `main()` used as args for rust start function let param_argc = bx.get_param(0); let param_argv = bx.get_param(1); @@ -549,7 +566,11 @@ pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option { use rustc_middle::middle::dependency_format::Linkage; list.iter().any(|&linkage| linkage == Linkage::Dynamic) }); - if any_dynamic_crate { None } else { tcx.allocator_kind(()) } + if any_dynamic_crate { + None + } else { + tcx.allocator_kind(()) + } } pub fn codegen_crate( diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs index 8968d3c8fc100..a50a55ad7e028 100644 --- a/compiler/rustc_target/src/spec/uefi_msvc_base.rs +++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs @@ -46,6 +46,7 @@ pub fn opts() -> TargetOptions { stack_probes: StackProbeType::Call, singlethread: true, linker: Some("rust-lld".into()), + entry_name: "efi_main".into(), ..base } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs index 67664a74710a3..41ba768068a35 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs @@ -5,13 +5,14 @@ // The win64 ABI is used. It differs from the sysv64 ABI, so we must use a windows target with // LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features. -use crate::spec::Target; +use crate::{abi::call::Conv, spec::Target}; pub fn target() -> Target { let mut base = super::uefi_msvc_base::opts(); base.cpu = "x86-64".into(); base.plt_by_default = false; base.max_atomic_width = Some(64); + base.entry_abi = Conv::X86_64Win64; // We disable MMX and SSE for now, even though UEFI allows using them. Problem is, you have to // enable these CPU features explicitly before their first use, otherwise their instructions diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index d675696f13f1a..bc0d7287be505 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -45,6 +45,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { } else if #[cfg(any(target_os = "hermit", all(target_vendor = "fortanix", target_env = "sgx"), target_os = "xous" + target_os = "uefi", ))] { unsafe fn abort() -> ! { // call std::sys::abort_internal diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index e8f642586cd7c..1908dfaa33db0 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -48,6 +48,10 @@ hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true [target.'cfg(target_os = "wasi")'.dependencies] wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } +[target.'cfg(target_os = "uefi")'.dependencies] +r-efi = { version = "4.1.0", features = ['rustc-dep-of-std', 'efiapi']} +r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std']} + [features] backtrace = [ "gimli-symbolize", diff --git a/library/std/build.rs b/library/std/build.rs index e5509504b8495..1c32b42565011 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -39,6 +39,7 @@ fn main() { || target.contains("nto") || target.contains("xous") || target.contains("hurd") + || target.contains("uefi") // See src/bootstrap/synthetic_targets.rs || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() { @@ -51,7 +52,7 @@ fn main() { // - mipsel-sony-psp // - nvptx64-nvidia-cuda // - arch=avr - // - uefi (x86_64-unknown-uefi, i686-unknown-uefi) + // - tvos (aarch64-apple-tvos, x86_64-apple-tvos) // - JSON targets // - Any new targets that have not been explicitly added above. println!("cargo:rustc-cfg=feature=\"restricted-std\""); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5e3249655b838..b572347019caf 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -263,7 +263,6 @@ #![cfg_attr(target_os = "xous", feature(slice_ptr_len))] // // Language features: -// tidy-alphabetical-start #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 24d16e64c8636..11ad21515fdec 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -142,6 +142,8 @@ pub mod solid; #[cfg(target_os = "tvos")] #[path = "ios/mod.rs"] pub(crate) mod tvos; +#[cfg(target_os = "uefi")] +pub mod uefi; #[cfg(target_os = "vita")] pub mod vita; #[cfg(target_os = "vxworks")] diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs new file mode 100644 index 0000000000000..479ff5f69757e --- /dev/null +++ b/library/std/src/os/uefi/env.rs @@ -0,0 +1,54 @@ +//! UEFI-specific extensions to the primitives in `std::env` module + +use crate::ffi::c_void; +use crate::ptr::NonNull; +use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::sync::OnceLock; + +// Position 0 = SystemTable +// Position 1 = ImageHandle +static GLOBALS: OnceLock<(AtomicPtr, AtomicPtr)> = OnceLock::new(); + +/// Initializes the global System Table and Image Handle pointers. +/// +/// The standard library requires access to the UEFI System Table and the Application Image Handle +/// to operate. Those are provided to UEFI Applications via their application entry point. By +/// calling `init_globals()`, those pointers are retained by the standard library for future use. +/// The pointers are never exposed to any entity outside of this application and it is guaranteed +/// that, once the application exited, these pointers are never dereferenced again. +/// +/// Callers are required to ensure the pointers are valid for the entire lifetime of this +/// application. In particular, UEFI Boot Services must not be exited while an application with the +/// standard library is loaded. +/// +/// This function must not be called more than once. +#[unstable(feature = "uefi_std", issue = "100499")] +pub unsafe fn init_globals(handle: NonNull, system_table: NonNull) { + GLOBALS.set((AtomicPtr::new(system_table.as_ptr()), AtomicPtr::new(handle.as_ptr()))).unwrap() +} + +/// Get the SystemTable Pointer. +/// Note: This function panics if the System Table and Image Handle is Not initialized +#[unstable(feature = "uefi_std", issue = "100499")] +pub fn system_table() -> NonNull { + try_system_table().unwrap() +} + +/// Get the SystemHandle Pointer. +/// Note: This function panics if the System Table and Image Handle is Not initialized +#[unstable(feature = "uefi_std", issue = "100499")] +pub fn image_handle() -> NonNull { + try_image_handle().unwrap() +} + +/// Get the SystemTable Pointer. +/// This function is mostly intended for places where panic is not an option +pub(crate) fn try_system_table() -> Option> { + NonNull::new(GLOBALS.get()?.0.load(Ordering::Acquire)) +} + +/// Get the SystemHandle Pointer. +/// This function is mostly intended for places where panic is not an option +pub(crate) fn try_image_handle() -> Option> { + NonNull::new(GLOBALS.get()?.1.load(Ordering::Acquire)) +} diff --git a/library/std/src/os/uefi/mod.rs b/library/std/src/os/uefi/mod.rs new file mode 100644 index 0000000000000..fc0468f25fc8e --- /dev/null +++ b/library/std/src/os/uefi/mod.rs @@ -0,0 +1,7 @@ +//! Platform-specific extensions to `std` for UEFI. + +#![unstable(feature = "uefi_std", issue = "100499")] + +pub mod env; +#[path = "../windows/ffi.rs"] +pub mod ffi; diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs index 975509bd412b0..8b2c839f837d4 100644 --- a/library/std/src/sys/common/thread_local/mod.rs +++ b/library/std/src/sys/common/thread_local/mod.rs @@ -6,7 +6,7 @@ // "static" is for single-threaded platforms where a global static is sufficient. cfg_if::cfg_if! { - if #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] { + if #[cfg(any(all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi"))] { #[doc(hidden)] mod static_local; #[doc(hidden)] diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 457eb782ccc66..a03501e6a1357 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -47,6 +47,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "xous")] { mod xous; pub use self::xous::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use self::uefi::*; } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { mod sgx; pub use self::sgx::*; diff --git a/library/std/src/sys/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs new file mode 100644 index 0000000000000..6a14ee76e92ce --- /dev/null +++ b/library/std/src/sys/uefi/alloc.rs @@ -0,0 +1,32 @@ +//! Global Allocator for UEFI. +//! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc) + +use crate::alloc::{handle_alloc_error, GlobalAlloc, Layout, System}; + +const MEMORY_TYPE: u32 = r_efi::efi::LOADER_DATA; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let system_table = match crate::os::uefi::env::try_system_table() { + None => return crate::ptr::null_mut(), + Some(x) => x.as_ptr() as *mut _, + }; + + if layout.size() > 0 { + unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) } + } else { + layout.dangling().as_ptr() + } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let system_table = match crate::os::uefi::env::try_system_table() { + None => handle_alloc_error(layout), + Some(x) => x.as_ptr() as *mut _, + }; + if layout.size() > 0 { + unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } + } + } +} diff --git a/library/std/src/sys/uefi/common.rs b/library/std/src/sys/uefi/common.rs new file mode 100644 index 0000000000000..6316a74d3670c --- /dev/null +++ b/library/std/src/sys/uefi/common.rs @@ -0,0 +1,269 @@ +//! Contains most of the shared UEFI specific stuff. Some of this might be moved to `std::os::uefi` +//! if needed but no point in adding extra public API when there is not Std support for UEFI in the +//! first place + +use r_efi::efi::Guid; + +use crate::io::{self, const_io_error}; +use crate::mem::MaybeUninit; +use crate::os::uefi; +use crate::ptr::NonNull; + +// Locate handles with a particular protocol GUID +/// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` +pub(crate) fn locate_handles(mut guid: Guid) -> io::Result>> { + fn inner( + guid: &mut Guid, + boot_services: NonNull, + buf_size: &mut usize, + buf: *mut r_efi::efi::Handle, + ) -> io::Result<()> { + let r = unsafe { + ((*boot_services.as_ptr()).locate_handle)( + r_efi::efi::BY_PROTOCOL, + guid, + crate::ptr::null_mut(), + buf_size, + buf, + ) + }; + + if r.is_error() { Err(status_to_io_error(r)) } else { Ok(()) } + } + + let boot_services = boot_services(); + let mut buf_len = 0usize; + + match inner(&mut guid, boot_services, &mut buf_len, crate::ptr::null_mut()) { + Ok(()) => unreachable!(), + Err(e) => match e.kind() { + io::ErrorKind::FileTooLarge => {} + _ => return Err(e), + }, + } + + // The returned buf_len is in bytes + let mut buf: Vec = + Vec::with_capacity(buf_len / crate::mem::size_of::()); + match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { + Ok(()) => { + // SAFETY: This is safe because the call will succeed only if buf_len >= required + // length. Also, on success, the `buf_len` is updated with the size of bufferv (in + // bytes) written + unsafe { buf.set_len(buf_len / crate::mem::size_of::()) }; + Ok(buf.iter().filter_map(|x| NonNull::new(*x)).collect()) + } + Err(e) => Err(e), + } +} + +/// Open Protocol on a handle +/// Implemented using `EFI_BOOT_SERVICES.OpenProtocol()` +pub(crate) fn open_protocol( + handle: NonNull, + mut protocol_guid: Guid, +) -> io::Result> { + let boot_services = boot_services(); + let system_handle = uefi::env::image_handle(); + let mut protocol: MaybeUninit<*mut T> = MaybeUninit::uninit(); + + let r = unsafe { + ((*boot_services.as_ptr()).open_protocol)( + handle.as_ptr(), + &mut protocol_guid, + protocol.as_mut_ptr().cast(), + system_handle.as_ptr(), + crate::ptr::null_mut(), + r_efi::system::OPEN_PROTOCOL_GET_PROTOCOL, + ) + }; + + if r.is_error() { + Err(status_to_io_error(r)) + } else { + NonNull::new(unsafe { protocol.assume_init() }) + .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + } +} + +pub(crate) fn status_to_io_error(s: r_efi::efi::Status) -> io::Error { + use io::ErrorKind; + use r_efi::efi::Status; + + // Keep the List in Alphabetical Order + // The Messages are taken from UEFI Specification Appendix D - Status Codes + match s { + Status::ABORTED => { + const_io_error!(ErrorKind::ConnectionAborted, "The operation was aborted.") + } + Status::ACCESS_DENIED => { + const_io_error!(ErrorKind::PermissionDenied, "Access was denied.") + } + Status::ALREADY_STARTED => { + const_io_error!(ErrorKind::Other, "The protocol has already been started.") + } + Status::BAD_BUFFER_SIZE => { + const_io_error!( + ErrorKind::InvalidData, + "The buffer was not the proper size for the request." + ) + } + Status::BUFFER_TOO_SMALL => { + const_io_error!( + ErrorKind::FileTooLarge, + "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs." + ) + } + Status::COMPROMISED_DATA => { + const_io_error!( + ErrorKind::Other, + "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status." + ) + } + Status::CONNECTION_FIN => { + const_io_error!( + ErrorKind::Other, + "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance." + ) + } + Status::CONNECTION_REFUSED => { + const_io_error!( + ErrorKind::ConnectionRefused, + "The receiving or transmission operation fails because this connection is refused." + ) + } + Status::CONNECTION_RESET => { + const_io_error!( + ErrorKind::ConnectionReset, + "The connect fails because the connection is reset either by instance itself or the communication peer." + ) + } + Status::CRC_ERROR => const_io_error!(ErrorKind::Other, "A CRC error was detected."), + Status::DEVICE_ERROR => const_io_error!( + ErrorKind::Other, + "The physical device reported an error while attempting the operation." + ), + Status::END_OF_FILE => { + const_io_error!(ErrorKind::UnexpectedEof, "The end of the file was reached.") + } + Status::END_OF_MEDIA => { + const_io_error!(ErrorKind::Other, "Beginning or end of media was reached") + } + Status::HOST_UNREACHABLE => { + const_io_error!(ErrorKind::HostUnreachable, "The remote host is not reachable.") + } + Status::HTTP_ERROR => { + const_io_error!(ErrorKind::Other, "A HTTP error occurred during the network operation.") + } + Status::ICMP_ERROR => { + const_io_error!( + ErrorKind::Other, + "An ICMP error occurred during the network operation." + ) + } + Status::INCOMPATIBLE_VERSION => { + const_io_error!( + ErrorKind::Other, + "The function encountered an internal version that was incompatible with a version requested by the caller." + ) + } + Status::INVALID_LANGUAGE => { + const_io_error!(ErrorKind::InvalidData, "The language specified was invalid.") + } + Status::INVALID_PARAMETER => { + const_io_error!(ErrorKind::InvalidInput, "A parameter was incorrect.") + } + Status::IP_ADDRESS_CONFLICT => { + const_io_error!(ErrorKind::AddrInUse, "There is an address conflict address allocation") + } + Status::LOAD_ERROR => { + const_io_error!(ErrorKind::Other, "The image failed to load.") + } + Status::MEDIA_CHANGED => { + const_io_error!( + ErrorKind::Other, + "The medium in the device has changed since the last access." + ) + } + Status::NETWORK_UNREACHABLE => { + const_io_error!( + ErrorKind::NetworkUnreachable, + "The network containing the remote host is not reachable." + ) + } + Status::NO_MAPPING => { + const_io_error!(ErrorKind::Other, "A mapping to a device does not exist.") + } + Status::NO_MEDIA => { + const_io_error!( + ErrorKind::Other, + "The device does not contain any medium to perform the operation." + ) + } + Status::NO_RESPONSE => { + const_io_error!( + ErrorKind::HostUnreachable, + "The server was not found or did not respond to the request." + ) + } + Status::NOT_FOUND => const_io_error!(ErrorKind::NotFound, "The item was not found."), + Status::NOT_READY => { + const_io_error!(ErrorKind::ResourceBusy, "There is no data pending upon return.") + } + Status::NOT_STARTED => { + const_io_error!(ErrorKind::Other, "The protocol has not been started.") + } + Status::OUT_OF_RESOURCES => { + const_io_error!(ErrorKind::OutOfMemory, "A resource has run out.") + } + Status::PROTOCOL_ERROR => { + const_io_error!( + ErrorKind::Other, + "A protocol error occurred during the network operation." + ) + } + Status::PROTOCOL_UNREACHABLE => { + const_io_error!(ErrorKind::Other, "An ICMP protocol unreachable error is received.") + } + Status::SECURITY_VIOLATION => { + const_io_error!( + ErrorKind::PermissionDenied, + "The function was not performed due to a security violation." + ) + } + Status::TFTP_ERROR => { + const_io_error!(ErrorKind::Other, "A TFTP error occurred during the network operation.") + } + Status::TIMEOUT => const_io_error!(ErrorKind::TimedOut, "The timeout time expired."), + Status::UNSUPPORTED => { + const_io_error!(ErrorKind::Unsupported, "The operation is not supported.") + } + Status::VOLUME_FULL => { + const_io_error!(ErrorKind::StorageFull, "There is no more space on the file system.") + } + Status::VOLUME_CORRUPTED => { + const_io_error!( + ErrorKind::Other, + "An inconstancy was detected on the file system causing the operating to fail." + ) + } + Status::WRITE_PROTECTED => { + const_io_error!(ErrorKind::ReadOnlyFilesystem, "The device cannot be written to.") + } + _ => io::Error::new(ErrorKind::Uncategorized, format!("Status: {}", s.as_usize())), + } +} + +/// Get the BootServices Pointer. +pub(crate) fn boot_services() -> NonNull { + let system_table: NonNull = uefi::env::system_table().cast(); + let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; + NonNull::new(boot_services).unwrap() +} +/// Get the BootServices Pointer. +/// This function is mostly intended for places where panic is not an option +pub(crate) fn try_boot_services() -> Option> { + let system_table: NonNull = uefi::env::try_system_table()?.cast(); + let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; + NonNull::new(boot_services) +} diff --git a/library/std/src/sys/uefi/env.rs b/library/std/src/sys/uefi/env.rs new file mode 100644 index 0000000000000..c106d5fed3e1d --- /dev/null +++ b/library/std/src/sys/uefi/env.rs @@ -0,0 +1,9 @@ +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = "uefi"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ".efi"; + pub const EXE_EXTENSION: &str = "efi"; +} diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs new file mode 100644 index 0000000000000..193d4ecc16fa7 --- /dev/null +++ b/library/std/src/sys/uefi/mod.rs @@ -0,0 +1,155 @@ +//! Platform-specific extensions to `std` for UEFI platforms. +//! +//! Provides access to platform-level information on UEFI platforms, and +//! exposes UEFI-specific functions that would otherwise be inappropriate as +//! part of the core `std` library. +//! +//! It exposes more ways to deal with platform-specific strings ([`OsStr`], +//! [`OsString`]), allows to set permissions more granularly, extract low-level +//! file descriptors from files and sockets, and has platform-specific helpers +//! for spawning processes. +//! +//! [`OsStr`]: crate::ffi::OsStr +//! [`OsString`]: crate::ffi::OsString + +#![deny(unsafe_op_in_unsafe_fn)] +pub mod alloc; +#[path = "../unsupported/args.rs"] +pub mod args; +#[path = "../unix/cmath.rs"] +pub mod cmath; +pub mod env; +#[path = "../unsupported/fs.rs"] +pub mod fs; +#[path = "../unsupported/io.rs"] +pub mod io; +#[path = "../unsupported/locks/mod.rs"] +pub mod locks; +#[path = "../unsupported/net.rs"] +pub mod net; +#[path = "../unsupported/once.rs"] +pub mod once; +#[path = "../unsupported/os.rs"] +pub mod os; +#[path = "../windows/os_str.rs"] +pub mod os_str; +pub mod path; +#[path = "../unsupported/pipe.rs"] +pub mod pipe; +#[path = "../unsupported/process.rs"] +pub mod process; +#[path = "../unsupported/stdio.rs"] +pub mod stdio; +#[path = "../unsupported/thread.rs"] +pub mod thread; +#[path = "../unsupported/thread_local_key.rs"] +pub mod thread_local_key; +#[path = "../unsupported/time.rs"] +pub mod time; + +pub(crate) mod common; + +#[cfg(test)] +mod tests; + +use crate::io as std_io; +use crate::os::uefi; +use crate::ptr::NonNull; + +pub mod memchr { + pub use core::slice::memchr::{memchr, memrchr}; +} + +// SAFETY: must be called only once during runtime initialization. +// SAFETY: argc must be 2. +// SAFETY: argv must be &[Handle, *mut SystemTable]. +pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { + assert_eq!(argc, 2); + let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; + let system_table = unsafe { NonNull::new(*argv.add(1) as *mut crate::ffi::c_void).unwrap() }; + unsafe { crate::os::uefi::env::init_globals(image_handle, system_table) }; +} + +// SAFETY: must be called only once during runtime cleanup. +// NOTE: this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() {} + +#[inline] +pub const fn unsupported() -> std_io::Result { + Err(unsupported_err()) +} + +#[inline] +pub const fn unsupported_err() -> std_io::Error { + std_io::const_io_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI",) +} + +pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { + use crate::io::ErrorKind; + use r_efi::efi::Status; + + if let Ok(code) = usize::try_from(code) { + common::status_to_io_error(Status::from_usize(code)).kind() + } else { + ErrorKind::Uncategorized + } +} + +pub fn abort_internal() -> ! { + if let (Some(boot_services), Some(handle)) = + (common::try_boot_services(), uefi::env::try_image_handle()) + { + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + r_efi::efi::Status::ABORTED, + 0, + crate::ptr::null_mut(), + ) + }; + } + + // In case SystemTable and ImageHandle cannot be reached, use `core::intrinsics::abort` + core::intrinsics::abort(); +} + +// This function is needed by the panic runtime. The symbol is named in +// pre-link args for the target specification, so keep that in sync. +#[cfg(not(test))] +#[no_mangle] +pub extern "C" fn __rust_abort() { + abort_internal(); +} + +#[inline] +pub fn hashmap_random_keys() -> (u64, u64) { + get_random().unwrap() +} + +fn get_random() -> Option<(u64, u64)> { + use r_efi::protocols::rng; + + let mut buf = [0u8; 16]; + let handles = common::locate_handles(rng::PROTOCOL_GUID).ok()?; + for handle in handles { + if let Ok(protocol) = common::open_protocol::(handle, rng::PROTOCOL_GUID) { + let r = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + crate::ptr::null_mut(), + buf.len(), + buf.as_mut_ptr(), + ) + }; + if r.is_error() { + continue; + } else { + return Some(( + u64::from_le_bytes(buf[..8].try_into().ok()?), + u64::from_le_bytes(buf[8..].try_into().ok()?), + )); + } + } + } + None +} diff --git a/library/std/src/sys/uefi/path.rs b/library/std/src/sys/uefi/path.rs new file mode 100644 index 0000000000000..106682eee56b6 --- /dev/null +++ b/library/std/src/sys/uefi/path.rs @@ -0,0 +1,25 @@ +use super::unsupported; +use crate::ffi::OsStr; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; + +pub const MAIN_SEP_STR: &str = "\\"; +pub const MAIN_SEP: char = '\\'; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'\\' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'\\' +} + +pub fn parse_prefix(_p: &OsStr) -> Option> { + None +} + +pub(crate) fn absolute(_path: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/uefi/tests.rs b/library/std/src/sys/uefi/tests.rs new file mode 100644 index 0000000000000..8806eda3ac0a6 --- /dev/null +++ b/library/std/src/sys/uefi/tests.rs @@ -0,0 +1,21 @@ +use super::alloc::*; + +#[test] +fn align() { + // UEFI ABI specifies that allocation alignment minimum is always 8. So this can be + // statically verified. + assert_eq!(POOL_ALIGNMENT, 8); + + // Loop over allocation-request sizes from 0-256 and alignments from 1-128, and verify + // that in case of overalignment there is at least space for one additional pointer to + // store in the allocation. + for i in 0..256 { + for j in &[1, 2, 4, 8, 16, 32, 64, 128] { + if *j <= 8 { + assert_eq!(align_size(i, *j), i); + } else { + assert!(align_size(i, *j) > i + std::mem::size_of::<*mut ()>()); + } + } + } +} diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index f7d821750635d..d65d68337d811 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -44,6 +44,8 @@ cfg_if::cfg_if! { cfg_if::cfg_if! { if #[cfg(any(target_os = "l4re", + target_os = "hermit", + target_os = "uefi", feature = "restricted-std", all(target_family = "wasm", not(target_os = "emscripten")), target_os = "xous", diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 176ef8e92dbec..836328f94ef81 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -532,11 +532,7 @@ pub struct Target { impl Target { pub fn from_triple(triple: &str) -> Self { let mut target: Self = Default::default(); - if triple.contains("-none") - || triple.contains("nvptx") - || triple.contains("switch") - || triple.contains("-uefi") - { + if triple.contains("-none") || triple.contains("nvptx") || triple.contains("switch") { target.no_std = true; } target diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index 03fa284620e4c..8d4d60e12fb1a 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -19,8 +19,8 @@ Available targets: ## Requirements All UEFI targets can be used as `no-std` environments via cross-compilation. -Support for `std` is missing, but actively worked on. `alloc` is supported if -an allocator is provided by the user. No host tools are supported. +Support for `std` is present, but incomplete and extreamly new. `alloc` is supported if +an allocator is provided by the user or if using std. No host tools are supported. The UEFI environment resembles the environment for Microsoft Windows, with some minor differences. Therefore, cross-compiling for UEFI works with the same @@ -230,3 +230,71 @@ pub extern "C" fn main(_h: efi::Handle, st: *mut efi::SystemTable) -> efi::Statu efi::Status::SUCCESS } ``` + +## Rust std for UEFI +This section contains information on how to use std on UEFI. + +### Build std +The building std part is pretty much the same as the official [docs](https://rustc-dev-guide.rust-lang.org/getting-started.html). +The linker that should be used is `rust-lld`. Here is a sample `config.toml`: +```toml +[llvm] +download-ci-llvm = false +[rust] +lld = true +[target.x86_64-unknown-uefi] +linker = "rust-lld" +``` +Then just build using `x.py`: +```sh +./x.py build --target x86_64-unknown-uefi +``` + +### Std Requirements +The current std has a few basic requirements to function: +1. Memory Allocation Services (`EFI_BOOT_SERVICES.AllocatePool()` and + `EFI_BOOT_SERVICES.FreePool()`) are available. +If the above requirement is satisfied, the Rust code will reach `main`. +Now we will discuss what the different modules of std use in UEFI. + +### Implemented features +#### alloc +- Implemented using `EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`. +- Passes all the tests. +- Some Quirks: + - Currently uses `EfiLoaderData` as the `EFI_ALLOCATE_POOL->PoolType`. +#### cmath +- Provided by compiler-builtins. +#### env +- Just some global consants. +#### locks +- Uses `unsupported/locks`. +- They should work for a platform without threads according to docs. +#### os_str +- Uses WTF-8 from windows. + +## Example: Hello World With std +The following code is a valid UEFI application showing stdio in UEFI. It also +uses `alloc` type `OsString` and `Vec`. + +This example can be compiled as binary crate via `cargo` using the toolchain +compiled from the above source (named custom): + +```sh +cargo +custom build --target x86_64-unknown-uefi +``` + +```rust,ignore (platform-specific) +use r_efi::efi; +use std::os::uefi::ffi::OsStrExt; +use std::{ffi::OsString, panic}; + +pub fn main() { + let st = std::os::uefi::env::system_table().as_ptr() as *mut efi::SystemTable; + let mut s: Vec = OsString::from("Hello World!\n").encode_wide().collect(); + s.push(0); + let r = + unsafe { ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut efi::Char16) }; + assert!(!r.is_error()) +} +``` diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 843ffe2c4c332..daa9cb8d22ed5 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -18,6 +18,7 @@ const LICENSES: &[&str] = &[ "Apache-2.0/MIT", "ISC", "MIT / Apache-2.0", + "MIT OR Apache-2.0 OR LGPL-2.1-or-later", // r-efi, r-efi-alloc "MIT OR Apache-2.0 OR Zlib", // tinyvec_macros "MIT OR Apache-2.0", "MIT OR Zlib OR Apache-2.0", // miniz_oxide @@ -217,6 +218,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "pulldown-cmark", "punycode", "quote", + "r-efi", + "r-efi-alloc", "rand", "rand_chacha", "rand_core", From 8e56b33d59593a99fdef6f73f0c1a09a012ca907 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sat, 28 Jan 2023 23:37:54 +0530 Subject: [PATCH 069/149] Fixes from PR Signed-off-by: Ayush Singh --- library/std/src/os/uefi/env.rs | 11 +++-- library/std/src/sys/uefi/alloc.rs | 12 ++--- .../src/sys/uefi/{common.rs => helpers.rs} | 37 ++++++++++------ library/std/src/sys/uefi/mod.rs | 24 +++++----- library/std/src/sys_common/mod.rs | 1 - .../src/platform-support/unknown-uefi.md | 44 ++++++++++--------- 6 files changed, 70 insertions(+), 59 deletions(-) rename library/std/src/sys/uefi/{common.rs => helpers.rs} (86%) diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index 479ff5f69757e..71bf0ff8ff3f5 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -1,5 +1,7 @@ //! UEFI-specific extensions to the primitives in `std::env` module +#![unstable(feature = "uefi_std", issue = "100499")] + use crate::ffi::c_void; use crate::ptr::NonNull; use crate::sync::atomic::{AtomicPtr, Ordering}; @@ -22,21 +24,18 @@ static GLOBALS: OnceLock<(AtomicPtr, AtomicPtr)> = OnceLock::new /// standard library is loaded. /// /// This function must not be called more than once. -#[unstable(feature = "uefi_std", issue = "100499")] pub unsafe fn init_globals(handle: NonNull, system_table: NonNull) { GLOBALS.set((AtomicPtr::new(system_table.as_ptr()), AtomicPtr::new(handle.as_ptr()))).unwrap() } /// Get the SystemTable Pointer. -/// Note: This function panics if the System Table and Image Handle is Not initialized -#[unstable(feature = "uefi_std", issue = "100499")] +/// Note: This function panics if the System Table or Image Handle is not initialized pub fn system_table() -> NonNull { try_system_table().unwrap() } -/// Get the SystemHandle Pointer. -/// Note: This function panics if the System Table and Image Handle is Not initialized -#[unstable(feature = "uefi_std", issue = "100499")] +/// Get the ImageHandle Pointer. +/// Note: This function panics if the System Table or Image Handle is not initialized pub fn image_handle() -> NonNull { try_image_handle().unwrap() } diff --git a/library/std/src/sys/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs index 6a14ee76e92ce..54d86d0c8a75a 100644 --- a/library/std/src/sys/uefi/alloc.rs +++ b/library/std/src/sys/uefi/alloc.rs @@ -13,11 +13,8 @@ unsafe impl GlobalAlloc for System { Some(x) => x.as_ptr() as *mut _, }; - if layout.size() > 0 { - unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) } - } else { - layout.dangling().as_ptr() - } + // The caller must ensure non-0 layout + unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) } } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { @@ -25,8 +22,7 @@ unsafe impl GlobalAlloc for System { None => handle_alloc_error(layout), Some(x) => x.as_ptr() as *mut _, }; - if layout.size() > 0 { - unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } - } + // The caller must ensure non-0 layout + unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } } } diff --git a/library/std/src/sys/uefi/common.rs b/library/std/src/sys/uefi/helpers.rs similarity index 86% rename from library/std/src/sys/uefi/common.rs rename to library/std/src/sys/uefi/helpers.rs index 6316a74d3670c..e83adabaf1622 100644 --- a/library/std/src/sys/uefi/common.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -1,16 +1,25 @@ //! Contains most of the shared UEFI specific stuff. Some of this might be moved to `std::os::uefi` //! if needed but no point in adding extra public API when there is not Std support for UEFI in the //! first place +//! +//! Some Nomenclature +//! * Protocol: +//! - Protocols serve to enable communication between separately built modules, including drivers. +//! - Every protocol has a GUID associated with it. The GUID serves as the name for the protocol. +//! - Protocols are produced and consumed. +//! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::Guid; use crate::io::{self, const_io_error}; -use crate::mem::MaybeUninit; +use crate::mem::{size_of, MaybeUninit}; use crate::os::uefi; use crate::ptr::NonNull; -// Locate handles with a particular protocol GUID +/// Locate Handles with a particular Protocol GUID /// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` +/// +/// Returns an array of [Handles](r_efi::efi::Handle) that support a specified protocol. pub(crate) fn locate_handles(mut guid: Guid) -> io::Result>> { fn inner( guid: &mut Guid, @@ -34,6 +43,8 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result unreachable!(), Err(e) => match e.kind() { @@ -44,21 +55,23 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result = - Vec::with_capacity(buf_len / crate::mem::size_of::()); + Vec::with_capacity(buf_len / size_of::()); match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { Ok(()) => { - // SAFETY: This is safe because the call will succeed only if buf_len >= required - // length. Also, on success, the `buf_len` is updated with the size of bufferv (in - // bytes) written - unsafe { buf.set_len(buf_len / crate::mem::size_of::()) }; - Ok(buf.iter().filter_map(|x| NonNull::new(*x)).collect()) + // This is safe because the call will succeed only if buf_len >= required length. + // Also, on success, the `buf_len` is updated with the size of bufferv (in bytes) written + unsafe { buf.set_len(buf_len / size_of::()) }; + Ok(buf.into_iter().filter_map(|x| NonNull::new(x)).collect()) } Err(e) => Err(e), } } -/// Open Protocol on a handle -/// Implemented using `EFI_BOOT_SERVICES.OpenProtocol()` +/// Open Protocol on a handle. +/// Internally just a call to `EFI_BOOT_SERVICES.OpenProtocol()`. +/// +/// Queries a handle to determine if it supports a specified protocol. If the protocol is +/// supported by the handle, it opens the protocol on behalf of the calling agent. pub(crate) fn open_protocol( handle: NonNull, mut protocol_guid: Guid, @@ -256,9 +269,7 @@ pub(crate) fn status_to_io_error(s: r_efi::efi::Status) -> io::Error { /// Get the BootServices Pointer. pub(crate) fn boot_services() -> NonNull { - let system_table: NonNull = uefi::env::system_table().cast(); - let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; - NonNull::new(boot_services).unwrap() + try_boot_services().unwrap() } /// Get the BootServices Pointer. /// This function is mostly intended for places where panic is not an option diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index 193d4ecc16fa7..fed44b4f49dac 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -47,7 +47,7 @@ pub mod thread_local_key; #[path = "../unsupported/time.rs"] pub mod time; -pub(crate) mod common; +pub(crate) mod helpers; #[cfg(test)] mod tests; @@ -60,18 +60,20 @@ pub mod memchr { pub use core::slice::memchr::{memchr, memrchr}; } -// SAFETY: must be called only once during runtime initialization. -// SAFETY: argc must be 2. -// SAFETY: argv must be &[Handle, *mut SystemTable]. -pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { +/// # SAFETY +/// - must be called only once during runtime initialization. +/// - argc must be 2. +/// - argv must be &[Handle, *mut SystemTable]. +pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { assert_eq!(argc, 2); let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; let system_table = unsafe { NonNull::new(*argv.add(1) as *mut crate::ffi::c_void).unwrap() }; unsafe { crate::os::uefi::env::init_globals(image_handle, system_table) }; } -// SAFETY: must be called only once during runtime cleanup. -// NOTE: this is not guaranteed to run, for example when the program aborts. +/// # SAFETY +/// this is not guaranteed to run, for example when the program aborts. +/// - must be called only once during runtime cleanup. pub unsafe fn cleanup() {} #[inline] @@ -89,7 +91,7 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { use r_efi::efi::Status; if let Ok(code) = usize::try_from(code) { - common::status_to_io_error(Status::from_usize(code)).kind() + helpers::status_to_io_error(Status::from_usize(code)).kind() } else { ErrorKind::Uncategorized } @@ -97,7 +99,7 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { if let (Some(boot_services), Some(handle)) = - (common::try_boot_services(), uefi::env::try_image_handle()) + (helpers::try_boot_services(), uefi::env::try_image_handle()) { let _ = unsafe { ((*boot_services.as_ptr()).exit)( @@ -130,9 +132,9 @@ fn get_random() -> Option<(u64, u64)> { use r_efi::protocols::rng; let mut buf = [0u8; 16]; - let handles = common::locate_handles(rng::PROTOCOL_GUID).ok()?; + let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?; for handle in handles { - if let Ok(protocol) = common::open_protocol::(handle, rng::PROTOCOL_GUID) { + if let Ok(protocol) = helpers::open_protocol::(handle, rng::PROTOCOL_GUID) { let r = unsafe { ((*protocol.as_ptr()).get_rng)( protocol.as_ptr(), diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index d65d68337d811..e18638f2a5f54 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -44,7 +44,6 @@ cfg_if::cfg_if! { cfg_if::cfg_if! { if #[cfg(any(target_os = "l4re", - target_os = "hermit", target_os = "uefi", feature = "restricted-std", all(target_family = "wasm", not(target_os = "emscripten")), diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index 8d4d60e12fb1a..1a4cbc9f092f0 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -19,7 +19,7 @@ Available targets: ## Requirements All UEFI targets can be used as `no-std` environments via cross-compilation. -Support for `std` is present, but incomplete and extreamly new. `alloc` is supported if +Support for `std` is present, but incomplete and extremely new. `alloc` is supported if an allocator is provided by the user or if using std. No host tools are supported. The UEFI environment resembles the environment for Microsoft Windows, with some @@ -238,22 +238,22 @@ This section contains information on how to use std on UEFI. The building std part is pretty much the same as the official [docs](https://rustc-dev-guide.rust-lang.org/getting-started.html). The linker that should be used is `rust-lld`. Here is a sample `config.toml`: ```toml -[llvm] -download-ci-llvm = false [rust] lld = true -[target.x86_64-unknown-uefi] -linker = "rust-lld" ``` Then just build using `x.py`: ```sh -./x.py build --target x86_64-unknown-uefi +./x.py build --target x86_64-unknown-uefi --stage 1 +``` +Alternatively, it is possible to use the `build-std` feature. However, you must use a toolchain which has the UEFI std patches. +Then just build the project using the following command: +```sh +cargo build --target x86_64-unknown-uefi -Zbuild-std=std,panic_abort ``` ### Std Requirements The current std has a few basic requirements to function: -1. Memory Allocation Services (`EFI_BOOT_SERVICES.AllocatePool()` and - `EFI_BOOT_SERVICES.FreePool()`) are available. +1. Memory Allocation Services (`EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`) are available. This should be true in in the Driver Execution Environment or later. If the above requirement is satisfied, the Rust code will reach `main`. Now we will discuss what the different modules of std use in UEFI. @@ -261,21 +261,19 @@ Now we will discuss what the different modules of std use in UEFI. #### alloc - Implemented using `EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`. - Passes all the tests. -- Some Quirks: - - Currently uses `EfiLoaderData` as the `EFI_ALLOCATE_POOL->PoolType`. +- Currently uses `EfiLoaderData` as the `EFI_ALLOCATE_POOL->PoolType`. #### cmath - Provided by compiler-builtins. #### env -- Just some global consants. +- Just some global constants. #### locks -- Uses `unsupported/locks`. -- They should work for a platform without threads according to docs. +- The provided locks should work on all standard single-threaded UEFI implementations. #### os_str -- Uses WTF-8 from windows. +- While the strings in UEFI should be valid UCS-2, in practice, many implementations just do not care and use UTF-16 strings. +- Thus, the current implementation supports full UTF-16 strings. ## Example: Hello World With std -The following code is a valid UEFI application showing stdio in UEFI. It also -uses `alloc` type `OsString` and `Vec`. +The following code features a valid UEFI application, including stdio and `alloc` (`OsString` and `Vec`): This example can be compiled as binary crate via `cargo` using the toolchain compiled from the above source (named custom): @@ -286,15 +284,21 @@ cargo +custom build --target x86_64-unknown-uefi ```rust,ignore (platform-specific) use r_efi::efi; -use std::os::uefi::ffi::OsStrExt; -use std::{ffi::OsString, panic}; +use std::{ + ffi::OsString, + os::uefi::{env, ffi::OsStrExt} +}; pub fn main() { - let st = std::os::uefi::env::system_table().as_ptr() as *mut efi::SystemTable; + let st = env::system_table().as_ptr() as *mut efi::SystemTable; let mut s: Vec = OsString::from("Hello World!\n").encode_wide().collect(); s.push(0); let r = - unsafe { ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut efi::Char16) }; + unsafe { + let con_out: *mut simple_text_output::Protocol = (*st).con_out; + let output_string: extern "efiapi" fn(_: *mut simple_text_output::Protocol, *mut u16) = (*con_out).output_string; + output_string(con_out, s.as_ptr() as *mut efi::Char16) + }; assert!(!r.is_error()) } ``` From 032e3766d54fe1b91ed13ff12652f91354d019cb Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sat, 25 Mar 2023 14:20:49 +0530 Subject: [PATCH 070/149] Handle ExitBootServices - Make BootServices unavailable if ExitBootServices event is signaled. - Use thread locals for SystemTable and ImageHandle Signed-off-by: Ayush Singh --- library/std/src/os/uefi/env.rs | 43 +++++++++++++++++++------ library/std/src/sys/uefi/alloc.rs | 10 ++++++ library/std/src/sys/uefi/helpers.rs | 50 +++++++++++++++++++++-------- library/std/src/sys/uefi/mod.rs | 39 ++++++++++++++++++++-- 4 files changed, 116 insertions(+), 26 deletions(-) diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index 71bf0ff8ff3f5..e62441596f3a7 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -2,14 +2,17 @@ #![unstable(feature = "uefi_std", issue = "100499")] -use crate::ffi::c_void; -use crate::ptr::NonNull; -use crate::sync::atomic::{AtomicPtr, Ordering}; -use crate::sync::OnceLock; +use crate::{cell::Cell, ffi::c_void, ptr::NonNull}; -// Position 0 = SystemTable -// Position 1 = ImageHandle -static GLOBALS: OnceLock<(AtomicPtr, AtomicPtr)> = OnceLock::new(); +// Since UEFI is single-threaded, making the global variables thread local should be safe. +thread_local! { + // Flag to check if BootServices are still valid. + // Start with assuming that they are not available + static BOOT_SERVICES_FLAG: Cell = Cell::new(false); + // Position 0 = SystemTable + // Position 1 = ImageHandle + static GLOBALS: Cell, NonNull)>> = Cell::new(None); +} /// Initializes the global System Table and Image Handle pointers. /// @@ -25,7 +28,7 @@ static GLOBALS: OnceLock<(AtomicPtr, AtomicPtr)> = OnceLock::new /// /// This function must not be called more than once. pub unsafe fn init_globals(handle: NonNull, system_table: NonNull) { - GLOBALS.set((AtomicPtr::new(system_table.as_ptr()), AtomicPtr::new(handle.as_ptr()))).unwrap() + GLOBALS.set(Some((system_table, handle))); } /// Get the SystemTable Pointer. @@ -43,11 +46,31 @@ pub fn image_handle() -> NonNull { /// Get the SystemTable Pointer. /// This function is mostly intended for places where panic is not an option pub(crate) fn try_system_table() -> Option> { - NonNull::new(GLOBALS.get()?.0.load(Ordering::Acquire)) + GLOBALS.get().map(|x| x.0) } /// Get the SystemHandle Pointer. /// This function is mostly intended for places where panic is not an option pub(crate) fn try_image_handle() -> Option> { - NonNull::new(GLOBALS.get()?.1.load(Ordering::Acquire)) + GLOBALS.get().map(|x| x.1) +} + +/// Get the BootServices Pointer. +/// This function also checks if `ExitBootServices` has already been called. +pub(crate) fn boot_services() -> Option> { + if BOOT_SERVICES_FLAG.get() { + let system_table: NonNull = try_system_table()?.cast(); + let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; + NonNull::new(boot_services) + } else { + None + } +} + +pub(crate) fn enable_boot_services() { + BOOT_SERVICES_FLAG.set(true); +} + +pub(crate) fn disable_boot_services() { + BOOT_SERVICES_FLAG.set(false); } diff --git a/library/std/src/sys/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs index 54d86d0c8a75a..d6eb371033ce6 100644 --- a/library/std/src/sys/uefi/alloc.rs +++ b/library/std/src/sys/uefi/alloc.rs @@ -8,6 +8,11 @@ const MEMORY_TYPE: u32 = r_efi::efi::LOADER_DATA; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // Return null pointer if boot services are not available + if crate::os::uefi::env::boot_services().is_none() { + return crate::ptr::null_mut(); + } + let system_table = match crate::os::uefi::env::try_system_table() { None => return crate::ptr::null_mut(), Some(x) => x.as_ptr() as *mut _, @@ -18,6 +23,11 @@ unsafe impl GlobalAlloc for System { } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // Do nothing if boot services are not available + if crate::os::uefi::env::boot_services().is_none() { + return; + } + let system_table = match crate::os::uefi::env::try_system_table() { None => handle_alloc_error(layout), Some(x) => x.as_ptr() as *mut _, diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs index e83adabaf1622..28a8239422688 100644 --- a/library/std/src/sys/uefi/helpers.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -9,12 +9,18 @@ //! - Protocols are produced and consumed. //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) -use r_efi::efi::Guid; +use r_efi::efi::{self, Guid}; -use crate::io::{self, const_io_error}; use crate::mem::{size_of, MaybeUninit}; use crate::os::uefi; use crate::ptr::NonNull; +use crate::{ + io::{self, const_io_error}, + os::uefi::env::boot_services, +}; + +const BOOT_SERVICES_UNAVAILABLE: io::Error = + const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); /// Locate Handles with a particular Protocol GUID /// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` @@ -40,7 +46,7 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result( handle: NonNull, mut protocol_guid: Guid, ) -> io::Result> { - let boot_services = boot_services(); + let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; let system_handle = uefi::env::image_handle(); let mut protocol: MaybeUninit<*mut T> = MaybeUninit::uninit(); @@ -267,14 +273,32 @@ pub(crate) fn status_to_io_error(s: r_efi::efi::Status) -> io::Error { } } -/// Get the BootServices Pointer. -pub(crate) fn boot_services() -> NonNull { - try_boot_services().unwrap() +pub(crate) fn create_event( + signal: u32, + tpl: efi::Tpl, + handler: Option, + context: *mut crate::ffi::c_void, +) -> io::Result> { + let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; + let mut exit_boot_service_event: r_efi::efi::Event = crate::ptr::null_mut(); + let r = unsafe { + let create_event = (*boot_services.as_ptr()).create_event; + (create_event)(signal, tpl, handler, context, &mut exit_boot_service_event) + }; + if r.is_error() { + Err(status_to_io_error(r)) + } else { + NonNull::new(exit_boot_service_event) + .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + } } -/// Get the BootServices Pointer. -/// This function is mostly intended for places where panic is not an option -pub(crate) fn try_boot_services() -> Option> { - let system_table: NonNull = uefi::env::try_system_table()?.cast(); - let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; - NonNull::new(boot_services) + +pub(crate) fn close_event(evt: NonNull) -> io::Result<()> { + let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; + let r = unsafe { + let close_event = (*boot_services.as_ptr()).close_event; + (close_event)(evt.as_ptr()) + }; + + if r.is_error() { Err(status_to_io_error(r)) } else { Ok(()) } } diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index fed44b4f49dac..fa33b681a19d5 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -52,6 +52,7 @@ pub(crate) mod helpers; #[cfg(test)] mod tests; +use crate::cell::Cell; use crate::io as std_io; use crate::os::uefi; use crate::ptr::NonNull; @@ -60,6 +61,10 @@ pub mod memchr { pub use core::slice::memchr::{memchr, memrchr}; } +thread_local! { + static EXIT_BOOT_SERVICE_EVENT: Cell>> = Cell::new(None); +} + /// # SAFETY /// - must be called only once during runtime initialization. /// - argc must be 2. @@ -68,13 +73,32 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { assert_eq!(argc, 2); let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; let system_table = unsafe { NonNull::new(*argv.add(1) as *mut crate::ffi::c_void).unwrap() }; - unsafe { crate::os::uefi::env::init_globals(image_handle, system_table) }; + unsafe { uefi::env::init_globals(image_handle, system_table) }; + // Enable boot services once GLOBALS are initialized + uefi::env::enable_boot_services(); + + // Register exit boot services handler + match helpers::create_event( + r_efi::efi::EVT_SIGNAL_EXIT_BOOT_SERVICES, + r_efi::efi::TPL_NOTIFY, + Some(exit_boot_service_handler), + crate::ptr::null_mut(), + ) { + Ok(x) => { + EXIT_BOOT_SERVICE_EVENT.set(Some(x)); + } + Err(_) => abort_internal(), + } } /// # SAFETY /// this is not guaranteed to run, for example when the program aborts. /// - must be called only once during runtime cleanup. -pub unsafe fn cleanup() {} +pub unsafe fn cleanup() { + if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { + let _ = helpers::close_event(exit_boot_service_event); + } +} #[inline] pub const fn unsupported() -> std_io::Result { @@ -98,8 +122,12 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { } pub fn abort_internal() -> ! { + if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { + let _ = helpers::close_event(exit_boot_service_event); + } + if let (Some(boot_services), Some(handle)) = - (helpers::try_boot_services(), uefi::env::try_image_handle()) + (uefi::env::boot_services(), uefi::env::try_image_handle()) { let _ = unsafe { ((*boot_services.as_ptr()).exit)( @@ -155,3 +183,8 @@ fn get_random() -> Option<(u64, u64)> { } None } + +/// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled +extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { + uefi::env::disable_boot_services(); +} From 5df24d18b6cdb8abcede6b658cf066360a219c12 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Mon, 27 Mar 2023 19:53:53 +0530 Subject: [PATCH 071/149] Add support for building `std::os::uefi` docs Signed-off-by: Ayush Singh --- library/std/src/os/mod.rs | 1 + library/std/src/os/uefi/env.rs | 32 +++++++++++-------- library/std/src/os/uefi/mod.rs | 1 + library/std/src/sys/uefi/alloc.rs | 15 +++------ library/std/src/sys/uefi/helpers.rs | 11 ++++--- library/std/src/sys/uefi/mod.rs | 1 + .../src/platform-support/unknown-uefi.md | 11 +++---- 7 files changed, 38 insertions(+), 34 deletions(-) diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 11ad21515fdec..d1a735038992c 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -143,6 +143,7 @@ pub mod solid; #[path = "ios/mod.rs"] pub(crate) mod tvos; #[cfg(target_os = "uefi")] +#[cfg(any(target_os = "uefi", doc))] pub mod uefi; #[cfg(target_os = "vita")] pub mod vita; diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index e62441596f3a7..41dc7ccdb6ade 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -32,41 +32,45 @@ pub unsafe fn init_globals(handle: NonNull, system_table: NonNull NonNull { try_system_table().unwrap() } /// Get the ImageHandle Pointer. +/// /// Note: This function panics if the System Table or Image Handle is not initialized pub fn image_handle() -> NonNull { try_image_handle().unwrap() } -/// Get the SystemTable Pointer. -/// This function is mostly intended for places where panic is not an option -pub(crate) fn try_system_table() -> Option> { - GLOBALS.get().map(|x| x.0) -} - -/// Get the SystemHandle Pointer. -/// This function is mostly intended for places where panic is not an option -pub(crate) fn try_image_handle() -> Option> { - GLOBALS.get().map(|x| x.1) -} - /// Get the BootServices Pointer. /// This function also checks if `ExitBootServices` has already been called. -pub(crate) fn boot_services() -> Option> { +pub fn boot_services() -> Option> { if BOOT_SERVICES_FLAG.get() { let system_table: NonNull = try_system_table()?.cast(); let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; - NonNull::new(boot_services) + NonNull::new(boot_services).map(|x| x.cast()) } else { None } } +/// Get the SystemTable Pointer. +/// This function is mostly intended for places where panic is not an option +pub(crate) fn try_system_table() -> Option> { + GLOBALS.get().map(|x| x.0) +} + +/// Get the SystemHandle Pointer. +/// This function is mostly intended for places where panic is not an option +pub(crate) fn try_image_handle() -> Option> { + GLOBALS.get().map(|x| x.1) +} + pub(crate) fn enable_boot_services() { BOOT_SERVICES_FLAG.set(true); } diff --git a/library/std/src/os/uefi/mod.rs b/library/std/src/os/uefi/mod.rs index fc0468f25fc8e..8ef05eee1f4e7 100644 --- a/library/std/src/os/uefi/mod.rs +++ b/library/std/src/os/uefi/mod.rs @@ -1,6 +1,7 @@ //! Platform-specific extensions to `std` for UEFI. #![unstable(feature = "uefi_std", issue = "100499")] +#![doc(cfg(target_os = "uefi"))] pub mod env; #[path = "../windows/ffi.rs"] diff --git a/library/std/src/sys/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs index d6eb371033ce6..789e3cbd81ade 100644 --- a/library/std/src/sys/uefi/alloc.rs +++ b/library/std/src/sys/uefi/alloc.rs @@ -1,7 +1,7 @@ //! Global Allocator for UEFI. //! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc) -use crate::alloc::{handle_alloc_error, GlobalAlloc, Layout, System}; +use crate::alloc::{GlobalAlloc, Layout, System}; const MEMORY_TYPE: u32 = r_efi::efi::LOADER_DATA; @@ -13,11 +13,8 @@ unsafe impl GlobalAlloc for System { return crate::ptr::null_mut(); } - let system_table = match crate::os::uefi::env::try_system_table() { - None => return crate::ptr::null_mut(), - Some(x) => x.as_ptr() as *mut _, - }; - + // If boot services is valid then SystemTable is not null. + let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); // The caller must ensure non-0 layout unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) } } @@ -28,10 +25,8 @@ unsafe impl GlobalAlloc for System { return; } - let system_table = match crate::os::uefi::env::try_system_table() { - None => handle_alloc_error(layout), - Some(x) => x.as_ptr() as *mut _, - }; + // If boot services is valid then SystemTable is not null. + let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); // The caller must ensure non-0 layout unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } } diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs index 28a8239422688..2108484a40c3d 100644 --- a/library/std/src/sys/uefi/helpers.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -46,7 +46,7 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result( handle: NonNull, mut protocol_guid: Guid, ) -> io::Result> { - let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let system_handle = uefi::env::image_handle(); let mut protocol: MaybeUninit<*mut T> = MaybeUninit::uninit(); @@ -279,7 +280,8 @@ pub(crate) fn create_event( handler: Option, context: *mut crate::ffi::c_void, ) -> io::Result> { - let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let mut exit_boot_service_event: r_efi::efi::Event = crate::ptr::null_mut(); let r = unsafe { let create_event = (*boot_services.as_ptr()).create_event; @@ -294,7 +296,8 @@ pub(crate) fn create_event( } pub(crate) fn close_event(evt: NonNull) -> io::Result<()> { - let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let r = unsafe { let close_event = (*boot_services.as_ptr()).close_event; (close_event)(evt.as_ptr()) diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index fa33b681a19d5..6fd2c5f21cb4e 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -129,6 +129,7 @@ pub fn abort_internal() -> ! { if let (Some(boot_services), Some(handle)) = (uefi::env::boot_services(), uefi::env::try_image_handle()) { + let boot_services: NonNull = boot_services.cast(); let _ = unsafe { ((*boot_services.as_ptr()).exit)( handle.as_ptr(), diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index 1a4cbc9f092f0..e2c09d67bedab 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -251,12 +251,6 @@ Then just build the project using the following command: cargo build --target x86_64-unknown-uefi -Zbuild-std=std,panic_abort ``` -### Std Requirements -The current std has a few basic requirements to function: -1. Memory Allocation Services (`EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`) are available. This should be true in in the Driver Execution Environment or later. -If the above requirement is satisfied, the Rust code will reach `main`. -Now we will discuss what the different modules of std use in UEFI. - ### Implemented features #### alloc - Implemented using `EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`. @@ -302,3 +296,8 @@ pub fn main() { assert!(!r.is_error()) } ``` + +### BootServices +The current implementation of std make `BootServices` unavailable once `ExitBootServices` is called. Refer to [Runtime Drivers](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/7_driver_entry_point/711_runtime_drivers) for more information regarding how to handle switching from using physical addresses to using virtual addresses. + +Note: It should be noted that it is upto the user to drop all allocated memory before `ExitBootServices` is called. From 7a956441a15e6056448233d79f2d03581ce0ccfc Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Tue, 28 Mar 2023 20:14:33 +0530 Subject: [PATCH 072/149] Fixes from PR - Some comment fixes. - Make some functions unsafe. - Make helpers module private. - Rebase on master - Update r-efi to v4.2.0 Signed-off-by: Ayush Singh --- .nlsp-settings/rust_analyzer.json | 3 --- Cargo.lock | 4 ++-- library/std/Cargo.toml | 2 +- library/std/src/os/uefi/env.rs | 2 +- library/std/src/sys/uefi/helpers.rs | 4 +++- library/std/src/sys/uefi/mod.rs | 6 +++--- src/doc/rustc/src/platform-support/unknown-uefi.md | 2 +- 7 files changed, 11 insertions(+), 12 deletions(-) delete mode 100644 .nlsp-settings/rust_analyzer.json diff --git a/.nlsp-settings/rust_analyzer.json b/.nlsp-settings/rust_analyzer.json deleted file mode 100644 index eddf84afb2a3d..0000000000000 --- a/.nlsp-settings/rust_analyzer.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rust-analyzer.cargo.target": "x86_64-unknown-uefi" -} diff --git a/Cargo.lock b/Cargo.lock index f84387f30be61..a2025e58a8d1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3008,9 +3008,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "4.1.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7345c622833c6745e7b027a28aa95618813dc1f3c3de396206410267dce6f3" +checksum = "575fc2d9b3da54adbdfaddf6eca48fec256d977c8630a1750b8991347d1ac911" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 1908dfaa33db0..965132bdedb91 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -49,7 +49,7 @@ hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] -r-efi = { version = "4.1.0", features = ['rustc-dep-of-std', 'efiapi']} +r-efi = { version = "4.2.0", features = ['rustc-dep-of-std']} r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std']} [features] diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index 41dc7ccdb6ade..4dd090e22c620 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -66,7 +66,7 @@ pub(crate) fn try_system_table() -> Option> { } /// Get the SystemHandle Pointer. -/// This function is mostly intended for places where panic is not an option +/// This function is mostly intended for places where panicking is not an option pub(crate) fn try_image_handle() -> Option> { GLOBALS.get().map(|x| x.1) } diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs index 2108484a40c3d..ea1c68a90ef83 100644 --- a/library/std/src/sys/uefi/helpers.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -295,7 +295,9 @@ pub(crate) fn create_event( } } -pub(crate) fn close_event(evt: NonNull) -> io::Result<()> { +/// # SAFETY +/// - The supplied event must be valid +pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result<()> { let boot_services: NonNull = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let r = unsafe { diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index 6fd2c5f21cb4e..27f76f04c05aa 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -47,7 +47,7 @@ pub mod thread_local_key; #[path = "../unsupported/time.rs"] pub mod time; -pub(crate) mod helpers; +mod helpers; #[cfg(test)] mod tests; @@ -96,7 +96,7 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { /// - must be called only once during runtime cleanup. pub unsafe fn cleanup() { if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { - let _ = helpers::close_event(exit_boot_service_event); + let _ = unsafe { helpers::close_event(exit_boot_service_event) }; } } @@ -123,7 +123,7 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { - let _ = helpers::close_event(exit_boot_service_event); + let _ = unsafe { helpers::close_event(exit_boot_service_event) }; } if let (Some(boot_services), Some(handle)) = diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index e2c09d67bedab..019f030eb1c10 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -300,4 +300,4 @@ pub fn main() { ### BootServices The current implementation of std make `BootServices` unavailable once `ExitBootServices` is called. Refer to [Runtime Drivers](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/7_driver_entry_point/711_runtime_drivers) for more information regarding how to handle switching from using physical addresses to using virtual addresses. -Note: It should be noted that it is upto the user to drop all allocated memory before `ExitBootServices` is called. +Note: It should be noted that it is upto the user to drop all allocated memory before `ExitBootServices` is called. From 40c3dacc767a4fbf42ea5dfa686a54acf9ded5fb Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Thu, 11 May 2023 11:35:15 +0530 Subject: [PATCH 073/149] Use RawOsError for UEFI Some changes from this commit will probably be converted to its own PR. Signed-off-by: Ayush Singh --- library/std/src/sys/mod.rs | 1 + library/std/src/sys/uefi/helpers.rs | 176 +---------------------- library/std/src/sys/uefi/mod.rs | 49 ++++++- library/std/src/sys/uefi/os.rs | 213 ++++++++++++++++++++++++++++ 4 files changed, 261 insertions(+), 178 deletions(-) create mode 100644 library/std/src/sys/uefi/os.rs diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index a03501e6a1357..159ffe7ac9635 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -117,4 +117,5 @@ pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { log_fn(n) } +#[cfg(not(target_os = "uefi"))] pub type RawOsError = i32; diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs index ea1c68a90ef83..2e1bcb369447e 100644 --- a/library/std/src/sys/uefi/helpers.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -43,7 +43,7 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result( }; if r.is_error() { - Err(status_to_io_error(r)) + Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { NonNull::new(unsafe { protocol.assume_init() }) .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) } } -pub(crate) fn status_to_io_error(s: r_efi::efi::Status) -> io::Error { - use io::ErrorKind; - use r_efi::efi::Status; - - // Keep the List in Alphabetical Order - // The Messages are taken from UEFI Specification Appendix D - Status Codes - match s { - Status::ABORTED => { - const_io_error!(ErrorKind::ConnectionAborted, "The operation was aborted.") - } - Status::ACCESS_DENIED => { - const_io_error!(ErrorKind::PermissionDenied, "Access was denied.") - } - Status::ALREADY_STARTED => { - const_io_error!(ErrorKind::Other, "The protocol has already been started.") - } - Status::BAD_BUFFER_SIZE => { - const_io_error!( - ErrorKind::InvalidData, - "The buffer was not the proper size for the request." - ) - } - Status::BUFFER_TOO_SMALL => { - const_io_error!( - ErrorKind::FileTooLarge, - "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs." - ) - } - Status::COMPROMISED_DATA => { - const_io_error!( - ErrorKind::Other, - "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status." - ) - } - Status::CONNECTION_FIN => { - const_io_error!( - ErrorKind::Other, - "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance." - ) - } - Status::CONNECTION_REFUSED => { - const_io_error!( - ErrorKind::ConnectionRefused, - "The receiving or transmission operation fails because this connection is refused." - ) - } - Status::CONNECTION_RESET => { - const_io_error!( - ErrorKind::ConnectionReset, - "The connect fails because the connection is reset either by instance itself or the communication peer." - ) - } - Status::CRC_ERROR => const_io_error!(ErrorKind::Other, "A CRC error was detected."), - Status::DEVICE_ERROR => const_io_error!( - ErrorKind::Other, - "The physical device reported an error while attempting the operation." - ), - Status::END_OF_FILE => { - const_io_error!(ErrorKind::UnexpectedEof, "The end of the file was reached.") - } - Status::END_OF_MEDIA => { - const_io_error!(ErrorKind::Other, "Beginning or end of media was reached") - } - Status::HOST_UNREACHABLE => { - const_io_error!(ErrorKind::HostUnreachable, "The remote host is not reachable.") - } - Status::HTTP_ERROR => { - const_io_error!(ErrorKind::Other, "A HTTP error occurred during the network operation.") - } - Status::ICMP_ERROR => { - const_io_error!( - ErrorKind::Other, - "An ICMP error occurred during the network operation." - ) - } - Status::INCOMPATIBLE_VERSION => { - const_io_error!( - ErrorKind::Other, - "The function encountered an internal version that was incompatible with a version requested by the caller." - ) - } - Status::INVALID_LANGUAGE => { - const_io_error!(ErrorKind::InvalidData, "The language specified was invalid.") - } - Status::INVALID_PARAMETER => { - const_io_error!(ErrorKind::InvalidInput, "A parameter was incorrect.") - } - Status::IP_ADDRESS_CONFLICT => { - const_io_error!(ErrorKind::AddrInUse, "There is an address conflict address allocation") - } - Status::LOAD_ERROR => { - const_io_error!(ErrorKind::Other, "The image failed to load.") - } - Status::MEDIA_CHANGED => { - const_io_error!( - ErrorKind::Other, - "The medium in the device has changed since the last access." - ) - } - Status::NETWORK_UNREACHABLE => { - const_io_error!( - ErrorKind::NetworkUnreachable, - "The network containing the remote host is not reachable." - ) - } - Status::NO_MAPPING => { - const_io_error!(ErrorKind::Other, "A mapping to a device does not exist.") - } - Status::NO_MEDIA => { - const_io_error!( - ErrorKind::Other, - "The device does not contain any medium to perform the operation." - ) - } - Status::NO_RESPONSE => { - const_io_error!( - ErrorKind::HostUnreachable, - "The server was not found or did not respond to the request." - ) - } - Status::NOT_FOUND => const_io_error!(ErrorKind::NotFound, "The item was not found."), - Status::NOT_READY => { - const_io_error!(ErrorKind::ResourceBusy, "There is no data pending upon return.") - } - Status::NOT_STARTED => { - const_io_error!(ErrorKind::Other, "The protocol has not been started.") - } - Status::OUT_OF_RESOURCES => { - const_io_error!(ErrorKind::OutOfMemory, "A resource has run out.") - } - Status::PROTOCOL_ERROR => { - const_io_error!( - ErrorKind::Other, - "A protocol error occurred during the network operation." - ) - } - Status::PROTOCOL_UNREACHABLE => { - const_io_error!(ErrorKind::Other, "An ICMP protocol unreachable error is received.") - } - Status::SECURITY_VIOLATION => { - const_io_error!( - ErrorKind::PermissionDenied, - "The function was not performed due to a security violation." - ) - } - Status::TFTP_ERROR => { - const_io_error!(ErrorKind::Other, "A TFTP error occurred during the network operation.") - } - Status::TIMEOUT => const_io_error!(ErrorKind::TimedOut, "The timeout time expired."), - Status::UNSUPPORTED => { - const_io_error!(ErrorKind::Unsupported, "The operation is not supported.") - } - Status::VOLUME_FULL => { - const_io_error!(ErrorKind::StorageFull, "There is no more space on the file system.") - } - Status::VOLUME_CORRUPTED => { - const_io_error!( - ErrorKind::Other, - "An inconstancy was detected on the file system causing the operating to fail." - ) - } - Status::WRITE_PROTECTED => { - const_io_error!(ErrorKind::ReadOnlyFilesystem, "The device cannot be written to.") - } - _ => io::Error::new(ErrorKind::Uncategorized, format!("Status: {}", s.as_usize())), - } -} - pub(crate) fn create_event( signal: u32, tpl: efi::Tpl, @@ -288,7 +120,7 @@ pub(crate) fn create_event( (create_event)(signal, tpl, handler, context, &mut exit_boot_service_event) }; if r.is_error() { - Err(status_to_io_error(r)) + Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { NonNull::new(exit_boot_service_event) .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) @@ -305,5 +137,5 @@ pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result (close_event)(evt.as_ptr()) }; - if r.is_error() { Err(status_to_io_error(r)) } else { Ok(()) } + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index 27f76f04c05aa..85d00caf14911 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -29,7 +29,6 @@ pub mod locks; pub mod net; #[path = "../unsupported/once.rs"] pub mod once; -#[path = "../unsupported/os.rs"] pub mod os; #[path = "../windows/os_str.rs"] pub mod os_str; @@ -52,6 +51,8 @@ mod helpers; #[cfg(test)] mod tests; +pub type RawOsError = usize; + use crate::cell::Cell; use crate::io as std_io; use crate::os::uefi; @@ -110,14 +111,50 @@ pub const fn unsupported_err() -> std_io::Error { std_io::const_io_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI",) } -pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { +pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { use crate::io::ErrorKind; use r_efi::efi::Status; - if let Ok(code) = usize::try_from(code) { - helpers::status_to_io_error(Status::from_usize(code)).kind() - } else { - ErrorKind::Uncategorized + match r_efi::efi::Status::from_usize(code) { + Status::ALREADY_STARTED + | Status::COMPROMISED_DATA + | Status::CONNECTION_FIN + | Status::CRC_ERROR + | Status::DEVICE_ERROR + | Status::END_OF_MEDIA + | Status::HTTP_ERROR + | Status::ICMP_ERROR + | Status::INCOMPATIBLE_VERSION + | Status::LOAD_ERROR + | Status::MEDIA_CHANGED + | Status::NO_MAPPING + | Status::NO_MEDIA + | Status::NOT_STARTED + | Status::PROTOCOL_ERROR + | Status::PROTOCOL_UNREACHABLE + | Status::TFTP_ERROR + | Status::VOLUME_CORRUPTED => ErrorKind::Other, + Status::BAD_BUFFER_SIZE | Status::INVALID_LANGUAGE => ErrorKind::InvalidData, + Status::ABORTED => ErrorKind::ConnectionAborted, + Status::ACCESS_DENIED => ErrorKind::PermissionDenied, + Status::BUFFER_TOO_SMALL => ErrorKind::FileTooLarge, + Status::CONNECTION_REFUSED => ErrorKind::ConnectionRefused, + Status::CONNECTION_RESET => ErrorKind::ConnectionReset, + Status::END_OF_FILE => ErrorKind::UnexpectedEof, + Status::HOST_UNREACHABLE => ErrorKind::HostUnreachable, + Status::INVALID_PARAMETER => ErrorKind::InvalidInput, + Status::IP_ADDRESS_CONFLICT => ErrorKind::AddrInUse, + Status::NETWORK_UNREACHABLE => ErrorKind::NetworkUnreachable, + Status::NO_RESPONSE => ErrorKind::HostUnreachable, + Status::NOT_FOUND => ErrorKind::NotFound, + Status::NOT_READY => ErrorKind::ResourceBusy, + Status::OUT_OF_RESOURCES => ErrorKind::OutOfMemory, + Status::SECURITY_VIOLATION => ErrorKind::PermissionDenied, + Status::TIMEOUT => ErrorKind::TimedOut, + Status::UNSUPPORTED => ErrorKind::Unsupported, + Status::VOLUME_FULL => ErrorKind::StorageFull, + Status::WRITE_PROTECTED => ErrorKind::ReadOnlyFilesystem, + _ => ErrorKind::Uncategorized, } } diff --git a/library/std/src/sys/uefi/os.rs b/library/std/src/sys/uefi/os.rs new file mode 100644 index 0000000000000..cd489d8f7f9c6 --- /dev/null +++ b/library/std/src/sys/uefi/os.rs @@ -0,0 +1,213 @@ +use super::{unsupported, RawOsError}; +use crate::error::Error as StdError; +use crate::ffi::{OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::path::{self, PathBuf}; + +pub fn errno() -> RawOsError { + 0 +} + +pub fn error_string(errno: RawOsError) -> String { + use r_efi::efi::Status; + + // Keep the List in Alphabetical Order + // The Messages are taken from UEFI Specification Appendix D - Status Codes + match r_efi::efi::Status::from_usize(errno) { + Status::ABORTED => "The operation was aborted.".to_owned(), + Status::ACCESS_DENIED => "Access was denied.".to_owned(), + Status::ALREADY_STARTED => "The protocol has already been started.".to_owned(), + Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.".to_owned(), + Status::BUFFER_TOO_SMALL => { + "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.".to_owned() + } + Status::COMPROMISED_DATA => { + "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.".to_owned() + } + Status::CONNECTION_FIN => { + "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.".to_owned() + } + Status::CONNECTION_REFUSED => { + "The receiving or transmission operation fails because this connection is refused.".to_owned() + } + Status::CONNECTION_RESET => { + "The connect fails because the connection is reset either by instance itself or the communication peer.".to_owned() + } + Status::CRC_ERROR => "A CRC error was detected.".to_owned(), + Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.".to_owned() + , + Status::END_OF_FILE => { + "The end of the file was reached.".to_owned() + } + Status::END_OF_MEDIA => { + "Beginning or end of media was reached".to_owned() + } + Status::HOST_UNREACHABLE => { + "The remote host is not reachable.".to_owned() + } + Status::HTTP_ERROR => { + "A HTTP error occurred during the network operation.".to_owned() + } + Status::ICMP_ERROR => { + "An ICMP error occurred during the network operation.".to_owned() + } + Status::INCOMPATIBLE_VERSION => { + "The function encountered an internal version that was incompatible with a version requested by the caller.".to_owned() + } + Status::INVALID_LANGUAGE => { + "The language specified was invalid.".to_owned() + } + Status::INVALID_PARAMETER => { + "A parameter was incorrect.".to_owned() + } + Status::IP_ADDRESS_CONFLICT => { + "There is an address conflict address allocation".to_owned() + } + Status::LOAD_ERROR => { + "The image failed to load.".to_owned() + } + Status::MEDIA_CHANGED => { + "The medium in the device has changed since the last access.".to_owned() + } + Status::NETWORK_UNREACHABLE => { + "The network containing the remote host is not reachable.".to_owned() + } + Status::NO_MAPPING => { + "A mapping to a device does not exist.".to_owned() + } + Status::NO_MEDIA => { + "The device does not contain any medium to perform the operation.".to_owned() + } + Status::NO_RESPONSE => { + "The server was not found or did not respond to the request.".to_owned() + } + Status::NOT_FOUND => "The item was not found.".to_owned(), + Status::NOT_READY => { + "There is no data pending upon return.".to_owned() + } + Status::NOT_STARTED => { + "The protocol has not been started.".to_owned() + } + Status::OUT_OF_RESOURCES => { + "A resource has run out.".to_owned() + } + Status::PROTOCOL_ERROR => { + "A protocol error occurred during the network operation.".to_owned() + } + Status::PROTOCOL_UNREACHABLE => { + "An ICMP protocol unreachable error is received.".to_owned() + } + Status::SECURITY_VIOLATION => { + "The function was not performed due to a security violation.".to_owned() + } + Status::TFTP_ERROR => { + "A TFTP error occurred during the network operation.".to_owned() + } + Status::TIMEOUT => "The timeout time expired.".to_owned(), + Status::UNSUPPORTED => { + "The operation is not supported.".to_owned() + } + Status::VOLUME_FULL => { + "There is no more space on the file system.".to_owned() + } + Status::VOLUME_CORRUPTED => { + "An inconstancy was detected on the file system causing the operating to fail.".to_owned() + } + Status::WRITE_PROTECTED => { + "The device cannot be written to.".to_owned() + } + _ => format!("Status: {}", errno), + } +} + +pub fn getcwd() -> io::Result { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + unsupported() +} + +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "not supported on this platform yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "not supported on this platform yet" + } +} + +pub fn current_exe() -> io::Result { + unsupported() +} + +pub struct Env(!); + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.0 + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +pub fn getenv(_: &OsStr) -> Option { + None +} + +pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +} + +pub fn unsetenv(_: &OsStr) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem on this platform") +} + +pub fn home_dir() -> Option { + None +} + +pub fn exit(_code: i32) -> ! { + crate::intrinsics::abort() +} + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} From c7e5f3ca085c3adfd285a6d41080ff65a6299bc9 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sun, 21 May 2023 14:26:59 +0530 Subject: [PATCH 074/149] Rebase to master - Update Example - Add thread_parking to sys::uefi - Fix unsafe in unsafe errors - Improve docs - Improve os/exit - Some asserts - Switch back to atomics Signed-off-by: Ayush Singh --- compiler/rustc_codegen_ssa/src/base.rs | 13 ++--- library/panic_abort/src/lib.rs | 2 +- library/std/build.rs | 1 - library/std/src/lib.rs | 1 + library/std/src/os/mod.rs | 1 - library/std/src/os/uefi/env.rs | 54 +++++++++++-------- library/std/src/sys/uefi/helpers.rs | 14 ++--- library/std/src/sys/uefi/mod.rs | 36 +++++++++---- library/std/src/sys/uefi/os.rs | 42 +++++++++++---- .../src/platform-support/unknown-uefi.md | 10 ++-- 10 files changed, 111 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4753f12554065..6c51dffedbfad 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -421,7 +421,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( entry_type: EntryFnType, ) -> Bx::Function { // The entry function is either `int main(void)` or `int main(int argc, char **argv)`, or - // `Status efi_main(Handle hd, SystemTable *st)` depending on the target. + // `usize efi_main(void *handle, void *system_table)` depending on the target. let llfty = if cx.sess().target.os.contains("uefi") { cx.type_func(&[cx.type_ptr(), cx.type_ptr()], cx.type_isize()) } else if cx.sess().target.main_needs_argc_argv { @@ -508,10 +508,9 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let param_handle = bx.get_param(0); let param_system_table = bx.get_param(1); let arg_argc = bx.const_int(cx.type_isize(), 2); - let arg_argv = bx.alloca(cx.type_array(cx.type_i8p(), 2), Align::ONE); + let arg_argv = bx.alloca(cx.type_array(cx.type_ptr(), 2), Align::ONE); bx.store(param_handle, arg_argv, Align::ONE); - let arg_argv_el1 = - bx.gep(cx.type_ptr_to(cx.type_i8()), arg_argv, &[bx.const_int(cx.type_int(), 1)]); + let arg_argv_el1 = bx.gep(cx.type_ptr(), arg_argv, &[bx.const_int(cx.type_int(), 1)]); bx.store(param_system_table, arg_argv_el1, Align::ONE); (arg_argc, arg_argv) } else if cx.sess().target.main_needs_argc_argv { @@ -566,11 +565,7 @@ pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option { use rustc_middle::middle::dependency_format::Linkage; list.iter().any(|&linkage| linkage == Linkage::Dynamic) }); - if any_dynamic_crate { - None - } else { - tcx.allocator_kind(()) - } + if any_dynamic_crate { None } else { tcx.allocator_kind(()) } } pub fn codegen_crate( diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index bc0d7287be505..6e097e2caf24b 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -44,7 +44,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { } } else if #[cfg(any(target_os = "hermit", all(target_vendor = "fortanix", target_env = "sgx"), - target_os = "xous" + target_os = "xous", target_os = "uefi", ))] { unsafe fn abort() -> ! { diff --git a/library/std/build.rs b/library/std/build.rs index 1c32b42565011..36516978b7a09 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -52,7 +52,6 @@ fn main() { // - mipsel-sony-psp // - nvptx64-nvidia-cuda // - arch=avr - // - tvos (aarch64-apple-tvos, x86_64-apple-tvos) // - JSON targets // - Any new targets that have not been explicitly added above. println!("cargo:rustc-cfg=feature=\"restricted-std\""); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index b572347019caf..5e3249655b838 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -263,6 +263,7 @@ #![cfg_attr(target_os = "xous", feature(slice_ptr_len))] // // Language features: +// tidy-alphabetical-start #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index d1a735038992c..41aa0472f526a 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -142,7 +142,6 @@ pub mod solid; #[cfg(target_os = "tvos")] #[path = "ios/mod.rs"] pub(crate) mod tvos; -#[cfg(target_os = "uefi")] #[cfg(any(target_os = "uefi", doc))] pub mod uefi; #[cfg(target_os = "vita")] diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index 4dd090e22c620..5d082e7c11388 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -2,23 +2,22 @@ #![unstable(feature = "uefi_std", issue = "100499")] -use crate::{cell::Cell, ffi::c_void, ptr::NonNull}; +use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use crate::{ffi::c_void, ptr::NonNull}; -// Since UEFI is single-threaded, making the global variables thread local should be safe. -thread_local! { - // Flag to check if BootServices are still valid. - // Start with assuming that they are not available - static BOOT_SERVICES_FLAG: Cell = Cell::new(false); - // Position 0 = SystemTable - // Position 1 = ImageHandle - static GLOBALS: Cell, NonNull)>> = Cell::new(None); -} +static SYSTEM_TABLE: AtomicPtr = AtomicPtr::new(crate::ptr::null_mut()); +static IMAGE_HANDLE: AtomicPtr = AtomicPtr::new(crate::ptr::null_mut()); +// Flag to check if BootServices are still valid. +// Start with assuming that they are not available +static BOOT_SERVICES_FLAG: AtomicBool = AtomicBool::new(false); /// Initializes the global System Table and Image Handle pointers. /// /// The standard library requires access to the UEFI System Table and the Application Image Handle /// to operate. Those are provided to UEFI Applications via their application entry point. By /// calling `init_globals()`, those pointers are retained by the standard library for future use. +/// Thus this function must be called before any of the standard library services are used. +/// /// The pointers are never exposed to any entity outside of this application and it is guaranteed /// that, once the application exited, these pointers are never dereferenced again. /// @@ -26,9 +25,26 @@ thread_local! { /// application. In particular, UEFI Boot Services must not be exited while an application with the /// standard library is loaded. /// -/// This function must not be called more than once. -pub unsafe fn init_globals(handle: NonNull, system_table: NonNull) { - GLOBALS.set(Some((system_table, handle))); +/// # SAFETY +/// Calling this function more than once will panic +pub(crate) unsafe fn init_globals(handle: NonNull, system_table: NonNull) { + IMAGE_HANDLE + .compare_exchange( + crate::ptr::null_mut(), + handle.as_ptr(), + Ordering::Release, + Ordering::Acquire, + ) + .unwrap(); + SYSTEM_TABLE + .compare_exchange( + crate::ptr::null_mut(), + system_table.as_ptr(), + Ordering::Release, + Ordering::Acquire, + ) + .unwrap(); + BOOT_SERVICES_FLAG.store(true, Ordering::Release) } /// Get the SystemTable Pointer. @@ -50,7 +66,7 @@ pub fn image_handle() -> NonNull { /// Get the BootServices Pointer. /// This function also checks if `ExitBootServices` has already been called. pub fn boot_services() -> Option> { - if BOOT_SERVICES_FLAG.get() { + if BOOT_SERVICES_FLAG.load(Ordering::Acquire) { let system_table: NonNull = try_system_table()?.cast(); let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; NonNull::new(boot_services).map(|x| x.cast()) @@ -62,19 +78,15 @@ pub fn boot_services() -> Option> { /// Get the SystemTable Pointer. /// This function is mostly intended for places where panic is not an option pub(crate) fn try_system_table() -> Option> { - GLOBALS.get().map(|x| x.0) + NonNull::new(SYSTEM_TABLE.load(Ordering::Acquire)) } /// Get the SystemHandle Pointer. /// This function is mostly intended for places where panicking is not an option pub(crate) fn try_image_handle() -> Option> { - GLOBALS.get().map(|x| x.1) -} - -pub(crate) fn enable_boot_services() { - BOOT_SERVICES_FLAG.set(true); + NonNull::new(IMAGE_HANDLE.load(Ordering::Acquire)) } pub(crate) fn disable_boot_services() { - BOOT_SERVICES_FLAG.set(false); + BOOT_SERVICES_FLAG.store(false, Ordering::Release) } diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs index 2e1bcb369447e..126661bfc961c 100644 --- a/library/std/src/sys/uefi/helpers.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -60,13 +60,14 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result = - Vec::with_capacity(buf_len / size_of::()); + assert_eq!(buf_len % size_of::(), 0); + let num_of_handles = buf_len / size_of::(); + let mut buf: Vec = Vec::with_capacity(num_of_handles); match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { Ok(()) => { // This is safe because the call will succeed only if buf_len >= required length. // Also, on success, the `buf_len` is updated with the size of bufferv (in bytes) written - unsafe { buf.set_len(buf_len / size_of::()) }; + unsafe { buf.set_len(num_of_handles) }; Ok(buf.into_iter().filter_map(|x| NonNull::new(x)).collect()) } Err(e) => Err(e), @@ -114,16 +115,15 @@ pub(crate) fn create_event( ) -> io::Result> { let boot_services: NonNull = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); - let mut exit_boot_service_event: r_efi::efi::Event = crate::ptr::null_mut(); + let mut event: r_efi::efi::Event = crate::ptr::null_mut(); let r = unsafe { let create_event = (*boot_services.as_ptr()).create_event; - (create_event)(signal, tpl, handler, context, &mut exit_boot_service_event) + (create_event)(signal, tpl, handler, context, &mut event) }; if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { - NonNull::new(exit_boot_service_event) - .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + NonNull::new(event).ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) } } diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index 85d00caf14911..9a10395af8e31 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -12,7 +12,6 @@ //! [`OsStr`]: crate::ffi::OsStr //! [`OsString`]: crate::ffi::OsString -#![deny(unsafe_op_in_unsafe_fn)] pub mod alloc; #[path = "../unsupported/args.rs"] pub mod args; @@ -43,6 +42,8 @@ pub mod stdio; pub mod thread; #[path = "../unsupported/thread_local_key.rs"] pub mod thread_local_key; +#[path = "../unsupported/thread_parking.rs"] +pub mod thread_parking; #[path = "../unsupported/time.rs"] pub mod time; @@ -53,18 +54,17 @@ mod tests; pub type RawOsError = usize; -use crate::cell::Cell; use crate::io as std_io; use crate::os::uefi; use crate::ptr::NonNull; +use crate::sync::atomic::{AtomicPtr, Ordering}; pub mod memchr { pub use core::slice::memchr::{memchr, memrchr}; } -thread_local! { - static EXIT_BOOT_SERVICE_EVENT: Cell>> = Cell::new(None); -} +static EXIT_BOOT_SERVICE_EVENT: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); /// # SAFETY /// - must be called only once during runtime initialization. @@ -75,8 +75,6 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; let system_table = unsafe { NonNull::new(*argv.add(1) as *mut crate::ffi::c_void).unwrap() }; unsafe { uefi::env::init_globals(image_handle, system_table) }; - // Enable boot services once GLOBALS are initialized - uefi::env::enable_boot_services(); // Register exit boot services handler match helpers::create_event( @@ -86,7 +84,17 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { crate::ptr::null_mut(), ) { Ok(x) => { - EXIT_BOOT_SERVICE_EVENT.set(Some(x)); + if EXIT_BOOT_SERVICE_EVENT + .compare_exchange( + crate::ptr::null_mut(), + x.as_ptr(), + Ordering::Release, + Ordering::Acquire, + ) + .is_err() + { + abort_internal(); + }; } Err(_) => abort_internal(), } @@ -96,7 +104,9 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { /// this is not guaranteed to run, for example when the program aborts. /// - must be called only once during runtime cleanup. pub unsafe fn cleanup() { - if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { + if let Some(exit_boot_service_event) = + NonNull::new(EXIT_BOOT_SERVICE_EVENT.swap(crate::ptr::null_mut(), Ordering::Acquire)) + { let _ = unsafe { helpers::close_event(exit_boot_service_event) }; } } @@ -159,7 +169,9 @@ pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { } pub fn abort_internal() -> ! { - if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { + if let Some(exit_boot_service_event) = + NonNull::new(EXIT_BOOT_SERVICE_EVENT.load(Ordering::Acquire)) + { let _ = unsafe { helpers::close_event(exit_boot_service_event) }; } @@ -226,3 +238,7 @@ fn get_random() -> Option<(u64, u64)> { extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { uefi::env::disable_boot_services(); } + +pub fn is_interrupted(_code: RawOsError) -> bool { + false +} diff --git a/library/std/src/sys/uefi/os.rs b/library/std/src/sys/uefi/os.rs index cd489d8f7f9c6..e6693db68e641 100644 --- a/library/std/src/sys/uefi/os.rs +++ b/library/std/src/sys/uefi/os.rs @@ -4,15 +4,16 @@ use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io; use crate::marker::PhantomData; +use crate::os::uefi; use crate::path::{self, PathBuf}; +use crate::ptr::NonNull; +use r_efi::efi::Status; pub fn errno() -> RawOsError { 0 } pub fn error_string(errno: RawOsError) -> String { - use r_efi::efi::Status; - // Keep the List in Alphabetical Order // The Messages are taken from UEFI Specification Appendix D - Status Codes match r_efi::efi::Status::from_usize(errno) { @@ -160,12 +161,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "not supported on this platform yet" - } -} +impl StdError for JoinPathsError {} pub fn current_exe() -> io::Result { unsupported() @@ -173,6 +169,14 @@ pub fn current_exe() -> io::Result { pub struct Env(!); +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self(inner) = self; + match *inner {} + } +} + impl Iterator for Env { type Item = (OsString, OsString); fn next(&mut self) -> Option<(OsString, OsString)> { @@ -180,6 +184,13 @@ impl Iterator for Env { } } +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + match *inner {} + } +} + pub fn env() -> Env { panic!("not supported on this platform") } @@ -204,7 +215,20 @@ pub fn home_dir() -> Option { None } -pub fn exit(_code: i32) -> ! { +pub fn exit(code: i32) -> ! { + if let (Some(boot_services), Some(handle)) = + (uefi::env::boot_services(), uefi::env::try_image_handle()) + { + let boot_services: NonNull = boot_services.cast(); + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + Status::from_usize(code as usize), + 0, + crate::ptr::null_mut(), + ) + }; + } crate::intrinsics::abort() } diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index 019f030eb1c10..68cd7fae31971 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -277,7 +277,9 @@ cargo +custom build --target x86_64-unknown-uefi ``` ```rust,ignore (platform-specific) -use r_efi::efi; +#![feature(uefi_std)] + +use r_efi::{efi, protocols::simple_text_output}; use std::{ ffi::OsString, os::uefi::{env, ffi::OsStrExt} @@ -290,7 +292,7 @@ pub fn main() { let r = unsafe { let con_out: *mut simple_text_output::Protocol = (*st).con_out; - let output_string: extern "efiapi" fn(_: *mut simple_text_output::Protocol, *mut u16) = (*con_out).output_string; + let output_string: extern "efiapi" fn(_: *mut simple_text_output::Protocol, *mut u16) -> efi::Status = (*con_out).output_string; output_string(con_out, s.as_ptr() as *mut efi::Char16) }; assert!(!r.is_error()) @@ -298,6 +300,6 @@ pub fn main() { ``` ### BootServices -The current implementation of std make `BootServices` unavailable once `ExitBootServices` is called. Refer to [Runtime Drivers](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/7_driver_entry_point/711_runtime_drivers) for more information regarding how to handle switching from using physical addresses to using virtual addresses. +The current implementation of std makes `BootServices` unavailable once `ExitBootServices` is called. Refer to [Runtime Drivers](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/7_driver_entry_point/711_runtime_drivers) for more information regarding how to handle switching from using physical addresses to using virtual addresses. -Note: It should be noted that it is upto the user to drop all allocated memory before `ExitBootServices` is called. +Note: It should be noted that it is up to the user to drop all allocated memory before `ExitBootServices` is called. From 85801f55eff002a63db51125e9bc20aab1d4982e Mon Sep 17 00:00:00 2001 From: pc-linux Date: Fri, 22 Sep 2023 16:54:21 +0200 Subject: [PATCH 075/149] fixed fp caused by moving &mut reference inside of a closure --- clippy_lints/src/needless_pass_by_ref_mut.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 36ca61c0b44d3..57652e5ff546b 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -336,7 +336,12 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId, borrow: ty::BorrowKind) { self.prev_bind = None; if let euv::Place { - base: euv::PlaceBase::Local(vid), + base: + euv::PlaceBase::Local(vid) + | euv::PlaceBase::Upvar(UpvarId { + var_path: UpvarPath { hir_id: vid }, + .. + }), base_ty, .. } = &cmt.place From f3a27d20f60a7b0f23b9256ef6f405c4da8106fd Mon Sep 17 00:00:00 2001 From: jonboh Date: Fri, 22 Sep 2023 17:18:49 +0200 Subject: [PATCH 076/149] prevent ice when threshold is 0 and enum has no variants --- clippy_lints/src/enum_variants.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index d4df6f7aa2d09..85334d09d4b4a 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -167,7 +167,10 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n return; } - let first = &def.variants[0].ident.name.as_str(); + let first = match &def.variants.get(0) { + Some(variant) => variant.ident.name.as_str(), + None => return, + }; let mut pre = camel_case_split(first); let mut post = pre.clone(); post.reverse(); From ab51f66ec087bfa0159ae19c699cb65049e48efa Mon Sep 17 00:00:00 2001 From: pc-linux Date: Fri, 22 Sep 2023 18:04:57 +0200 Subject: [PATCH 077/149] added tests --- tests/ui/needless_pass_by_ref_mut.rs | 12 ++++++++++++ tests/ui/needless_pass_by_ref_mut.stderr | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index da2a72caacb4e..9cddcb3df2378 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -230,6 +230,18 @@ async fn async_vec(b: &mut Vec) { async fn async_vec2(b: &mut Vec) { b.push(true); } +fn non_mut(n: &str) {} +//Should warn +pub async fn call_in_closure1(n: &mut str) { + (|| non_mut(n))() +} +fn str_mut(str: &mut String) -> bool { + str.pop().is_some() +} +//Should not warn +pub async fn call_in_closure2(str: &mut String) { + (|| str_mut(str))(); +} // Should not warn. pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index 0fb9458d79488..0c7fbd5df6d9d 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -108,7 +108,15 @@ LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:235:25 + --> $DIR/needless_pass_by_ref_mut.rs:235:34 + | +LL | pub async fn call_in_closure1(n: &mut str) { + | ^^^^^^^^ help: consider changing to: `&str` + | + = warning: changing this function will impact semver compatibility + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:247:25 | LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { | ^^^^^^^^^^ help: consider changing to: `&usize` @@ -116,7 +124,7 @@ LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:242:20 + --> $DIR/needless_pass_by_ref_mut.rs:254:20 | LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { | ^^^^^^^^^^ help: consider changing to: `&usize` @@ -124,12 +132,12 @@ LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:253:26 + --> $DIR/needless_pass_by_ref_mut.rs:265:26 | LL | pub async fn closure4(n: &mut usize) { | ^^^^^^^^^^ help: consider changing to: `&usize` | = warning: changing this function will impact semver compatibility -error: aborting due to 20 previous errors +error: aborting due to 21 previous errors From 7e46fb9a658ceb5120fdf0de8665e7cc19114d7f Mon Sep 17 00:00:00 2001 From: Mick van Gelderen Date: Fri, 22 Sep 2023 19:41:00 +0200 Subject: [PATCH 078/149] Fix large_futures example The value used in the large_futures example was not large enough to trigger the lint given the default threshold. The example also contained more code than necessary. This PR changes the value size from 1kB to 16kB and reduces the example in size. --- clippy_lints/src/large_futures.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/large_futures.rs b/clippy_lints/src/large_futures.rs index d67d5899350a8..19f1e08b57ad1 100644 --- a/clippy_lints/src/large_futures.rs +++ b/clippy_lints/src/large_futures.rs @@ -17,26 +17,20 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// async fn wait(f: impl std::future::Future) {} + /// async fn large_future(_x: [u8; 16 * 1024]) {} /// - /// async fn big_fut(arg: [u8; 1024]) {} - /// - /// pub async fn test() { - /// let fut = big_fut([0u8; 1024]); - /// wait(fut).await; + /// pub async fn trigger() { + /// large_future([0u8; 16 * 1024]).await; /// } /// ``` /// /// `Box::pin` the big future instead. /// /// ```rust - /// async fn wait(f: impl std::future::Future) {} - /// - /// async fn big_fut(arg: [u8; 1024]) {} + /// async fn large_future(_x: [u8; 16 * 1024]) {} /// - /// pub async fn test() { - /// let fut = Box::pin(big_fut([0u8; 1024])); - /// wait(fut).await; + /// pub async fn trigger() { + /// Box::pin(large_future([0u8; 16 * 1024])).await; /// } /// ``` #[clippy::version = "1.70.0"] From 27fe1c380b481304a75411ac96cac52d5a8f6c2c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 22 Sep 2023 17:59:52 +0000 Subject: [PATCH 079/149] Fix debug printing of tuple --- compiler/rustc_type_ir/src/sty.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index b574cdcc879a1..380d70589bc1f 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -564,22 +564,18 @@ impl DebugWithInfcx for TyKind { } Never => write!(f, "!"), Tuple(t) => { - let mut iter = t.clone().into_iter(); - write!(f, "(")?; - - match iter.next() { - None => return write!(f, ")"), - Some(ty) => write!(f, "{:?}", &this.wrap(ty))?, - }; - - match iter.next() { - None => return write!(f, ",)"), - Some(ty) => write!(f, "{:?})", &this.wrap(ty))?, + let mut count = 0; + for ty in t.clone() { + if count > 0 { + write!(f, ", ")?; + } + write!(f, "{:?}", &this.wrap(ty))?; + count += 1; } - - for ty in iter { - write!(f, ", {:?}", &this.wrap(ty))?; + // unary tuples need a trailing comma + if count == 1 { + write!(f, ",")?; } write!(f, ")") } From 984ecefed8a12fc31200f5307f07acd7ce6b18c9 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Thu, 21 Sep 2023 18:59:43 +0530 Subject: [PATCH 080/149] Fixes from PR - Hide Docs - Use repr_unpacked error Signed-off-by: Ayush Singh --- library/std/src/io/error.rs | 8 ++++---- library/std/src/os/mod.rs | 2 +- src/tools/tidy/src/pal.rs | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index f63142ff01fc0..5966416e32a19 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,14 +1,14 @@ #[cfg(test)] mod tests; -#[cfg(target_pointer_width = "64")] +#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] mod repr_bitpacked; -#[cfg(target_pointer_width = "64")] +#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] use repr_bitpacked::Repr; -#[cfg(not(target_pointer_width = "64"))] +#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] mod repr_unpacked; -#[cfg(not(target_pointer_width = "64"))] +#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] use repr_unpacked::Repr; use crate::error; diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 41aa0472f526a..11ad21515fdec 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -142,7 +142,7 @@ pub mod solid; #[cfg(target_os = "tvos")] #[path = "ios/mod.rs"] pub(crate) mod tvos; -#[cfg(any(target_os = "uefi", doc))] +#[cfg(target_os = "uefi")] pub mod uefi; #[cfg(target_os = "vita")] pub mod vita; diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 3a4d9c53d7bc4..5f6b63a67fda7 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -56,6 +56,7 @@ const EXCEPTION_PATHS: &[&str] = &[ "library/std/src/path.rs", "library/std/src/sys_common", // Should only contain abstractions over platforms "library/std/src/net/test.rs", // Utility helpers for tests + "library/std/src/io/error.rs", // Repr unpacked needed for UEFI ]; pub fn check(path: &Path, bad: &mut bool) { From b5a904a9d450c2afbefcc0c95974f3396e73d487 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 22 Sep 2023 18:45:53 +0000 Subject: [PATCH 081/149] Use placeholders to prevent using inferred RPITIT types to imply their own WF-ness --- .../src/check/compare_impl_item.rs | 81 +++++++++++++++++-- .../rpitit-hidden-types-self-implied-wf.rs | 23 ++++++ ...rpitit-hidden-types-self-implied-wf.stderr | 16 ++++ 3 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf.rs create mode 100644 tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf.stderr diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 38f19aa09adf4..35a5e83638845 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -14,6 +14,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::util::ExplicitSelf; use rustc_middle::ty::{ self, GenericArgs, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, @@ -692,9 +693,9 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( let ocx = ObligationCtxt::new(infcx); // Normalize the impl signature with fresh variables for lifetime inference. - let norm_cause = ObligationCause::misc(return_span, impl_m_def_id); + let misc_cause = ObligationCause::misc(return_span, impl_m_def_id); let impl_sig = ocx.normalize( - &norm_cause, + &misc_cause, param_env, tcx.liberate_late_bound_regions( impl_m.def_id, @@ -725,12 +726,68 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( ); } - let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig); + let trait_sig = ocx.normalize(&misc_cause, param_env, unnormalized_trait_sig); trait_sig.error_reported()?; let trait_return_ty = trait_sig.output(); + // RPITITs are allowed to use the implied predicates of the method that + // defines them. This is because we want code like: + // ``` + // trait Foo { + // fn test<'a, T>(_: &'a T) -> impl Sized; + // } + // impl Foo for () { + // fn test<'a, T>(x: &'a T) -> &'a T { x } + // } + // ``` + // .. to compile. However, since we use both the normalized and unnormalized + // inputs and outputs from the substituted trait signature, we will end up + // seeing the hidden type of an RPIT in the signature itself. Naively, this + // means that we will use the hidden type to imply the hidden type's own + // well-formedness. + // + // To avoid this, we replace the infer vars used for hidden type inference + // with placeholders, which imply nothing about outlives bounds, and then + // prove below that the hidden types are well formed. + let universe = infcx.create_next_universe(); + let mut idx = 0; + let mapping: FxHashMap<_, _> = collector + .types + .iter() + .map(|(_, &(ty, _))| { + assert!( + infcx.resolve_vars_if_possible(ty) == ty && ty.is_ty_var(), + "{ty:?} should not have been constrained via normalization", + ty = infcx.resolve_vars_if_possible(ty) + ); + idx += 1; + ( + ty, + Ty::new_placeholder( + tcx, + ty::Placeholder { + universe, + bound: ty::BoundTy { + var: ty::BoundVar::from_usize(idx), + kind: ty::BoundTyKind::Anon, + }, + }, + ), + ) + }) + .collect(); + let mut type_mapper = BottomUpFolder { + tcx, + ty_op: |ty| *mapping.get(&ty).unwrap_or(&ty), + lt_op: |lt| lt, + ct_op: |ct| ct, + }; let wf_tys = FxIndexSet::from_iter( - unnormalized_trait_sig.inputs_and_output.iter().chain(trait_sig.inputs_and_output.iter()), + unnormalized_trait_sig + .inputs_and_output + .iter() + .chain(trait_sig.inputs_and_output.iter()) + .map(|ty| ty.fold_with(&mut type_mapper)), ); match ocx.eq(&cause, param_env, trait_return_ty, impl_return_ty) { @@ -787,6 +844,20 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( } } + // FIXME: This has the same issue as #108544, but since this isn't breaking + // existing code, I'm not particularly inclined to do the same hack as above + // where we process wf obligations manually. This can be fixed in a forward- + // compatible way later. + let collected_types = collector.types; + for (_, &(ty, _)) in &collected_types { + ocx.register_obligation(traits::Obligation::new( + tcx, + misc_cause.clone(), + param_env, + ty::ClauseKind::WellFormed(ty.into()), + )); + } + // Check that all obligations are satisfied by the implementation's // RPITs. let errors = ocx.select_all_or_error(); @@ -795,8 +866,6 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( return Err(reported); } - let collected_types = collector.types; - // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. let outlives_env = OutlivesEnvironment::with_bounds( diff --git a/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf.rs b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf.rs new file mode 100644 index 0000000000000..c1885af4e5e1f --- /dev/null +++ b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf.rs @@ -0,0 +1,23 @@ +#![feature(return_position_impl_trait_in_trait)] + +trait Extend { + fn extend(_: &str) -> (impl Sized + '_, &'static str); +} + +impl Extend for () { + fn extend(s: &str) -> (Option<&'static &'_ ()>, &'static str) { + //~^ ERROR in type `&'static &()`, reference has a longer lifetime than the data it references + (None, s) + } +} + +// This indirection is not necessary for reproduction, +// but it makes this test future-proof against #114936. +fn extend(s: &str) -> &'static str { + ::extend(s).1 +} + +fn main() { + let use_after_free = extend::<()>(&String::from("temporary")); + println!("{}", use_after_free); +} diff --git a/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf.stderr b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf.stderr new file mode 100644 index 0000000000000..7b63e72acbf21 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf.stderr @@ -0,0 +1,16 @@ +error[E0491]: in type `&'static &()`, reference has a longer lifetime than the data it references + --> $DIR/rpitit-hidden-types-self-implied-wf.rs:8:27 + | +LL | fn extend(s: &str) -> (Option<&'static &'_ ()>, &'static str) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the pointer is valid for the static lifetime +note: but the referenced data is only valid for the anonymous lifetime defined here + --> $DIR/rpitit-hidden-types-self-implied-wf.rs:8:18 + | +LL | fn extend(s: &str) -> (Option<&'static &'_ ()>, &'static str) { + | ^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0491`. From 85681219963220bd5e3bea0545b91eb7b340249a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 22 Sep 2023 18:55:37 +0000 Subject: [PATCH 082/149] Need to use hybrid param-env to make sure implication is not circular --- .../src/check/compare_impl_item.rs | 15 +++++++++-- ...-hidden-types-self-implied-wf-via-param.rs | 26 +++++++++++++++++++ ...den-types-self-implied-wf-via-param.stderr | 16 ++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.rs create mode 100644 tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.stderr diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 35a5e83638845..0e07c58b3995e 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -662,8 +662,6 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap(); let impl_trait_ref = tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap().instantiate_identity(); - let param_env = tcx.param_env(impl_m_def_id); - // First, check a few of the same things as `compare_impl_method`, // just so we don't ICE during substitution later. check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, true)?; @@ -689,6 +687,19 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( let trait_to_placeholder_args = impl_to_placeholder_args.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_args); + let hybrid_preds = tcx + .predicates_of(impl_m.container_id(tcx)) + .instantiate_identity(tcx) + .into_iter() + .chain(tcx.predicates_of(trait_m.def_id).instantiate_own(tcx, trait_to_placeholder_args)) + .map(|(clause, _)| clause); + let param_env = ty::ParamEnv::new(tcx.mk_clauses_from_iter(hybrid_preds), Reveal::UserFacing); + let param_env = traits::normalize_param_env_or_error( + tcx, + param_env, + ObligationCause::misc(tcx.def_span(impl_m_def_id), impl_m_def_id), + ); + let infcx = &tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new(infcx); diff --git a/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.rs b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.rs new file mode 100644 index 0000000000000..5e14a7f8e7213 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.rs @@ -0,0 +1,26 @@ +#![feature(return_position_impl_trait_in_trait)] + +trait Extend { + fn extend<'a: 'a>(_: &'a str) -> (impl Sized + 'a, &'static str); +} + +impl Extend for () { + fn extend<'a: 'a>(s: &'a str) -> (Option<&'static &'a ()>, &'static str) + //~^ ERROR in type `&'static &'a ()`, reference has a longer lifetime than the data it references + where + 'a: 'static, + { + (None, s) + } +} + +// This indirection is not necessary for reproduction, +// but it makes this test future-proof against #114936. +fn extend(s: &str) -> &'static str { + ::extend(s).1 +} + +fn main() { + let use_after_free = extend::<()>(&String::from("temporary")); + println!("{}", use_after_free); +} diff --git a/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.stderr b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.stderr new file mode 100644 index 0000000000000..1d947310e120a --- /dev/null +++ b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.stderr @@ -0,0 +1,16 @@ +error[E0491]: in type `&'static &'a ()`, reference has a longer lifetime than the data it references + --> $DIR/rpitit-hidden-types-self-implied-wf-via-param.rs:8:38 + | +LL | fn extend<'a: 'a>(s: &'a str) -> (Option<&'static &'a ()>, &'static str) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the pointer is valid for the static lifetime +note: but the referenced data is only valid for the lifetime `'a` as defined here + --> $DIR/rpitit-hidden-types-self-implied-wf-via-param.rs:8:15 + | +LL | fn extend<'a: 'a>(s: &'a str) -> (Option<&'static &'a ()>, &'static str) + | ^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0491`. From 1170b7b4478ed1674e0b498dfa52f0f68f6426c8 Mon Sep 17 00:00:00 2001 From: James Haywood Date: Fri, 22 Sep 2023 15:52:07 -0400 Subject: [PATCH 083/149] Resolve rust-lang/rust#116063 --- library/core/src/num/uint_macros.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 23ca37817d4fd..2e9f07e6a68f4 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1307,6 +1307,10 @@ macro_rules! uint_impl { /// This function exists, so that all operations /// are accounted for in the wrapping operations. /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// /// # Examples /// /// Basic usage: From 09c5f983e3ed388df406fe006c2baf89d03aec22 Mon Sep 17 00:00:00 2001 From: Chris Wailes Date: Fri, 22 Sep 2023 13:32:37 -0700 Subject: [PATCH 084/149] Add Zba, Zbb, and Zbs as target features for riscv64-linux-android --- compiler/rustc_target/src/spec/riscv64_linux_android.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/riscv64_linux_android.rs b/compiler/rustc_target/src/spec/riscv64_linux_android.rs index af0d685549414..91f5e562d8b86 100644 --- a/compiler/rustc_target/src/spec/riscv64_linux_android.rs +++ b/compiler/rustc_target/src/spec/riscv64_linux_android.rs @@ -9,7 +9,7 @@ pub fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic-rv64".into(), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+Zba,+Zbb,+Zbs".into(), llvm_abiname: "lp64d".into(), supported_sanitizers: SanitizerSet::ADDRESS, max_atomic_width: Some(64), From f286a756924eddfada894b05246e559262a6bd38 Mon Sep 17 00:00:00 2001 From: James Haywood Date: Fri, 22 Sep 2023 18:07:17 -0400 Subject: [PATCH 085/149] Extend fix to wrapping_div, wrapping_div_euclid and wrapping_rem_euclid --- library/core/src/num/uint_macros.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 2e9f07e6a68f4..7cbef9e779375 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1259,6 +1259,10 @@ macro_rules! uint_impl { /// This function exists, so that all operations /// are accounted for in the wrapping operations. /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// /// # Examples /// /// Basic usage: @@ -1284,6 +1288,10 @@ macro_rules! uint_impl { /// definitions of division are equal, this /// is exactly equal to `self.wrapping_div(rhs)`. /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// /// # Examples /// /// Basic usage: @@ -1337,6 +1345,10 @@ macro_rules! uint_impl { /// definitions of division are equal, this /// is exactly equal to `self.wrapping_rem(rhs)`. /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// /// # Examples /// /// Basic usage: From b6836849acf8ce97e71fdf3963e4000173f8e1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sat, 23 Sep 2023 13:21:50 +0200 Subject: [PATCH 086/149] Add regression test for issue #79865 --- .../ui/codegen/issue-79865-llvm-miscompile.rs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/ui/codegen/issue-79865-llvm-miscompile.rs diff --git a/tests/ui/codegen/issue-79865-llvm-miscompile.rs b/tests/ui/codegen/issue-79865-llvm-miscompile.rs new file mode 100644 index 0000000000000..b77f09f8e9a70 --- /dev/null +++ b/tests/ui/codegen/issue-79865-llvm-miscompile.rs @@ -0,0 +1,38 @@ +// run-pass +// only-x86_64 +// compile-flags: -C opt-level=3 + +// Regression test for issue #79865. +// The assertion will fail when compiled with Rust 1.56..=1.59 +// due to a LLVM miscompilation. + +use std::arch::x86_64::*; + +fn main() { + if is_x86_feature_detected!("avx") { + let res: [f64; 4] = unsafe { std::mem::transmute::<_, _>(first()) }; + assert_eq!(res, [22.0, 44.0, 66.0, 88.0]); + } +} + +#[target_feature(enable = "avx")] +unsafe fn first() -> __m256d { + second() +} + +unsafe fn second() -> __m256d { + let v0 = _mm256_setr_pd(1.0, 2.0, 3.0, 4.0); + let v1 = _mm256_setr_pd(10.0, 20.0, 30.0, 40.0); + + // needs to be called twice to hit the miscompilation + let (add, _) = add_sub(v0, v1); + let (add, _) = add_sub(add, add); + add +} + +#[inline(never)] // needed to hit the miscompilation +unsafe fn add_sub(v1: __m256d, v0: __m256d) -> (__m256d, __m256d) { + let add = _mm256_add_pd(v0, v1); + let sub = _mm256_sub_pd(v0, v1); + (add, sub) +} From 0a2d39de2e0b87361432ae695cc84ad74d09972a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 28 Jan 2023 12:56:04 +0000 Subject: [PATCH 087/149] Enable drop_tracking_mir by default. --- clippy_lints/src/await_holding_invalid.rs | 43 +++++++++++++---------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index d40a385435afb..7dd808a7b3b73 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -2,9 +2,9 @@ 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_id::DefId; -use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_hir::{AsyncGeneratorKind, Body, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::GeneratorInteriorTypeCause; +use rustc_middle::mir::GeneratorLayout; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; @@ -197,28 +197,35 @@ impl LateLintPass<'_> for AwaitHolding { fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { use AsyncGeneratorKind::{Block, Closure, Fn}; if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { - let body_id = BodyId { - hir_id: body.value.hir_id, - }; - let typeck_results = cx.tcx.typeck_body(body_id); - self.check_interior_types( - cx, - typeck_results.generator_interior_types.as_ref().skip_binder(), - body.value.span, - ); + let def_id = cx.tcx.hir().body_owner_def_id(body.id()); + if let Some(generator_layout) = cx.tcx.mir_generator_witnesses(def_id) { + self.check_interior_types(cx, generator_layout); + } } } } impl AwaitHolding { - fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { + fn check_interior_types(&self, cx: &LateContext<'_>, generator: &GeneratorLayout<'_>) { + for (ty_index, ty_cause) in generator.field_tys.iter_enumerated() { if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + let await_points = || { + generator + .variant_source_info + .iter_enumerated() + .filter_map(|(variant, source_info)| { + generator.variant_fields[variant] + .raw + .contains(&ty_index) + .then_some(source_info.span) + }) + .collect::>() + }; if is_mutex_guard(cx, adt.did()) { span_lint_and_then( cx, AWAIT_HOLDING_LOCK, - ty_cause.span, + ty_cause.source_info.span, "this `MutexGuard` is held across an `await` point", |diag| { diag.help( @@ -226,7 +233,7 @@ impl AwaitHolding { `MutexGuard` is dropped before calling await", ); diag.span_note( - ty_cause.scope_span.unwrap_or(span), + await_points(), "these are all the `await` points this lock is held through", ); }, @@ -235,18 +242,18 @@ impl AwaitHolding { span_lint_and_then( cx, AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, + ty_cause.source_info.span, "this `RefCell` reference is held across an `await` point", |diag| { diag.help("ensure the reference is dropped before calling `await`"); diag.span_note( - ty_cause.scope_span.unwrap_or(span), + await_points(), "these are all the `await` points this reference is held through", ); }, ); } else if let Some(disallowed) = self.def_ids.get(&adt.did()) { - emit_invalid_type(cx, ty_cause.span, disallowed); + emit_invalid_type(cx, ty_cause.source_info.span, disallowed); } } } From 19e160fe2fa0c8f04dc2b550db32f690ae27c64b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 28 Jan 2023 23:20:02 +0000 Subject: [PATCH 088/149] Bless clippy. --- .../await_holding_invalid_type.rs | 6 +- .../await_holding_invalid_type.stderr | 6 +- tests/ui/await_holding_lock.stderr | 137 ++++++------------ tests/ui/await_holding_refcell_ref.stderr | 67 +++------ tests/ui/future_not_send.rs | 1 + tests/ui/future_not_send.stderr | 36 +---- 6 files changed, 87 insertions(+), 166 deletions(-) diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs index fbef5c4564b1b..868cf00a8d469 100644 --- a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs @@ -7,8 +7,10 @@ async fn bad() -> u32 { } async fn bad_reason() -> u32 { - let _x = Ipv4Addr::new(127, 0, 0, 1); - baz().await + let x = Ipv4Addr::new(127, 0, 0, 1); + let y = baz().await; + let _x = x; + y } async fn good() -> u32 { diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr index 6f8b04fd12b94..ddcd1940d475c 100644 --- a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -11,11 +11,11 @@ LL | let _x = String::from("hello"); error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml` --> $DIR/await_holding_invalid_type.rs:10:9 | -LL | let _x = Ipv4Addr::new(127, 0, 0, 1); - | ^^ +LL | let x = Ipv4Addr::new(127, 0, 0, 1); + | ^ error: `std::string::String` may not be held across an `await` point per `clippy.toml` - --> $DIR/await_holding_invalid_type.rs:31:13 + --> $DIR/await_holding_invalid_type.rs:33:13 | LL | let _x = String::from("hi!"); | ^^ diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr index 56b111c4d9e41..4782104000228 100644 --- a/tests/ui/await_holding_lock.stderr +++ b/tests/ui/await_holding_lock.stderr @@ -6,13 +6,10 @@ LL | let guard = x.lock().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:9:9 + --> $DIR/await_holding_lock.rs:11:15 | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ = note: `-D clippy::await-holding-lock` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::await_holding_lock)]` @@ -24,13 +21,10 @@ LL | let guard = x.read().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:25:9 + --> $DIR/await_holding_lock.rs:27:15 | -LL | / let guard = x.read().unwrap(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:31:13 @@ -40,13 +34,10 @@ LL | let mut guard = x.write().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:31:9 + --> $DIR/await_holding_lock.rs:33:15 | -LL | / let mut guard = x.write().unwrap(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:53:13 @@ -56,16 +47,13 @@ LL | let guard = x.lock().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:53:9 - | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | -LL | | let second = baz().await; -... | -LL | | first + second + third -LL | | } - | |_____^ + --> $DIR/await_holding_lock.rs:56:28 + | +LL | let second = baz().await; + | ^^^^^ +LL | +LL | let third = baz().await; + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:67:17 @@ -75,13 +63,10 @@ LL | let guard = x.lock().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:67:13 + --> $DIR/await_holding_lock.rs:69:19 | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | baz().await -LL | | }; - | |_________^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:80:17 @@ -91,13 +76,10 @@ LL | let guard = x.lock().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:80:13 + --> $DIR/await_holding_lock.rs:82:19 | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | baz().await -LL | | } - | |_________^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:93:13 @@ -107,13 +89,10 @@ LL | let guard = x.lock(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:93:9 + --> $DIR/await_holding_lock.rs:95:15 | -LL | / let guard = x.lock(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:109:13 @@ -123,13 +102,10 @@ LL | let guard = x.read(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:109:9 + --> $DIR/await_holding_lock.rs:111:15 | -LL | / let guard = x.read(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:115:13 @@ -139,13 +115,10 @@ LL | let mut guard = x.write(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:115:9 + --> $DIR/await_holding_lock.rs:117:15 | -LL | / let mut guard = x.write(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:137:13 @@ -155,16 +128,13 @@ LL | let guard = x.lock(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:137:9 - | -LL | / let guard = x.lock(); -LL | | -LL | | -LL | | let second = baz().await; -... | -LL | | first + second + third -LL | | } - | |_____^ + --> $DIR/await_holding_lock.rs:140:28 + | +LL | let second = baz().await; + | ^^^^^ +LL | +LL | let third = baz().await; + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:151:17 @@ -174,13 +144,10 @@ LL | let guard = x.lock(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:151:13 + --> $DIR/await_holding_lock.rs:153:19 | -LL | / let guard = x.lock(); -LL | | -LL | | baz().await -LL | | }; - | |_________^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:164:17 @@ -190,13 +157,10 @@ LL | let guard = x.lock(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:164:13 + --> $DIR/await_holding_lock.rs:166:19 | -LL | / let guard = x.lock(); -LL | | -LL | | baz().await -LL | | } - | |_________^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:185:9 @@ -206,15 +170,10 @@ LL | let mut guard = x.lock().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:185:5 - | -LL | / let mut guard = x.lock().unwrap(); -LL | | -LL | | *guard += 1; -LL | | drop(guard); -LL | | baz().await; -LL | | } - | |_^ + --> $DIR/await_holding_lock.rs:189:11 + | +LL | baz().await; + | ^^^^^ error: aborting due to 13 previous errors diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr index 6b7e1d1afddf4..9264af93dc168 100644 --- a/tests/ui/await_holding_refcell_ref.stderr +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -6,13 +6,10 @@ LL | let b = x.borrow(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:6:5 + --> $DIR/await_holding_refcell_ref.rs:8:11 | -LL | / let b = x.borrow(); -LL | | -LL | | baz().await -LL | | } - | |_^ +LL | baz().await + | ^^^^^ = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::await_holding_refcell_ref)]` @@ -24,13 +21,10 @@ LL | let b = x.borrow_mut(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:12:5 + --> $DIR/await_holding_refcell_ref.rs:14:11 | -LL | / let b = x.borrow_mut(); -LL | | -LL | | baz().await -LL | | } - | |_^ +LL | baz().await + | ^^^^^ error: this `RefCell` reference is held across an `await` point --> $DIR/await_holding_refcell_ref.rs:34:9 @@ -40,16 +34,13 @@ LL | let b = x.borrow_mut(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:34:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | -LL | | let second = baz().await; -... | -LL | | first + second + third -LL | | } - | |_^ + --> $DIR/await_holding_refcell_ref.rs:37:24 + | +LL | let second = baz().await; + | ^^^^^ +LL | +LL | let third = baz().await; + | ^^^^^ error: this `RefCell` reference is held across an `await` point --> $DIR/await_holding_refcell_ref.rs:47:9 @@ -59,16 +50,10 @@ LL | let b = x.borrow_mut(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:47:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | -LL | | let second = baz().await; -... | -LL | | first + second + third -LL | | } - | |_^ + --> $DIR/await_holding_refcell_ref.rs:50:24 + | +LL | let second = baz().await; + | ^^^^^ error: this `RefCell` reference is held across an `await` point --> $DIR/await_holding_refcell_ref.rs:63:13 @@ -78,13 +63,10 @@ LL | let b = x.borrow_mut(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:63:9 + --> $DIR/await_holding_refcell_ref.rs:65:15 | -LL | / let b = x.borrow_mut(); -LL | | -LL | | baz().await -LL | | }; - | |_____^ +LL | baz().await + | ^^^^^ error: this `RefCell` reference is held across an `await` point --> $DIR/await_holding_refcell_ref.rs:76:13 @@ -94,13 +76,10 @@ LL | let b = x.borrow_mut(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:76:9 + --> $DIR/await_holding_refcell_ref.rs:78:15 | -LL | / let b = x.borrow_mut(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: aborting due to 6 previous errors diff --git a/tests/ui/future_not_send.rs b/tests/ui/future_not_send.rs index 06090e2713dee..9274340b5caa5 100644 --- a/tests/ui/future_not_send.rs +++ b/tests/ui/future_not_send.rs @@ -59,6 +59,7 @@ where { let rt = &t; async { true }.await; + let _ = rt; t } diff --git a/tests/ui/future_not_send.stderr b/tests/ui/future_not_send.stderr index e417de723ff3e..f43e3c8ff9f7b 100644 --- a/tests/ui/future_not_send.stderr +++ b/tests/ui/future_not_send.stderr @@ -12,19 +12,12 @@ LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { LL | LL | async { true }.await | ^^^^^ await occurs here, with `rc` maybe used later -LL | } - | - `rc` is later dropped here = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` -note: future is not `Send` as this value is used across an await - --> $DIR/future_not_send.rs:9:20 +note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + --> $DIR/future_not_send.rs:7:39 | LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { - | ---- has type `&std::cell::Cell` which is not `Send` -LL | -LL | async { true }.await - | ^^^^^ await occurs here, with `cell` maybe used later -LL | } - | - `cell` is later dropped here + | ^^^^ has type `&std::cell::Cell` which is not `Send`, because `std::cell::Cell` is not `Sync` = note: `std::cell::Cell` doesn't implement `std::marker::Sync` = note: `-D clippy::future-not-send` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::future_not_send)]` @@ -43,8 +36,6 @@ LL | pub async fn public_future(rc: Rc<[u8]>) { LL | LL | async { true }.await; | ^^^^^ await occurs here, with `rc` maybe used later -LL | } - | - `rc` is later dropped here = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` error: future cannot be sent between threads safely @@ -93,9 +84,6 @@ LL | async fn private_future(&self) -> usize { LL | LL | async { true }.await; | ^^^^^ await occurs here, with `&self` maybe used later -LL | self.rc.len() -LL | } - | - `&self` is later dropped here = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely @@ -104,16 +92,11 @@ error: future cannot be sent between threads safely LL | pub async fn public_future(&self) { | ^ future returned by `public_future` is not `Send` | -note: future is not `Send` as this value is used across an await - --> $DIR/future_not_send.rs:46:31 +note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + --> $DIR/future_not_send.rs:44:32 | LL | pub async fn public_future(&self) { - | ----- has type `&Dummy` which is not `Send` -LL | -LL | self.private_future().await; - | ^^^^^ await occurs here, with `&self` maybe used later -LL | } - | - `&self` is later dropped here + | ^^^^^ has type `&Dummy` which is not `Send`, because `Dummy` is not `Sync` = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely @@ -129,19 +112,16 @@ LL | let rt = &t; | -- has type `&T` which is not `Send` LL | async { true }.await; | ^^^^^ await occurs here, with `rt` maybe used later -LL | t -LL | } - | - `rt` is later dropped here = note: `T` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:72:34 + --> $DIR/future_not_send.rs:73:34 | LL | async fn unclear_future(t: T) {} | ^ future returned by `unclear_future` is not `Send` | note: captured value is not `Send` - --> $DIR/future_not_send.rs:72:28 + --> $DIR/future_not_send.rs:73:28 | LL | async fn unclear_future(t: T) {} | ^ has type `T` which is not `Send` From e2669b27f3cd32fbf956bfcae96c3e19f49108a1 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 3 Aug 2023 13:47:04 +0000 Subject: [PATCH 089/149] Remove GeneratorWitness and rename GeneratorWitnessMIR. --- clippy_lints/src/dereference.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 8090f821d1ff2..fe37fd4a0c192 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -940,7 +940,6 @@ impl TyCoercionStability { | ty::FnDef(..) | ty::Generator(..) | ty::GeneratorWitness(..) - | ty::GeneratorWitnessMIR(..) | ty::Closure(..) | ty::Never | ty::Tuple(_) From 699240567446ba9d794b7e4883831ae13aadac59 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 19 Sep 2023 19:20:22 +0000 Subject: [PATCH 090/149] Tolerate non-ptr indirect scalars in codegen. --- compiler/rustc_codegen_ssa/src/mir/operand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 9205acc527e53..63de6a7207284 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -156,7 +156,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { Abi::Scalar(s @ abi::Scalar::Initialized { .. }) => { let size = s.size(bx); assert_eq!(size, layout.size, "abi::Scalar size does not match layout size"); - let val = read_scalar(Size::ZERO, size, s, bx.type_ptr()); + let val = read_scalar(Size::ZERO, size, s, bx.backend_type(layout)); OperandRef { val: OperandValue::Immediate(val), layout } } Abi::ScalarPair( From ac0683b78310bba9c3c601cfd81bbda4a10be322 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 23 Sep 2023 09:35:44 +0000 Subject: [PATCH 091/149] Use correct offset when codegening mir::Const::Indirect. --- compiler/rustc_codegen_ssa/src/mir/operand.rs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 63de6a7207284..0ab2b7ecd9c80 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -135,15 +135,14 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { assert_eq!(alloc_align, layout.align.abi); let read_scalar = |start, size, s: abi::Scalar, ty| { - let val = alloc - .0 - .read_scalar( - bx, - alloc_range(start, size), - /*read_provenance*/ matches!(s.primitive(), abi::Pointer(_)), - ) - .unwrap(); - bx.scalar_to_backend(val, s, ty) + match alloc.0.read_scalar( + bx, + alloc_range(start, size), + /*read_provenance*/ matches!(s.primitive(), abi::Pointer(_)), + ) { + Ok(val) => bx.scalar_to_backend(val, s, ty), + Err(_) => bx.const_poison(ty), + } }; // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point. @@ -156,7 +155,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { Abi::Scalar(s @ abi::Scalar::Initialized { .. }) => { let size = s.size(bx); assert_eq!(size, layout.size, "abi::Scalar size does not match layout size"); - let val = read_scalar(Size::ZERO, size, s, bx.backend_type(layout)); + let val = read_scalar(offset, size, s, bx.backend_type(layout)); OperandRef { val: OperandValue::Immediate(val), layout } } Abi::ScalarPair( @@ -164,10 +163,10 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { b @ abi::Scalar::Initialized { .. }, ) => { let (a_size, b_size) = (a.size(bx), b.size(bx)); - let b_offset = a_size.align_to(b.align(bx).abi); + let b_offset = (offset + a_size).align_to(b.align(bx).abi); assert!(b_offset.bytes() > 0); let a_val = read_scalar( - Size::ZERO, + offset, a_size, a, bx.scalar_pair_element_backend_type(layout, 0, true), From 93863383c8240c35f6bfda97ab96fd5ce8a5783c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Fri, 22 Sep 2023 20:44:56 +0200 Subject: [PATCH 092/149] Avoid overflow in `IoSlice::advance_slices` --- library/std/src/io/mod.rs | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 7582c7444f03d..604b795cd52a2 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1236,22 +1236,22 @@ impl<'a> IoSliceMut<'a> { pub fn advance_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) { // Number of buffers to remove. let mut remove = 0; - // Total length of all the to be removed buffers. - let mut accumulated_len = 0; + // Remaining length before reaching n. + let mut left = n; for buf in bufs.iter() { - if accumulated_len + buf.len() > n { - break; - } else { - accumulated_len += buf.len(); + if let Some(remainder) = left.checked_sub(buf.len()) { + left = remainder; remove += 1; + } else { + break; } } *bufs = &mut take(bufs)[remove..]; if bufs.is_empty() { - assert!(n == accumulated_len, "advancing io slices beyond their length"); + assert!(left == 0, "advancing io slices beyond their length"); } else { - bufs[0].advance(n - accumulated_len) + bufs[0].advance(left); } } } @@ -1379,22 +1379,25 @@ impl<'a> IoSlice<'a> { pub fn advance_slices(bufs: &mut &mut [IoSlice<'a>], n: usize) { // Number of buffers to remove. let mut remove = 0; - // Total length of all the to be removed buffers. - let mut accumulated_len = 0; + // Remaining length before reaching n. This prevents overflow + // that could happen if the length of slices in `bufs` were instead + // accumulated. Those slice may be aliased and, if they are large + // enough, their added length may overflow a `usize`. + let mut left = n; for buf in bufs.iter() { - if accumulated_len + buf.len() > n { - break; - } else { - accumulated_len += buf.len(); + if let Some(remainder) = left.checked_sub(buf.len()) { + left = remainder; remove += 1; + } else { + break; } } *bufs = &mut take(bufs)[remove..]; if bufs.is_empty() { - assert!(n == accumulated_len, "advancing io slices beyond their length"); + assert!(left == 0, "advancing io slices beyond their length"); } else { - bufs[0].advance(n - accumulated_len) + bufs[0].advance(left); } } } From 0f248d8ea9403850cd3d80b370fcc9b6793f17b1 Mon Sep 17 00:00:00 2001 From: FZs <39064892+fzs111@users.noreply.github.com> Date: Fri, 22 Sep 2023 19:33:33 +0200 Subject: [PATCH 093/149] Clarify example in `Pin::new_unchecked` docs --- library/core/src/pin.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 6b319b4355ce6..94c682b615ad4 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -572,7 +572,10 @@ impl Pin

{ /// // though we have previously pinned it! We have violated the pinning API contract. /// } /// ``` - /// A value, once pinned, must remain pinned forever (unless its type implements `Unpin`). + /// A value, once pinned, must remain pinned until it is dropped (unless its type implements + /// `Unpin`). Because `Pin<&mut T>` does not own the value, dropping the `Pin` will not drop + /// the value and will not end the pinning contract. So moving the value after dropping the + /// `Pin<&mut T>` is still a violation of the API contract. /// /// Similarly, calling `Pin::new_unchecked` on an `Rc` is unsafe because there could be /// aliases to the same data that are not subject to the pinning restrictions: From d63959f2faa0a3e1164dd42c641685de1fcad295 Mon Sep 17 00:00:00 2001 From: Caio Date: Sat, 23 Sep 2023 14:23:51 -0300 Subject: [PATCH 094/149] Add the `cfg_match!` macro --- library/core/src/macros/mod.rs | 89 +++++++++++++++++++ library/core/tests/lib.rs | 2 + library/core/tests/macros.rs | 154 +++++++++++++++++++++++++++++++++ library/std/src/lib.rs | 3 + 4 files changed, 248 insertions(+) diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 646100fe27b75..c27be8d2ffd31 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -321,6 +321,95 @@ pub macro debug_assert_matches($($arg:tt)*) { } } +/// A macro for defining `#[cfg]` match-like statements. +/// +/// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of +/// `#[cfg]` cases, emitting the implementation which matches first. +/// +/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code +/// without having to rewrite each clause multiple times. +/// +/// Trailing `_` wildcard match arms are **optional** and they indicate a fallback branch when +/// all previous declarations do not evaluate to true. +/// +/// # Example +/// +/// ``` +/// #![feature(cfg_match)] +/// +/// cfg_match! { +/// cfg(unix) => { +/// fn foo() { /* unix specific functionality */ } +/// } +/// cfg(target_pointer_width = "32") => { +/// fn foo() { /* non-unix, 32-bit functionality */ } +/// } +/// _ => { +/// fn foo() { /* fallback implementation */ } +/// } +/// } +/// ``` +#[macro_export] +#[unstable(feature = "cfg_match", issue = "115585")] +#[rustc_diagnostic_item = "cfg_match"] +macro_rules! cfg_match { + // with a final wildcard + ( + $(cfg($initial_meta:meta) => { $($initial_tokens:item)* })+ + _ => { $($extra_tokens:item)* } + ) => { + cfg_match! { + @__items (); + $((($initial_meta) ($($initial_tokens)*)),)+ + (() ($($extra_tokens)*)), + } + }; + + // without a final wildcard + ( + $(cfg($extra_meta:meta) => { $($extra_tokens:item)* })* + ) => { + cfg_match! { + @__items (); + $((($extra_meta) ($($extra_tokens)*)),)* + } + }; + + // Internal and recursive macro to emit all the items + // + // Collects all the previous cfgs in a list at the beginning, so they can be + // negated. After the semicolon is all the remaining items. + (@__items ($($_:meta,)*);) => {}; + ( + @__items ($($no:meta,)*); + (($($yes:meta)?) ($($tokens:item)*)), + $($rest:tt,)* + ) => { + // Emit all items within one block, applying an appropriate #[cfg]. The + // #[cfg] will require all `$yes` matchers specified and must also negate + // all previous matchers. + #[cfg(all( + $($yes,)? + not(any($($no),*)) + ))] + cfg_match! { @__identity $($tokens)* } + + // Recurse to emit all other items in `$rest`, and when we do so add all + // our `$yes` matchers to the list of `$no` matchers as future emissions + // will have to negate everything we just matched as well. + cfg_match! { + @__items ($($no,)* $($yes,)?); + $($rest,)* + } + }; + + // Internal macro to make __apply work out right for different match types, + // because of how macros match/expand stuff. + (@__identity $($tokens:item)*) => { + $($tokens)* + }; +} + /// Returns whether the given expression matches any of the given patterns. /// /// Like in a `match` expression, the pattern can be optionally followed by `if` diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 17011b845cfb5..773f2b955d849 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -96,6 +96,7 @@ #![feature(const_option_ext)] #![feature(const_result)] #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] +#![cfg_attr(test, feature(cfg_match))] #![feature(int_roundings)] #![feature(slice_group_by)] #![feature(split_array)] @@ -139,6 +140,7 @@ mod hash; mod intrinsics; mod iter; mod lazy; +#[cfg(test)] mod macros; mod manually_drop; mod mem; diff --git a/library/core/tests/macros.rs b/library/core/tests/macros.rs index ff3632e3550c2..eb886def164ac 100644 --- a/library/core/tests/macros.rs +++ b/library/core/tests/macros.rs @@ -1,3 +1,25 @@ +trait Trait { + fn blah(&self); +} + +#[allow(dead_code)] +struct Struct; + +impl Trait for Struct { + cfg_match! { + cfg(feature = "blah") => { + fn blah(&self) { + unimplemented!(); + } + } + _ => { + fn blah(&self) { + unimplemented!(); + } + } + } +} + #[test] fn assert_eq_trailing_comma() { assert_eq!(1, 1,); @@ -18,3 +40,135 @@ fn assert_ne_trailing_comma() { fn matches_leading_pipe() { matches!(1, | 1 | 2 | 3); } + +#[test] +fn cfg_match_basic() { + cfg_match! { + cfg(target_pointer_width = "64") => { fn f0_() -> bool { true }} + } + + cfg_match! { + cfg(unix) => { fn f1_() -> bool { true }} + cfg(any(target_os = "macos", target_os = "linux")) => { fn f1_() -> bool { false }} + } + + cfg_match! { + cfg(target_pointer_width = "32") => { fn f2_() -> bool { false }} + cfg(target_pointer_width = "64") => { fn f2_() -> bool { true }} + } + + cfg_match! { + cfg(target_pointer_width = "16") => { fn f3_() -> i32 { 1 }} + _ => { fn f3_() -> i32 { 2 }} + } + + #[cfg(target_pointer_width = "64")] + assert!(f0_()); + + #[cfg(unix)] + assert!(f1_()); + + #[cfg(target_pointer_width = "32")] + assert!(!f2_()); + #[cfg(target_pointer_width = "64")] + assert!(f2_()); + + #[cfg(not(target_pointer_width = "16"))] + assert_eq!(f3_(), 2); +} + +#[test] +fn cfg_match_debug_assertions() { + cfg_match! { + cfg(debug_assertions) => { + assert!(cfg!(debug_assertions)); + assert_eq!(4, 2+2); + } + _ => { + assert!(cfg!(not(debug_assertions))); + assert_eq!(10, 5+5); + } + } +} + +#[cfg(target_pointer_width = "64")] +#[test] +fn cfg_match_no_duplication_on_64() { + cfg_match! { + cfg(windows) => { + fn foo() {} + } + cfg(unix) => { + fn foo() {} + } + cfg(target_pointer_width = "64") => { + fn foo() {} + } + } + foo(); +} + +#[test] +fn cfg_match_options() { + cfg_match! { + cfg(test) => { + use core::option::Option as Option2; + fn works1() -> Option2 { Some(1) } + } + _ => { fn works1() -> Option { None } } + } + + cfg_match! { + cfg(feature = "foo") => { fn works2() -> bool { false } } + cfg(test) => { fn works2() -> bool { true } } + _ => { fn works2() -> bool { false } } + } + + cfg_match! { + cfg(feature = "foo") => { fn works3() -> bool { false } } + _ => { fn works3() -> bool { true } } + } + + cfg_match! { + cfg(test) => { + use core::option::Option as Option3; + fn works4() -> Option3 { Some(1) } + } + } + + cfg_match! { + cfg(feature = "foo") => { fn works5() -> bool { false } } + cfg(test) => { fn works5() -> bool { true } } + } + + assert!(works1().is_some()); + assert!(works2()); + assert!(works3()); + assert!(works4().is_some()); + assert!(works5()); +} + +#[test] +fn cfg_match_two_functions() { + cfg_match! { + cfg(target_pointer_width = "64") => { + fn foo1() {} + fn bar1() {} + } + _ => { + fn foo2() {} + fn bar2() {} + } + } + + #[cfg(target_pointer_width = "64")] + { + foo1(); + bar1(); + } + #[cfg(not(target_pointer_width = "64"))] + { + foo2(); + bar2(); + } +} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5e3249655b838..f1f0f8b1653dc 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -665,6 +665,9 @@ pub use core::{ )] pub use core::concat_bytes; +#[unstable(feature = "cfg_match", issue = "115585")] +pub use core::cfg_match; + #[stable(feature = "core_primitive", since = "1.43.0")] pub use core::primitive; From ba8d53dc8fd43fac0e539fee9c91fcdd307cc17e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Wed, 20 Sep 2023 15:38:18 +0200 Subject: [PATCH 095/149] Don't use a thread to load the dep graph --- compiler/rustc_incremental/src/lib.rs | 2 +- .../rustc_incremental/src/persist/load.rs | 132 ++++++++---------- compiler/rustc_incremental/src/persist/mod.rs | 3 +- .../rustc_incremental/src/persist/save.rs | 2 +- compiler/rustc_interface/src/queries.rs | 47 +------ 5 files changed, 68 insertions(+), 118 deletions(-) diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index b9171fad55ba3..220ea194a6de1 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -28,8 +28,8 @@ pub use persist::load_query_result_cache; pub use persist::prepare_session_directory; pub use persist::save_dep_graph; pub use persist::save_work_product_index; +pub use persist::setup_dep_graph; pub use persist::LoadResult; -pub use persist::{build_dep_graph, load_dep_graph, DepGraphFuture}; use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; use rustc_fluent_macro::fluent_messages; diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 25e694fa1c155..2310d0b12ef0c 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -3,17 +3,19 @@ use crate::errors; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::unord::UnordMap; -use rustc_middle::dep_graph::{DepsType, SerializedDepGraph, WorkProductMap}; +use rustc_middle::dep_graph::{DepGraph, DepsType, SerializedDepGraph, WorkProductMap}; use rustc_middle::query::on_disk_cache::OnDiskCache; use rustc_serialize::opaque::MemDecoder; use rustc_serialize::Decodable; use rustc_session::config::IncrementalStateAssertion; -use rustc_session::Session; +use rustc_session::{Session, StableCrateId}; +use rustc_span::{ErrorGuaranteed, Symbol}; use std::path::{Path, PathBuf}; use super::data::*; use super::file_format; use super::fs::*; +use super::save::build_dep_graph; use super::work_product; #[derive(Debug)] @@ -72,21 +74,12 @@ impl LoadResult { } fn load_data(path: &Path, sess: &Session) -> LoadResult<(Mmap, usize)> { - load_data_no_sess( + match file_format::read_file( path, sess.opts.unstable_opts.incremental_info, sess.is_nightly_build(), sess.cfg_version, - ) -} - -fn load_data_no_sess( - path: &Path, - report_incremental_info: bool, - is_nightly_build: bool, - cfg_version: &'static str, -) -> LoadResult<(Mmap, usize)> { - match file_format::read_file(path, report_incremental_info, is_nightly_build, cfg_version) { + ) { Ok(Some(data_and_pos)) => LoadResult::Ok { data: data_and_pos }, Ok(None) => { // The file either didn't exist or was produced by an incompatible @@ -102,39 +95,12 @@ fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) { work_product::delete_workproduct_files(sess, &swp.work_product); } -/// Either a result that has already be computed or a -/// handle that will let us wait until it is computed -/// by a background thread. -pub enum MaybeAsync { - Sync(T), - Async(std::thread::JoinHandle), -} - -impl MaybeAsync> { - /// Accesses the data returned in [`LoadResult::Ok`] in an asynchronous way if possible. - pub fn open(self) -> LoadResult { - match self { - MaybeAsync::Sync(result) => result, - MaybeAsync::Async(handle) => { - handle.join().unwrap_or_else(|e| LoadResult::DecodeIncrCache(e)) - } - } - } -} - -/// An asynchronous type for computing the dependency graph. -pub type DepGraphFuture = MaybeAsync>; - -/// Launch a thread and load the dependency graph in the background. -pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { - // Since `sess` isn't `Sync`, we perform all accesses to `sess` - // before we fire the background thread. - +fn load_dep_graph(sess: &Session) -> LoadResult<(SerializedDepGraph, WorkProductMap)> { let prof = sess.prof.clone(); if sess.opts.incremental.is_none() { // No incremental compilation. - return MaybeAsync::Sync(LoadResult::Ok { data: Default::default() }); + return LoadResult::Ok { data: Default::default() }; } let _timer = sess.prof.generic_activity("incr_comp_prepare_load_dep_graph"); @@ -142,7 +108,6 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { // Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`. // Fortunately, we just checked that this isn't the case. let path = dep_graph_path(&sess); - let report_incremental_info = sess.opts.unstable_opts.incremental_info; let expected_hash = sess.opts.dep_tracking_hash(false); let mut prev_work_products = UnordMap::default(); @@ -180,40 +145,35 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { } } - let is_nightly_build = sess.is_nightly_build(); - let cfg_version = sess.cfg_version; - - MaybeAsync::Async(std::thread::spawn(move || { - let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph"); + let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph"); - match load_data_no_sess(&path, report_incremental_info, is_nightly_build, cfg_version) { - LoadResult::DataOutOfDate => LoadResult::DataOutOfDate, - LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err), - LoadResult::DecodeIncrCache(err) => LoadResult::DecodeIncrCache(err), - LoadResult::Ok { data: (bytes, start_pos) } => { - let mut decoder = MemDecoder::new(&bytes, start_pos); - let prev_commandline_args_hash = u64::decode(&mut decoder); + match load_data(&path, sess) { + LoadResult::DataOutOfDate => LoadResult::DataOutOfDate, + LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err), + LoadResult::DecodeIncrCache(err) => LoadResult::DecodeIncrCache(err), + LoadResult::Ok { data: (bytes, start_pos) } => { + let mut decoder = MemDecoder::new(&bytes, start_pos); + let prev_commandline_args_hash = u64::decode(&mut decoder); - if prev_commandline_args_hash != expected_hash { - if report_incremental_info { - eprintln!( - "[incremental] completely ignoring cache because of \ + if prev_commandline_args_hash != expected_hash { + if sess.opts.unstable_opts.incremental_info { + eprintln!( + "[incremental] completely ignoring cache because of \ differing commandline arguments" - ); - } - // We can't reuse the cache, purge it. - debug!("load_dep_graph_new: differing commandline arg hashes"); - - // No need to do any further work - return LoadResult::DataOutOfDate; + ); } + // We can't reuse the cache, purge it. + debug!("load_dep_graph_new: differing commandline arg hashes"); - let dep_graph = SerializedDepGraph::decode::(&mut decoder); - - LoadResult::Ok { data: (dep_graph, prev_work_products) } + // No need to do any further work + return LoadResult::DataOutOfDate; } + + let dep_graph = SerializedDepGraph::decode::(&mut decoder); + + LoadResult::Ok { data: (dep_graph, prev_work_products) } } - })) + } } /// Attempts to load the query result cache from disk @@ -235,3 +195,35 @@ pub fn load_query_result_cache(sess: &Session) -> Option> { _ => Some(OnDiskCache::new_empty(sess.source_map())), } } + +/// Setups the dependency graph by loading an existing graph from disk and set up streaming of a +/// new graph to an incremental session directory. +pub fn setup_dep_graph( + sess: &Session, + crate_name: Symbol, + stable_crate_id: StableCrateId, +) -> Result { + // `load_dep_graph` can only be called after `prepare_session_directory`. + prepare_session_directory(sess, crate_name, stable_crate_id)?; + + let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess)); + + if sess.opts.incremental.is_some() { + sess.time("incr_comp_garbage_collect_session_directories", || { + if let Err(e) = garbage_collect_session_directories(sess) { + warn!( + "Error while trying to garbage collect incremental \ + compilation cache directory: {}", + e + ); + } + }); + } + + Ok(res + .and_then(|result| { + let (prev_graph, prev_work_products) = result.open(sess); + build_dep_graph(sess, prev_graph, prev_work_products) + }) + .unwrap_or_else(DepGraph::new_disabled)) +} diff --git a/compiler/rustc_incremental/src/persist/mod.rs b/compiler/rustc_incremental/src/persist/mod.rs index 1336189bc0d26..fdecaca5a2dc1 100644 --- a/compiler/rustc_incremental/src/persist/mod.rs +++ b/compiler/rustc_incremental/src/persist/mod.rs @@ -16,9 +16,8 @@ pub use fs::in_incr_comp_dir; pub use fs::in_incr_comp_dir_sess; pub use fs::prepare_session_directory; pub use load::load_query_result_cache; +pub use load::setup_dep_graph; pub use load::LoadResult; -pub use load::{load_dep_graph, DepGraphFuture}; -pub use save::build_dep_graph; pub use save::save_dep_graph; pub use save::save_work_product_index; pub use work_product::copy_cgu_workproduct_to_incr_comp_cache_dir; diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 7719482890e6e..210da751d95e4 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -147,7 +147,7 @@ fn encode_query_cache(tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResult /// execution, the new dependency information is not kept in memory but directly /// output to this file. `save_dep_graph` then finalizes the staging dep-graph /// and moves it to the permanent dep-graph path -pub fn build_dep_graph( +pub(crate) fn build_dep_graph( sess: &Session, prev_graph: SerializedDepGraph, prev_work_products: WorkProductMap, diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 449d7a295904e..fe253febfc787 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -10,7 +10,7 @@ use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, Lrc, OnceLock, WorkerLocal}; use rustc_hir::def_id::{StableCrateId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; -use rustc_incremental::DepGraphFuture; +use rustc_incremental::setup_dep_graph; use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; @@ -19,7 +19,6 @@ use rustc_session::config::{self, CrateType, OutputFilenames, OutputType}; use rustc_session::cstore::Untracked; use rustc_session::{output::find_crate_name, Session}; use rustc_span::symbol::sym; -use rustc_span::Symbol; use std::any::Any; use std::cell::{RefCell, RefMut}; use std::sync::Arc; @@ -132,43 +131,6 @@ impl<'tcx> Queries<'tcx> { }) } - fn dep_graph_future( - &self, - crate_name: Symbol, - stable_crate_id: StableCrateId, - ) -> Result> { - let sess = self.session(); - - // `load_dep_graph` can only be called after `prepare_session_directory`. - rustc_incremental::prepare_session_directory(sess, crate_name, stable_crate_id)?; - let res = sess.opts.build_dep_graph().then(|| rustc_incremental::load_dep_graph(sess)); - - if sess.opts.incremental.is_some() { - sess.time("incr_comp_garbage_collect_session_directories", || { - if let Err(e) = rustc_incremental::garbage_collect_session_directories(sess) { - warn!( - "Error while trying to garbage collect incremental \ - compilation cache directory: {}", - e - ); - } - }); - } - - Ok(res) - } - - fn dep_graph(&self, dep_graph_future: Option) -> DepGraph { - dep_graph_future - .and_then(|future| { - let sess = self.session(); - let (prev_graph, prev_work_products) = - sess.time("blocked_on_dep_graph_loading", || future.open().open(sess)); - rustc_incremental::build_dep_graph(sess, prev_graph, prev_work_products) - }) - .unwrap_or_else(DepGraph::new_disabled) - } - pub fn global_ctxt(&'tcx self) -> Result>> { self.gcx.compute(|| { let sess = self.session(); @@ -184,10 +146,7 @@ impl<'tcx> Queries<'tcx> { sess.opts.cg.metadata.clone(), sess.cfg_version, ); - - // Compute the dependency graph (in the background). We want to do this as early as - // possible, to give the DepGraph maximum time to load before `dep_graph` is called. - let dep_graph_future = self.dep_graph_future(crate_name, stable_crate_id)?; + let dep_graph = setup_dep_graph(sess, crate_name, stable_crate_id)?; let lint_store = Lrc::new(passes::create_lint_store( sess, @@ -210,7 +169,7 @@ impl<'tcx> Queries<'tcx> { crate_types, stable_crate_id, lint_store, - self.dep_graph(dep_graph_future), + dep_graph, untracked, &self.gcx_cell, &self.arena, From 073feb5e69c762bffc53e615d2fde844e0d08a92 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 24 Sep 2023 10:44:31 +0200 Subject: [PATCH 096/149] Rename the legacy feature gating macro It had a really confusing name by shadowing the previous name, which has caused issues in the past where people added their new syntax in the legacy location. This makes it clear. --- compiler/rustc_ast_passes/src/feature_gate.rs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 8405ae6ff8e8b..3b80751698bcf 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -578,11 +578,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { } } - // All uses of `gate_all!` below this point were added in #65742, + // All uses of `gate_all_legacy_dont_use!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). // We emit an early future-incompatible warning for these. // New syntax gates should go above here to get a hard error gate. - macro_rules! gate_all { + macro_rules! gate_all_legacy_dont_use { ($gate:ident, $msg:literal) => { for span in spans.get(&sym::$gate).unwrap_or(&vec![]) { gate_feature_post!(future_incompatible; &visitor, $gate, *span, $msg); @@ -590,13 +590,16 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { }; } - gate_all!(trait_alias, "trait aliases are experimental"); - gate_all!(associated_type_bounds, "associated type bounds are unstable"); - gate_all!(return_type_notation, "return type notation is experimental"); - gate_all!(decl_macro, "`macro` is experimental"); - gate_all!(box_patterns, "box pattern syntax is experimental"); - gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental"); - gate_all!(try_blocks, "`try` blocks are unstable"); + gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); + gate_all_legacy_dont_use!(associated_type_bounds, "associated type bounds are unstable"); + gate_all_legacy_dont_use!(return_type_notation, "return type notation is experimental"); + gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental"); + gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental"); + gate_all_legacy_dont_use!( + exclusive_range_pattern, + "exclusive range pattern syntax is experimental" + ); + gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable"); visit::walk_crate(&mut visitor, krate); } From 845a027ddb3750bb841d0676b7eb460b7c1a2f9d Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 24 Sep 2023 10:47:31 +0200 Subject: [PATCH 097/149] Add comment about RTN feature gating --- compiler/rustc_ast_passes/src/feature_gate.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 3b80751698bcf..62dc7ae58a2f0 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -592,6 +592,9 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); gate_all_legacy_dont_use!(associated_type_bounds, "associated type bounds are unstable"); + // Despite being a new feature, `where T: Trait`, which is RTN syntax now, + // used to be gated under associated_type_bounds, which are right above, so RTN needs to + // be too. gate_all_legacy_dont_use!(return_type_notation, "return type notation is experimental"); gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental"); gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental"); From ded1a8b026205167fd427743a8920c10fecd4888 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 3 Aug 2023 15:30:26 +0000 Subject: [PATCH 098/149] Remove dead code. --- .../src/traits/error_reporting/suggestions.rs | 74 ++----------------- 1 file changed, 8 insertions(+), 66 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 748ecdc01b460..15f2ba809a4a6 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -49,7 +49,7 @@ use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; #[derive(Debug)] pub enum GeneratorInteriorOrUpvar { // span of interior type - Interior(Span, Option<(Option, Span, Option, Option)>), + Interior(Span, Option<(Span, Option)>), // span of upvar Upvar(Span), } @@ -249,7 +249,6 @@ pub trait TypeErrCtxtExt<'tcx> { outer_generator: Option, trait_pred: ty::TraitPredicate<'tcx>, target_ty: Ty<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, obligation: &PredicateObligation<'tcx>, next_code: Option<&ObligationCauseCode<'tcx>>, ); @@ -2316,7 +2315,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if ty_matches(ty::Binder::dummy(decl.ty)) && !decl.ignore_for_traits { interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior( decl.source_info.span, - Some((None, source_info.span, None, from_awaited_ty)), + Some((source_info.span, from_awaited_ty)), )); break 'find_source; } @@ -2336,7 +2335,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { debug!(?interior_or_upvar_span); if let Some(interior_or_upvar_span) = interior_or_upvar_span { let is_async = self.tcx.generator_is_async(generator_did); - let typeck_results = generator_data.0; self.note_obligation_cause_for_async_await( err, interior_or_upvar_span, @@ -2344,7 +2342,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { outer_generator, trait_ref, target_ty, - typeck_results, obligation, next_code, ); @@ -2365,7 +2362,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { outer_generator: Option, trait_pred: ty::TraitPredicate<'tcx>, target_ty: Ty<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, obligation: &PredicateObligation<'tcx>, next_code: Option<&ObligationCauseCode<'tcx>>, ) { @@ -2423,9 +2419,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { format!("does not implement `{}`", trait_pred.print_modifiers_and_trait_path()) }; - let mut explain_yield = |interior_span: Span, - yield_span: Span, - scope_span: Option| { + let mut explain_yield = |interior_span: Span, yield_span: Span| { let mut span = MultiSpan::from_span(yield_span); let snippet = match source_map.span_to_snippet(interior_span) { // #70935: If snippet contains newlines, display "the value" instead @@ -2457,22 +2451,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { interior_span, format!("has type `{target_ty}` which {trait_explanation}"), ); - if let Some(scope_span) = scope_span { - let scope_span = source_map.end_point(scope_span); - - let msg = format!("{snippet} is later dropped here"); - span.push_span_label(scope_span, msg); - } err.span_note( - span, - format!( - "{future_or_generator} {trait_explanation} as this value is used across {an_await_or_yield}" - ), - ); + span, + format!("{future_or_generator} {trait_explanation} as this value is used across {an_await_or_yield}"), + ); }; match interior_or_upvar_span { GeneratorInteriorOrUpvar::Interior(interior_span, interior_extra_info) => { - if let Some((scope_span, yield_span, expr, from_awaited_ty)) = interior_extra_info { + if let Some((yield_span, from_awaited_ty)) = interior_extra_info { if let Some(await_span) = from_awaited_ty { // The type causing this obligation is one being awaited at await_span. let mut span = MultiSpan::from_span(await_span); @@ -2490,51 +2476,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } else { // Look at the last interior type to get a span for the `.await`. - explain_yield(interior_span, yield_span, scope_span); - } - - if let Some(expr_id) = expr { - let expr = hir.expect_expr(expr_id); - debug!("target_ty evaluated from {:?}", expr); - - let parent = hir.parent_id(expr_id); - if let Some(hir::Node::Expr(e)) = hir.find(parent) { - let parent_span = hir.span(parent); - let parent_did = parent.owner.to_def_id(); - // ```rust - // impl T { - // fn foo(&self) -> i32 {} - // } - // T.foo(); - // ^^^^^^^ a temporary `&T` created inside this method call due to `&self` - // ``` - // - let is_region_borrow = typeck_results - .expr_adjustments(expr) - .iter() - .any(|adj| adj.is_region_borrow()); - - // ```rust - // struct Foo(*const u8); - // bar(Foo(std::ptr::null())).await; - // ^^^^^^^^^^^^^^^^^^^^^ raw-ptr `*T` created inside this struct ctor. - // ``` - debug!(parent_def_kind = ?self.tcx.def_kind(parent_did)); - let is_raw_borrow_inside_fn_like_call = - match self.tcx.def_kind(parent_did) { - DefKind::Fn | DefKind::Ctor(..) => target_ty.is_unsafe_ptr(), - _ => false, - }; - if (typeck_results.is_method_call(e) && is_region_borrow) - || is_raw_borrow_inside_fn_like_call - { - err.span_help( - parent_span, - "consider moving this into a `let` \ - binding to create a shorter lived borrow", - ); - } - } + explain_yield(interior_span, yield_span); } } } From 26cb34cd182b57f6595bc64f1c9f5498fbadadeb Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 3 Aug 2023 15:56:56 +0000 Subject: [PATCH 099/149] Remove span from BrAnon. --- .../src/diagnostics/region_name.rs | 2 +- compiler/rustc_borrowck/src/renumber.rs | 14 ++++--------- compiler/rustc_borrowck/src/type_check/mod.rs | 9 ++++----- .../src/type_check/relate_tys.rs | 9 ++++----- .../rustc_borrowck/src/universal_regions.rs | 20 ++++++------------- .../rustc_hir_analysis/src/astconv/mod.rs | 4 ++-- .../rustc_hir_analysis/src/check/intrinsic.rs | 8 ++++---- compiler/rustc_hir_typeck/src/check.rs | 8 ++++---- .../src/errors/note_and_explain.rs | 7 ++----- .../src/infer/canonical/canonicalizer.rs | 2 +- .../src/infer/error_reporting/mod.rs | 13 +++--------- .../nice_region_error/named_anon_conflict.rs | 2 +- .../nice_region_error/placeholder_relation.rs | 6 ++---- compiler/rustc_middle/src/infer/canonical.rs | 2 +- compiler/rustc_middle/src/mir/query.rs | 3 +-- compiler/rustc_middle/src/ty/context.rs | 4 ++-- compiler/rustc_middle/src/ty/fold.rs | 2 +- compiler/rustc_middle/src/ty/print/pretty.rs | 4 ++-- .../rustc_middle/src/ty/structural_impls.rs | 2 +- compiler/rustc_middle/src/ty/sty.rs | 6 +++--- compiler/rustc_smir/src/rustc_smir/mod.rs | 6 ++---- compiler/rustc_smir/src/stable_mir/ty.rs | 2 +- compiler/rustc_symbol_mangling/src/v0.rs | 2 +- .../src/solve/assembly/structural_traits.rs | 5 ++--- .../src/solve/canonicalize.rs | 2 +- .../src/traits/select/mod.rs | 9 +++++---- .../escape-argument-callee.stderr | 2 +- .../escape-argument.stderr | 2 +- ...pagate-approximated-fail-no-postdom.stderr | 2 +- .../propagate-approximated-ref.stderr | 2 +- ...er-to-static-comparing-against-free.stderr | 4 ++-- ...oximated-shorter-to-static-no-bound.stderr | 2 +- ...mated-shorter-to-static-wrong-bound.stderr | 2 +- .../propagate-approximated-val.stderr | 2 +- .../propagate-despite-same-free-region.stderr | 2 +- ...ail-to-approximate-longer-no-bounds.stderr | 2 +- ...-to-approximate-longer-wrong-bounds.stderr | 2 +- .../return-wrong-bound-region.stderr | 2 +- ...ram-closure-approximate-lower-bound.stderr | 4 ++-- 39 files changed, 76 insertions(+), 107 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 096bf826cfb32..30b47d076e27e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -354,7 +354,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { }) } - ty::BoundRegionKind::BrAnon(..) => None, + ty::BoundRegionKind::BrAnon => None, }, ty::ReLateBound(..) diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index 4c7c4982050e0..5d6f5cc896782 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -7,7 +7,7 @@ use rustc_middle::mir::visit::{MutVisitor, TyContext}; use rustc_middle::mir::{Body, ConstOperand, Location, Promoted}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc_span::{Span, Symbol}; +use rustc_span::Symbol; /// Replaces all free regions appearing in the MIR with fresh /// inference variables, returning the number of variables created. @@ -28,21 +28,15 @@ pub fn renumber_mir<'tcx>( renumberer.visit_body(body); } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub(crate) enum BoundRegionInfo { - Name(Symbol), - Span(Span), -} - #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub(crate) enum RegionCtxt { Location(Location), TyContext(TyContext), Free(Symbol), - Bound(BoundRegionInfo), - LateBound(BoundRegionInfo), + Bound(Symbol), + LateBound(Symbol), Existential(Option), - Placeholder(BoundRegionInfo), + Placeholder(Symbol), Unknown, } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 60e6dcaf0b887..aa750dd0169eb 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1367,14 +1367,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } }; let (sig, map) = tcx.replace_late_bound_regions(sig, |br| { - use crate::renumber::{BoundRegionInfo, RegionCtxt}; + use crate::renumber::RegionCtxt; let region_ctxt_fn = || { let reg_info = match br.kind { - ty::BoundRegionKind::BrAnon(Some(span)) => BoundRegionInfo::Span(span), - ty::BoundRegionKind::BrAnon(..) => BoundRegionInfo::Name(sym::anon), - ty::BoundRegionKind::BrNamed(_, name) => BoundRegionInfo::Name(name), - ty::BoundRegionKind::BrEnv => BoundRegionInfo::Name(sym::env), + ty::BoundRegionKind::BrAnon => sym::anon, + ty::BoundRegionKind::BrNamed(_, name) => name, + ty::BoundRegionKind::BrEnv => sym::env, }; RegionCtxt::LateBound(reg_info) diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index e0c6295627bbf..c1f82e19c02e2 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -11,7 +11,7 @@ use rustc_span::{Span, Symbol}; use crate::constraints::OutlivesConstraint; use crate::diagnostics::UniverseInfo; -use crate::renumber::{BoundRegionInfo, RegionCtxt}; +use crate::renumber::RegionCtxt; use crate::type_check::{InstantiateOpaqueType, Locations, TypeChecker}; impl<'a, 'tcx> TypeChecker<'a, 'tcx> { @@ -126,10 +126,9 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> .placeholder_region(self.type_checker.infcx, placeholder); let reg_info = match placeholder.bound.kind { - ty::BoundRegionKind::BrAnon(Some(span)) => BoundRegionInfo::Span(span), - ty::BoundRegionKind::BrAnon(..) => BoundRegionInfo::Name(sym::anon), - ty::BoundRegionKind::BrNamed(_, name) => BoundRegionInfo::Name(name), - ty::BoundRegionKind::BrEnv => BoundRegionInfo::Name(sym::env), + ty::BoundRegionKind::BrAnon => sym::anon, + ty::BoundRegionKind::BrNamed(_, name) => name, + ty::BoundRegionKind::BrEnv => sym::env, }; if cfg!(debug_assertions) { diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 02b52784edec1..e00299bad66d1 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -28,7 +28,7 @@ use rustc_span::symbol::{kw, sym}; use rustc_span::Symbol; use std::iter; -use crate::renumber::{BoundRegionInfo, RegionCtxt}; +use crate::renumber::RegionCtxt; use crate::BorrowckInferCtxt; #[derive(Debug)] @@ -446,9 +446,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { if !indices.indices.contains_key(&r) { let region_vid = { let name = r.get_name_or_anon(); - self.infcx.next_nll_region_var(FR, || { - RegionCtxt::LateBound(BoundRegionInfo::Name(name)) - }) + self.infcx.next_nll_region_var(FR, || RegionCtxt::LateBound(name)) }; debug!(?region_vid); @@ -480,9 +478,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { if !indices.indices.contains_key(&r) { let region_vid = { let name = r.get_name_or_anon(); - self.infcx.next_nll_region_var(FR, || { - RegionCtxt::LateBound(BoundRegionInfo::Name(name)) - }) + self.infcx.next_nll_region_var(FR, || RegionCtxt::LateBound(name)) }; debug!(?region_vid); @@ -796,7 +792,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> { _ => sym::anon, }; - self.next_nll_region_var(origin, || RegionCtxt::Bound(BoundRegionInfo::Name(name))) + self.next_nll_region_var(origin, || RegionCtxt::Bound(name)) }; indices.insert_late_bound_region(liberated_region, region_vid.as_var()); @@ -826,9 +822,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> { if !indices.indices.contains_key(&r) { let region_vid = { let name = r.get_name_or_anon(); - self.next_nll_region_var(FR, || { - RegionCtxt::LateBound(BoundRegionInfo::Name(name)) - }) + self.next_nll_region_var(FR, || RegionCtxt::LateBound(name)) }; debug!(?region_vid); @@ -848,9 +842,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> { if !indices.indices.contains_key(&r) { let region_vid = { let name = r.get_name_or_anon(); - self.next_nll_region_var(FR, || { - RegionCtxt::LateBound(BoundRegionInfo::Name(name)) - }) + self.next_nll_region_var(FR, || RegionCtxt::LateBound(name)) }; indices.insert_late_bound_region(r, region_vid.as_var()); diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 3a97a383ee19e..106cd223d302e 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2781,7 +2781,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) { for br in referenced_regions.difference(&constrained_regions) { let br_name = match *br { - ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(..) | ty::BrEnv => { + ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon | ty::BrEnv => { "an anonymous lifetime".to_string() } ty::BrNamed(_, name) => format!("lifetime `{name}`"), @@ -2789,7 +2789,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut err = generate_err(&br_name); - if let ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(..) = *br { + if let ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon = *br { // The only way for an anonymous lifetime to wind up // in the return type but **also** be unconstrained is // if it only appears in "associated types" in the diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 444103ffe8f33..3c0d977917fd9 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -137,7 +137,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { let name_str = intrinsic_name.as_str(); let bound_vars = tcx.mk_bound_variable_kinds(&[ - ty::BoundVariableKind::Region(ty::BrAnon(None)), + ty::BoundVariableKind::Region(ty::BrAnon), ty::BoundVariableKind::Region(ty::BrEnv), ]); let mk_va_list_ty = |mutbl| { @@ -145,7 +145,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { let region = ty::Region::new_late_bound( tcx, ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(None) }, + ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }, ); let env_region = ty::Region::new_late_bound( tcx, @@ -405,7 +405,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { ); let discriminant_def_id = assoc_items[0]; - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(None) }; + let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }; ( 1, vec![Ty::new_imm_ref( @@ -463,7 +463,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { } sym::raw_eq => { - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(None) }; + let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }; let param_ty = Ty::new_imm_ref( tcx, ty::Region::new_late_bound(tcx, ty::INNERMOST, br), diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 041cc1abd54e4..1fa0ec173a7e4 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -204,7 +204,7 @@ fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_> &[ty::GenericArg::from(ty::Region::new_late_bound( tcx, ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrAnon(None) }, + ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrAnon }, ))], ); let panic_info_ref_ty = Ty::new_imm_ref( @@ -212,14 +212,14 @@ fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_> ty::Region::new_late_bound( tcx, ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(None) }, + ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }, ), panic_info_ty, ); let bounds = tcx.mk_bound_variable_kinds(&[ - ty::BoundVariableKind::Region(ty::BrAnon(None)), - ty::BoundVariableKind::Region(ty::BrAnon(None)), + ty::BoundVariableKind::Region(ty::BrAnon), + ty::BoundVariableKind::Region(ty::BrAnon), ]); let expected_sig = ty::Binder::bind_with_vars( tcx.mk_fn_sig([panic_info_ref_ty], tcx.types.never, false, fn_sig.unsafety, Abi::Rust), diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index bd168f047faf5..9276bb0a7b7c1 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -56,11 +56,8 @@ impl<'a> DescriptionCtx<'a> { (Some(span), "as_defined", name.to_string()) } } - ty::BrAnon(span) => { - let span = match span { - Some(_) => span, - None => Some(tcx.def_span(scope)), - }; + ty::BrAnon => { + let span = Some(tcx.def_span(scope)); (span, "defined_here", String::new()) } _ => { diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 5978e2c743c90..4124c9eada9c1 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -775,7 +775,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { let var = self.canonical_var(info, r.into()); - let br = ty::BoundRegion { var, kind: ty::BrAnon(None) }; + let br = ty::BoundRegion { var, kind: ty::BrAnon }; ty::Region::new_late_bound(self.interner(), self.binder_index, br) } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index e418864026f6c..72cfc1337e2f0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -242,12 +242,9 @@ fn msg_span_from_named_region<'tcx>( }; (text, Some(span)) } - ty::BrAnon(span) => ( + ty::BrAnon => ( "the anonymous lifetime as defined here".to_string(), - Some(match span { - Some(span) => span, - None => tcx.def_span(scope) - }) + Some(tcx.def_span(scope)) ), _ => ( format!("the lifetime `{region}` as defined here"), @@ -262,11 +259,7 @@ fn msg_span_from_named_region<'tcx>( .. }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))), ty::RePlaceholder(ty::PlaceholderRegion { - bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(Some(span)), .. }, - .. - }) => ("the anonymous lifetime defined here".to_owned(), Some(span)), - ty::RePlaceholder(ty::PlaceholderRegion { - bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(None), .. }, + bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. }, .. }) => ("an anonymous lifetime".to_owned(), None), _ => bug!("{:?}", region), diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs index 07f04ec1e4449..1b43022f8f7d0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -61,7 +61,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let is_impl_item = region_info.is_impl_item; match br { - ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(..) => {} + ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon => {} _ => { /* not an anonymous region */ debug!("try_report_named_anon_conflict: not an anonymous region"); diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs index 8a78a1956c997..f5b8912532b11 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs @@ -36,15 +36,13 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { ty::BrNamed(def_id, symbol) => { (Some(self.tcx().def_span(def_id)), Some(symbol)) } - ty::BrAnon(span) => (*span, None), - ty::BrEnv => (None, None), + ty::BrAnon | ty::BrEnv => (None, None), }; let (sup_span, sup_symbol) = match sup_name { ty::BrNamed(def_id, symbol) => { (Some(self.tcx().def_span(def_id)), Some(symbol)) } - ty::BrAnon(span) => (*span, None), - ty::BrEnv => (None, None), + ty::BrAnon | ty::BrEnv => (None, None), }; let diag = match (sub_span, sup_span, sub_symbol, sup_symbol) { (Some(sub_span), Some(sup_span), Some(&sub_symbol), Some(&sup_symbol)) => { diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 2f2597d6b2232..41beca072bfea 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -455,7 +455,7 @@ impl<'tcx> CanonicalVarValues<'tcx> { CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => { let br = ty::BoundRegion { var: ty::BoundVar::from_usize(i), - kind: ty::BrAnon(None), + kind: ty::BrAnon, }; ty::Region::new_late_bound(tcx, ty::INNERMOST, br).into() } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index c157b7052abb3..c74a9536b6335 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -414,8 +414,7 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> { pub fn bind(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { let inner = tcx.fold_regions(ty, |r, depth| match r.kind() { ty::ReVar(vid) => { - let br = - ty::BoundRegion { var: ty::BoundVar::new(vid.index()), kind: ty::BrAnon(None) }; + let br = ty::BoundRegion { var: ty::BoundVar::new(vid.index()), kind: ty::BrAnon }; ty::Region::new_late_bound(tcx, depth, br) } _ => bug!("unexpected region in ClosureOutlivesSubjectTy: {r:?}"), diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 400d4bcad5b0e..a7dd6dc737da6 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -318,7 +318,7 @@ pub struct CommonLifetimes<'tcx> { pub re_vars: Vec>, /// Pre-interned values of the form: - /// `ReLateBound(DebruijnIndex(i), BoundRegion { var: v, kind: BrAnon(None) })` + /// `ReLateBound(DebruijnIndex(i), BoundRegion { var: v, kind: BrAnon })` /// for small values of `i` and `v`. pub re_late_bounds: Vec>>, } @@ -395,7 +395,7 @@ impl<'tcx> CommonLifetimes<'tcx> { .map(|v| { mk(ty::ReLateBound( ty::DebruijnIndex::from(i), - ty::BoundRegion { var: ty::BoundVar::from(v), kind: ty::BrAnon(None) }, + ty::BoundRegion { var: ty::BoundVar::from(v), kind: ty::BrAnon }, )) }) .collect() diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 77cf6bee79d64..00529a1e066cc 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -385,7 +385,7 @@ impl<'tcx> TyCtxt<'tcx> { let index = entry.index(); let var = ty::BoundVar::from_usize(index); let kind = entry - .or_insert_with(|| ty::BoundVariableKind::Region(ty::BrAnon(None))) + .or_insert_with(|| ty::BoundVariableKind::Region(ty::BrAnon)) .expect_region(); let br = ty::BoundRegion { var, kind }; ty::Region::new_late_bound(self.tcx, ty::INNERMOST, br) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 34e3479c58e40..99dc0b2607eb5 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2330,7 +2330,7 @@ impl<'a, 'tcx> ty::TypeFolder> for RegionFolder<'a, 'tcx> { // If this is an anonymous placeholder, don't rename. Otherwise, in some // async fns, we get a `for<'r> Send` bound match kind { - ty::BrAnon(..) | ty::BrEnv => r, + ty::BrAnon | ty::BrEnv => r, _ => { // Index doesn't matter, since this is just for naming and these never get bound let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind }; @@ -2451,7 +2451,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { binder_level_idx: ty::DebruijnIndex, br: ty::BoundRegion| { let (name, kind) = match br.kind { - ty::BrAnon(..) | ty::BrEnv => { + ty::BrAnon | ty::BrEnv => { let name = next_name(&self); if let Some(lt_idx) = lifetime_idx { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index f6372ef3f2f48..2adbe9e030997 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -68,7 +68,7 @@ impl<'tcx> fmt::Debug for ty::adjustment::Adjustment<'tcx> { impl fmt::Debug for ty::BoundRegionKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - ty::BrAnon(span) => write!(f, "BrAnon({span:?})"), + ty::BrAnon => write!(f, "BrAnon"), ty::BrNamed(did, name) => { if did.is_crate_root() { write!(f, "BrNamed({name})") diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index d2c42235adb9f..c823c4c2b57a0 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -69,7 +69,7 @@ pub struct FreeRegion { #[derive(HashStable)] pub enum BoundRegionKind { /// An anonymous region parameter for a given fn (&T) - BrAnon(Option), + BrAnon, /// Named region parameters for functions (a in &'a T) /// @@ -1465,7 +1465,7 @@ impl<'tcx> Region<'tcx> { bound_region: ty::BoundRegion, ) -> Region<'tcx> { // Use a pre-interned one when possible. - if let ty::BoundRegion { var, kind: ty::BrAnon(None) } = bound_region + if let ty::BoundRegion { var, kind: ty::BrAnon } = bound_region && let Some(inner) = tcx.lifetimes.re_late_bounds.get(debruijn.as_usize()) && let Some(re) = inner.get(var.as_usize()).copied() { @@ -3010,7 +3010,7 @@ mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; // tidy-alphabetical-start - static_assert_size!(RegionKind<'_>, 28); + static_assert_size!(RegionKind<'_>, 24); static_assert_size!(TyKind<'_>, 32); // tidy-alphabetical-end } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 07aeac6b5397c..b575445c4d5d2 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -979,13 +979,11 @@ impl<'tcx> Stable<'tcx> for ty::BoundTyKind { impl<'tcx> Stable<'tcx> for ty::BoundRegionKind { type T = stable_mir::ty::BoundRegionKind; - fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use stable_mir::ty::BoundRegionKind; match self { - ty::BoundRegionKind::BrAnon(option_span) => { - BoundRegionKind::BrAnon(option_span.map(|span| span.stable(tables))) - } + ty::BoundRegionKind::BrAnon => BoundRegionKind::BrAnon, ty::BoundRegionKind::BrNamed(def_id, symbol) => { BoundRegionKind::BrNamed(rustc_internal::br_named_def(*def_id), symbol.to_string()) } diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 3a8fc1a502e6d..eb9967b3888eb 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -286,7 +286,7 @@ pub enum BoundTyKind { #[derive(Clone, Debug)] pub enum BoundRegionKind { - BrAnon(Option), + BrAnon, BrNamed(BrNamedDef, String), BrEnv, } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index ec94deb4658a4..82b1a772e3d7b 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -329,7 +329,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // Late-bound lifetimes use indices starting at 1, // see `BinderLevel` for more details. - ty::ReLateBound(debruijn, ty::BoundRegion { var, kind: ty::BrAnon(_) }) => { + ty::ReLateBound(debruijn, ty::BoundRegion { var, kind: ty::BrAnon }) => { let binder = &self.binders[self.binders.len() - 1 - debruijn.index()]; let depth = binder.lifetime_depths.start + var.as_u32(); diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index 5935c614ffd26..26c68acddffef 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -94,8 +94,7 @@ pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>( let mut counter = 0; let ty = tcx.fold_regions(ty, |r, current_depth| match r.kind() { ty::ReErased => { - let br = - ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon(None) }; + let br = ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon }; counter += 1; ty::Region::new_late_bound(tcx, current_depth, br) } @@ -103,7 +102,7 @@ pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>( r => bug!("unexpected region: {r:?}"), }); let bound_vars = tcx.mk_bound_variable_kinds_from_iter( - (0..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon(None))), + (0..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon)), ); ty::Binder::bind_with_vars(ty, bound_vars) } diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs index 64b1321e51d6a..aa92b924ef2e5 100644 --- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs +++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs @@ -269,7 +269,7 @@ impl<'tcx> TypeFolder> for Canonicalizer<'_, 'tcx> { self.primitive_var_infos.push(CanonicalVarInfo { kind }); var }); - let br = ty::BoundRegion { var, kind: BrAnon(None) }; + let br = ty::BoundRegion { var, kind: BrAnon }; ty::Region::new_late_bound(self.interner(), self.binder_index, br) } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 7d672e658c797..bc9ba85fc9f1b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -3102,7 +3102,7 @@ fn bind_generator_hidden_types_above<'tcx>( ty::ReErased => { let br = ty::BoundRegion { var: ty::BoundVar::from_u32(counter), - kind: ty::BrAnon(None), + kind: ty::BrAnon, }; counter += 1; ty::Region::new_late_bound(tcx, current_depth, br) @@ -3118,8 +3118,9 @@ fn bind_generator_hidden_types_above<'tcx>( if considering_regions { debug_assert!(!hidden_types.has_erased_regions()); } - let bound_vars = tcx.mk_bound_variable_kinds_from_iter(bound_vars.iter().chain( - (num_bound_variables..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon(None))), - )); + let bound_vars = + tcx.mk_bound_variable_kinds_from_iter(bound_vars.iter().chain( + (num_bound_variables..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon)), + )); ty::Binder::bind_with_vars(hidden_types, bound_vars) } diff --git a/tests/ui/nll/closure-requirements/escape-argument-callee.stderr b/tests/ui/nll/closure-requirements/escape-argument-callee.stderr index 1e3a5328d3d35..b3cb7813e19c8 100644 --- a/tests/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/tests/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -6,7 +6,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); | = note: defining type: test::{closure#0} with closure args [ i16, - for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon(None) }) i32)), + for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon }) i32)), (), ] diff --git a/tests/ui/nll/closure-requirements/escape-argument.stderr b/tests/ui/nll/closure-requirements/escape-argument.stderr index bc4ba93f88499..4f0156728ac1e 100644 --- a/tests/ui/nll/closure-requirements/escape-argument.stderr +++ b/tests/ui/nll/closure-requirements/escape-argument.stderr @@ -6,7 +6,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); | = note: defining type: test::{closure#0} with closure args [ i16, - for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) i32)), + for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) i32)), (), ] diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index f8383cc42a205..ccf56bf6f37df 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -6,7 +6,7 @@ LL | |_outlives1, _outlives2, _outlives3, x, y| { | = note: defining type: supply::{closure#0} with closure args [ i16, - for extern "rust-call" fn((std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) u32>, std::cell::Cell<&'?2 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) &'?3 u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) u32>)), + for extern "rust-call" fn((std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) u32>, std::cell::Cell<&'?2 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) &'?3 u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) u32>)), (), ] = note: late-bound region is '?4 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr index 113173d8f764c..a16433c9d374b 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -6,7 +6,7 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y | = note: defining type: supply::{closure#0} with closure args [ i16, - for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon(None) }) &'?2 u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon(None) }) u32>)), + for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon }) &'?2 u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon }) u32>)), (), ] = note: late-bound region is '?3 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index ba15199ab5a11..9e0f16c0fc783 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -6,7 +6,7 @@ LL | foo(cell, |cell_a, cell_x| { | = note: defining type: case1::{closure#0} with closure args [ i32, - for extern "rust-call" fn((std::cell::Cell<&'?1 u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) u32>)), + for extern "rust-call" fn((std::cell::Cell<&'?1 u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) u32>)), (), ] @@ -36,7 +36,7 @@ LL | foo(cell, |cell_a, cell_x| { | = note: defining type: case2::{closure#0} with closure args [ i32, - for extern "rust-call" fn((std::cell::Cell<&'?1 u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) u32>)), + for extern "rust-call" fn((std::cell::Cell<&'?1 u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) u32>)), (), ] = note: number of external vids: 2 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index f2bf83c6c59e4..e4989e321550f 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -6,7 +6,7 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { | = note: defining type: supply::{closure#0} with closure args [ i16, - for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrAnon(None) }) u32>)), + for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrAnon }) u32>)), (), ] = note: late-bound region is '?2 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index 2734326ed6471..be35e62d0704b 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -6,7 +6,7 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y | = note: defining type: supply::{closure#0} with closure args [ i16, - for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon(None) }) std::cell::Cell<&'?2 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon(None) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon(None) }) u32>)), + for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon }) std::cell::Cell<&'?2 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon }) u32>)), (), ] = note: late-bound region is '?3 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr index 5ab321eb666de..8880dd816a1fe 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -6,7 +6,7 @@ LL | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { | = note: defining type: test::{closure#0} with closure args [ i16, - for extern "rust-call" fn((std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) &'?2 u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) u32>)), + for extern "rust-call" fn((std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) &'?2 u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) u32>)), (), ] = note: late-bound region is '?3 diff --git a/tests/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr b/tests/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr index 595fd5ff565cb..47774b63f8186 100644 --- a/tests/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr +++ b/tests/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr @@ -6,7 +6,7 @@ LL | |_outlives1, _outlives2, x, y| { | = note: defining type: supply::{closure#0} with closure args [ i16, - for extern "rust-call" fn((std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) &'?2 u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) u32>)), + for extern "rust-call" fn((std::cell::Cell<&'?1 &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) &'?2 u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) u32>)), (), ] = note: late-bound region is '?3 diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index 62b0e3eed85b5..3404bb128278d 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -6,7 +6,7 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { | = note: defining type: supply::{closure#0} with closure args [ i16, - for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) &'?1 u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon(None) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) u32>)), + for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) &'?1 u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) u32>)), (), ] = note: late-bound region is '?2 diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index 6f2044d621eb3..e40648912e3cf 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -6,7 +6,7 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y | = note: defining type: supply::{closure#0} with closure args [ i16, - for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) &'?1 u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon(None) }) &'?2 u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrAnon(None) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon(None) }) u32>)), + for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) &'?1 u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon }) &'?2 u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrAnon }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrAnon }) u32>)), (), ] = note: late-bound region is '?3 diff --git a/tests/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/tests/ui/nll/closure-requirements/return-wrong-bound-region.stderr index 7635f2ede0a31..18fb7195d021f 100644 --- a/tests/ui/nll/closure-requirements/return-wrong-bound-region.stderr +++ b/tests/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -6,7 +6,7 @@ LL | expect_sig(|a, b| b); // ought to return `a` | = note: defining type: test::{closure#0} with closure args [ i16, - for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) i32)) -> &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon(None) }) i32, + for extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) i32)) -> &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrAnon }) i32, (), ] diff --git a/tests/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr b/tests/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr index 660211fe21ad0..f58d49d8461b0 100644 --- a/tests/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr +++ b/tests/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr @@ -6,7 +6,7 @@ LL | twice(cell, value, |a, b| invoke(a, b)); | = note: defining type: generic::::{closure#0} with closure args [ i16, - for extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) T)), + for extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) T)), (), ] = note: number of external vids: 2 @@ -28,7 +28,7 @@ LL | twice(cell, value, |a, b| invoke(a, b)); | = note: defining type: generic_fail::::{closure#0} with closure args [ i16, - for extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon(None) }) T)), + for extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrAnon }) T)), (), ] = note: late-bound region is '?2 From 79f3fe48b04fcf4a87316a2e61b098153d959657 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 24 Sep 2023 14:09:38 +0200 Subject: [PATCH 100/149] Add regression test for #102467 --- tests/rustdoc-ui/issue-102467.rs | 15 +++++++++++++++ tests/rustdoc-ui/issue-102467.stderr | 9 +++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/rustdoc-ui/issue-102467.rs create mode 100644 tests/rustdoc-ui/issue-102467.stderr diff --git a/tests/rustdoc-ui/issue-102467.rs b/tests/rustdoc-ui/issue-102467.rs new file mode 100644 index 0000000000000..bff876e41d66a --- /dev/null +++ b/tests/rustdoc-ui/issue-102467.rs @@ -0,0 +1,15 @@ +// Regression test for . +// It ensures that the expected error is displayed. + +#![feature(associated_const_equality)] + +trait T { + type A: S = 34>; + //~^ ERROR associated type bindings are not allowed here +} + +trait S { + const C: i32; +} + +fn main() {} diff --git a/tests/rustdoc-ui/issue-102467.stderr b/tests/rustdoc-ui/issue-102467.stderr new file mode 100644 index 0000000000000..a337293f7a055 --- /dev/null +++ b/tests/rustdoc-ui/issue-102467.stderr @@ -0,0 +1,9 @@ +error[E0229]: associated type bindings are not allowed here + --> $DIR/issue-102467.rs:7:17 + | +LL | type A: S = 34>; + | ^^^^^^^^ associated type not allowed here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0229`. From 141c4636a7c84e45984f3153245e6d29cec1f1d2 Mon Sep 17 00:00:00 2001 From: chenx97 Date: Thu, 21 Sep 2023 19:31:01 +0800 Subject: [PATCH 101/149] deps: update rustix and linux-raw-sys for MIPS R6 commands that perform this update: ```shell cargo +nightly update tempfile clap cargo +nightly update linux-raw-sys rustix ``` --- Cargo.lock | 92 ++++++++++++++++-------------------------------------- 1 file changed, 27 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2025e58a8d1d..c60a37e24c310 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,16 +118,15 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] @@ -157,9 +156,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -291,9 +290,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "block-buffer" @@ -459,25 +458,23 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.10" +version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a" +checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.10" +version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d" +checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" dependencies = [ "anstream", "anstyle", "clap_lex", - "once_cell", "strsim", "terminal_size", ] @@ -493,9 +490,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", "proc-macro2", @@ -1253,12 +1250,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "field-offset" @@ -1993,17 +1987,6 @@ dependencies = [ "unic-langid", ] -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.2", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" version = "2.7.2" @@ -2017,7 +2000,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" dependencies = [ "hermit-abi 0.3.2", - "rustix 0.38.2", + "rustix", "windows-sys 0.48.0", ] @@ -2214,15 +2197,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "litemap" @@ -4711,28 +4688,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8818fa822adcc98b18fedbb3632a6a33213c070556b5aa7c4c8cc21cff565c4c" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.2" +version = "0.38.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabcb0461ebd01d6b79945797c27f8529082226cb630a9865a71870ff63532a4" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys 0.4.3", + "linux-raw-sys", "windows-sys 0.48.0", ] @@ -5196,15 +5159,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.6.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ - "autocfg", "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.22", + "rustix", "windows-sys 0.48.0", ] @@ -5241,11 +5203,11 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.37.22", + "rustix", "windows-sys 0.48.0", ] From 8e558bcf1efd85992cbf60b313d5e0416d0083e3 Mon Sep 17 00:00:00 2001 From: chenx97 Date: Thu, 21 Sep 2023 19:40:56 +0800 Subject: [PATCH 102/149] tidy: remove io-lifetimes from the dependency list --- src/tools/tidy/src/deps.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index daa9cb8d22ed5..c05349c8e8da7 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -180,7 +180,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "instant", "intl-memoizer", "intl_pluralrules", - "io-lifetimes", "is-terminal", "itertools", "itoa", From 6ce61d543acd54edfbd3dde5861e3a0bee752cd6 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 24 Sep 2023 14:56:40 +0200 Subject: [PATCH 103/149] add missing configuration tests --- .../clippy.toml | 1 + .../decimal_literal_representation.fixed | 6 ++++++ .../decimal_literal_representation.rs | 6 ++++++ .../decimal_literal_representation.stderr | 11 ++++++++++ .../disallowed_script_idents/clippy.toml | 1 + .../disallowed_script_idents.rs | 6 ++++++ .../disallowed_script_idents.stderr | 11 ++++++++++ tests/ui-toml/enum_variant_names/clippy.toml | 1 + .../enum_variant_names/enum_variant_names.rs | 16 ++++++++++++++ .../enum_variant_names.stderr | 18 ++++++++++++++++ tests/ui-toml/enum_variant_size/clippy.toml | 1 + .../enum_variant_size/enum_variant_size.fixed | 11 ++++++++++ .../enum_variant_size/enum_variant_size.rs | 11 ++++++++++ .../enum_variant_size.stderr | 21 +++++++++++++++++++ tests/ui-toml/explicit_iter_loop/clippy.toml | 1 + .../explicit_iter_loop.fixed | 10 +++++++++ .../explicit_iter_loop/explicit_iter_loop.rs | 10 +++++++++ .../explicit_iter_loop.stderr | 17 +++++++++++++++ tests/ui-toml/large_stack_frames/clippy.toml | 1 + .../large_stack_frames/large_stack_frames.rs | 17 +++++++++++++++ .../large_stack_frames.stderr | 15 +++++++++++++ .../large_types_passed_by_value/clippy.toml | 1 + .../large_types_passed_by_value.fixed | 7 +++++++ .../large_types_passed_by_value.rs | 7 +++++++ .../large_types_passed_by_value.stderr | 11 ++++++++++ tests/ui-toml/manual_let_else/clippy.toml | 1 + .../manual_let_else/manual_let_else.fixed | 10 +++++++++ .../manual_let_else/manual_let_else.rs | 14 +++++++++++++ .../manual_let_else/manual_let_else.stderr | 15 +++++++++++++ tests/ui-toml/path_ends_with_ext/clippy.toml | 1 + .../path_ends_with_ext/path_ends_with_ext.rs | 9 ++++++++ tests/ui-toml/result_large_err/clippy.toml | 1 + .../result_large_err/result_large_err.rs | 10 +++++++++ .../result_large_err/result_large_err.stderr | 12 +++++++++++ .../too_large_for_stack/boxed_local.rs | 5 +++++ .../too_large_for_stack/boxed_local.stderr | 11 ++++++++++ tests/ui-toml/too_large_for_stack/clippy.toml | 1 + .../too_large_for_stack/useless_vec.fixed | 9 ++++++++ .../too_large_for_stack/useless_vec.rs | 9 ++++++++ .../too_large_for_stack/useless_vec.stderr | 11 ++++++++++ tests/ui-toml/too_many_arguments/clippy.toml | 1 + .../too_many_arguments/too_many_arguments.rs | 7 +++++++ .../too_many_arguments.stderr | 11 ++++++++++ tests/ui-toml/type_complexity/clippy.toml | 1 + .../type_complexity/type_complexity.rs | 7 +++++++ .../type_complexity/type_complexity.stderr | 11 ++++++++++ .../type_repetition_in_bounds/clippy.toml | 1 + .../ui-toml/type_repetition_in_bounds/main.rs | 18 ++++++++++++++++ .../type_repetition_in_bounds/main.stderr | 12 +++++++++++ .../unnecessary_box_returns/clippy.toml | 1 + .../unnecessary_box_returns.fixed | 11 ++++++++++ .../unnecessary_box_returns.rs | 11 ++++++++++ .../unnecessary_box_returns.stderr | 12 +++++++++++ tests/ui-toml/verbose_bit_mask/clippy.toml | 1 + .../verbose_bit_mask/verbose_bit_mask.fixed | 7 +++++++ .../verbose_bit_mask/verbose_bit_mask.rs | 7 +++++++ .../verbose_bit_mask/verbose_bit_mask.stderr | 11 ++++++++++ tests/ui-toml/wildcard_imports/clippy.toml | 1 + .../wildcard_imports/wildcard_imports.fixed | 11 ++++++++++ .../wildcard_imports/wildcard_imports.rs | 11 ++++++++++ .../wildcard_imports/wildcard_imports.stderr | 11 ++++++++++ 61 files changed, 501 insertions(+) create mode 100644 tests/ui-toml/decimal_literal_representation/clippy.toml create mode 100644 tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed create mode 100644 tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs create mode 100644 tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr create mode 100644 tests/ui-toml/disallowed_script_idents/clippy.toml create mode 100644 tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs create mode 100644 tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr create mode 100644 tests/ui-toml/enum_variant_names/clippy.toml create mode 100644 tests/ui-toml/enum_variant_names/enum_variant_names.rs create mode 100644 tests/ui-toml/enum_variant_names/enum_variant_names.stderr create mode 100644 tests/ui-toml/enum_variant_size/clippy.toml create mode 100644 tests/ui-toml/enum_variant_size/enum_variant_size.fixed create mode 100644 tests/ui-toml/enum_variant_size/enum_variant_size.rs create mode 100644 tests/ui-toml/enum_variant_size/enum_variant_size.stderr create mode 100644 tests/ui-toml/explicit_iter_loop/clippy.toml create mode 100644 tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed create mode 100644 tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs create mode 100644 tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr create mode 100644 tests/ui-toml/large_stack_frames/clippy.toml create mode 100644 tests/ui-toml/large_stack_frames/large_stack_frames.rs create mode 100644 tests/ui-toml/large_stack_frames/large_stack_frames.stderr create mode 100644 tests/ui-toml/large_types_passed_by_value/clippy.toml create mode 100644 tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed create mode 100644 tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs create mode 100644 tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr create mode 100644 tests/ui-toml/manual_let_else/clippy.toml create mode 100644 tests/ui-toml/manual_let_else/manual_let_else.fixed create mode 100644 tests/ui-toml/manual_let_else/manual_let_else.rs create mode 100644 tests/ui-toml/manual_let_else/manual_let_else.stderr create mode 100644 tests/ui-toml/path_ends_with_ext/clippy.toml create mode 100644 tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs create mode 100644 tests/ui-toml/result_large_err/clippy.toml create mode 100644 tests/ui-toml/result_large_err/result_large_err.rs create mode 100644 tests/ui-toml/result_large_err/result_large_err.stderr create mode 100644 tests/ui-toml/too_large_for_stack/boxed_local.rs create mode 100644 tests/ui-toml/too_large_for_stack/boxed_local.stderr create mode 100644 tests/ui-toml/too_large_for_stack/clippy.toml create mode 100644 tests/ui-toml/too_large_for_stack/useless_vec.fixed create mode 100644 tests/ui-toml/too_large_for_stack/useless_vec.rs create mode 100644 tests/ui-toml/too_large_for_stack/useless_vec.stderr create mode 100644 tests/ui-toml/too_many_arguments/clippy.toml create mode 100644 tests/ui-toml/too_many_arguments/too_many_arguments.rs create mode 100644 tests/ui-toml/too_many_arguments/too_many_arguments.stderr create mode 100644 tests/ui-toml/type_complexity/clippy.toml create mode 100644 tests/ui-toml/type_complexity/type_complexity.rs create mode 100644 tests/ui-toml/type_complexity/type_complexity.stderr create mode 100644 tests/ui-toml/type_repetition_in_bounds/clippy.toml create mode 100644 tests/ui-toml/type_repetition_in_bounds/main.rs create mode 100644 tests/ui-toml/type_repetition_in_bounds/main.stderr create mode 100644 tests/ui-toml/unnecessary_box_returns/clippy.toml create mode 100644 tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed create mode 100644 tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs create mode 100644 tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr create mode 100644 tests/ui-toml/verbose_bit_mask/clippy.toml create mode 100644 tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed create mode 100644 tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs create mode 100644 tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr create mode 100644 tests/ui-toml/wildcard_imports/clippy.toml create mode 100644 tests/ui-toml/wildcard_imports/wildcard_imports.fixed create mode 100644 tests/ui-toml/wildcard_imports/wildcard_imports.rs create mode 100644 tests/ui-toml/wildcard_imports/wildcard_imports.stderr diff --git a/tests/ui-toml/decimal_literal_representation/clippy.toml b/tests/ui-toml/decimal_literal_representation/clippy.toml new file mode 100644 index 0000000000000..74fc5d249d005 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/clippy.toml @@ -0,0 +1 @@ +literal-representation-threshold = 0xFFFFFF diff --git a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed new file mode 100644 index 0000000000000..750f3be84c040 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed @@ -0,0 +1,6 @@ +#![warn(clippy::decimal_literal_representation)] +fn main() { + let _ = 8388608; + let _ = 0x00FF_FFFF; + //~^ ERROR: integer literal has a better hexadecimal representation +} diff --git a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs new file mode 100644 index 0000000000000..26b3354d15909 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs @@ -0,0 +1,6 @@ +#![warn(clippy::decimal_literal_representation)] +fn main() { + let _ = 8388608; + let _ = 16777215; + //~^ ERROR: integer literal has a better hexadecimal representation +} diff --git a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr new file mode 100644 index 0000000000000..6f817a3fdde42 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr @@ -0,0 +1,11 @@ +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:4:13 + | +LL | let _ = 16777215; + | ^^^^^^^^ help: consider: `0x00FF_FFFF` + | + = note: `-D clippy::decimal-literal-representation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::decimal_literal_representation)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/disallowed_script_idents/clippy.toml b/tests/ui-toml/disallowed_script_idents/clippy.toml new file mode 100644 index 0000000000000..26cb2d77bfd9f --- /dev/null +++ b/tests/ui-toml/disallowed_script_idents/clippy.toml @@ -0,0 +1 @@ +allowed-scripts = ["Cyrillic"] diff --git a/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs new file mode 100644 index 0000000000000..9df1ec6fab0d7 --- /dev/null +++ b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs @@ -0,0 +1,6 @@ +#![warn(clippy::disallowed_script_idents)] +fn main() { + let счётчик = 10; + let カウンタ = 10; + //~^ ERROR: identifier `カウンタ` has a Unicode script that is not allowed by configuration +} diff --git a/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr new file mode 100644 index 0000000000000..31bb5ee3514a5 --- /dev/null +++ b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr @@ -0,0 +1,11 @@ +error: identifier `カウンタ` has a Unicode script that is not allowed by configuration: Katakana + --> $DIR/disallowed_script_idents.rs:4:9 + | +LL | let カウンタ = 10; + | ^^^^^^^^ + | + = note: `-D clippy::disallowed-script-idents` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::disallowed_script_idents)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/enum_variant_names/clippy.toml b/tests/ui-toml/enum_variant_names/clippy.toml new file mode 100644 index 0000000000000..0ad7a97994849 --- /dev/null +++ b/tests/ui-toml/enum_variant_names/clippy.toml @@ -0,0 +1 @@ +enum-variant-name-threshold = 5 diff --git a/tests/ui-toml/enum_variant_names/enum_variant_names.rs b/tests/ui-toml/enum_variant_names/enum_variant_names.rs new file mode 100644 index 0000000000000..8f4e178ccfe17 --- /dev/null +++ b/tests/ui-toml/enum_variant_names/enum_variant_names.rs @@ -0,0 +1,16 @@ +enum Foo { + AFoo, + BFoo, + CFoo, + DFoo, +} +enum Foo2 { + //~^ ERROR: all variants have the same postfix + AFoo, + BFoo, + CFoo, + DFoo, + EFoo, +} + +fn main() {} diff --git a/tests/ui-toml/enum_variant_names/enum_variant_names.stderr b/tests/ui-toml/enum_variant_names/enum_variant_names.stderr new file mode 100644 index 0000000000000..11039b1db4877 --- /dev/null +++ b/tests/ui-toml/enum_variant_names/enum_variant_names.stderr @@ -0,0 +1,18 @@ +error: all variants have the same postfix: `Foo` + --> $DIR/enum_variant_names.rs:7:1 + | +LL | / enum Foo2 { +LL | | +LL | | AFoo, +LL | | BFoo, +... | +LL | | EFoo, +LL | | } + | |_^ + | + = help: remove the postfixes and use full paths to the variants instead of glob imports + = note: `-D clippy::enum-variant-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::enum_variant_names)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/enum_variant_size/clippy.toml b/tests/ui-toml/enum_variant_size/clippy.toml new file mode 100644 index 0000000000000..64a8017fe02e4 --- /dev/null +++ b/tests/ui-toml/enum_variant_size/clippy.toml @@ -0,0 +1 @@ +enum-variant-size-threshold = 500 diff --git a/tests/ui-toml/enum_variant_size/enum_variant_size.fixed b/tests/ui-toml/enum_variant_size/enum_variant_size.fixed new file mode 100644 index 0000000000000..9ae760ae41aea --- /dev/null +++ b/tests/ui-toml/enum_variant_size/enum_variant_size.fixed @@ -0,0 +1,11 @@ +enum Fine { + A(()), + B([u8; 500]), +} +enum Bad { + //~^ ERROR: large size difference between variants + A(()), + B(Box<[u8; 501]>), +} + +fn main() {} diff --git a/tests/ui-toml/enum_variant_size/enum_variant_size.rs b/tests/ui-toml/enum_variant_size/enum_variant_size.rs new file mode 100644 index 0000000000000..cf7f432bf0b14 --- /dev/null +++ b/tests/ui-toml/enum_variant_size/enum_variant_size.rs @@ -0,0 +1,11 @@ +enum Fine { + A(()), + B([u8; 500]), +} +enum Bad { + //~^ ERROR: large size difference between variants + A(()), + B([u8; 501]), +} + +fn main() {} diff --git a/tests/ui-toml/enum_variant_size/enum_variant_size.stderr b/tests/ui-toml/enum_variant_size/enum_variant_size.stderr new file mode 100644 index 0000000000000..4d9bc9d48e45a --- /dev/null +++ b/tests/ui-toml/enum_variant_size/enum_variant_size.stderr @@ -0,0 +1,21 @@ +error: large size difference between variants + --> $DIR/enum_variant_size.rs:5:1 + | +LL | / enum Bad { +LL | | +LL | | A(()), + | | ----- the second-largest variant contains at least 0 bytes +LL | | B([u8; 501]), + | | ------------ the largest variant contains at least 501 bytes +LL | | } + | |_^ the entire enum is at least 502 bytes + | + = note: `-D clippy::large-enum-variant` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]` +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[u8; 501]>), + | ~~~~~~~~~~~~~~ + +error: aborting due to previous error + diff --git a/tests/ui-toml/explicit_iter_loop/clippy.toml b/tests/ui-toml/explicit_iter_loop/clippy.toml new file mode 100644 index 0000000000000..15d175ef14785 --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/clippy.toml @@ -0,0 +1 @@ +enforce-iter-loop-reborrow = true diff --git a/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed new file mode 100644 index 0000000000000..468da22a926fe --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::explicit_iter_loop)] + +fn main() { + let mut vec = vec![1, 2, 3]; + let rmvec = &mut vec; + for _ in &*rmvec {} + //~^ ERROR: it is more concise to loop over references to containers + for _ in &mut *rmvec {} + //~^ ERROR: it is more concise to loop over references to containers +} diff --git a/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs new file mode 100644 index 0000000000000..a934648608c21 --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs @@ -0,0 +1,10 @@ +#![warn(clippy::explicit_iter_loop)] + +fn main() { + let mut vec = vec![1, 2, 3]; + let rmvec = &mut vec; + for _ in rmvec.iter() {} + //~^ ERROR: it is more concise to loop over references to containers + for _ in rmvec.iter_mut() {} + //~^ ERROR: it is more concise to loop over references to containers +} diff --git a/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr new file mode 100644 index 0000000000000..587d4f9b3f053 --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr @@ -0,0 +1,17 @@ +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/explicit_iter_loop.rs:6:14 + | +LL | for _ in rmvec.iter() {} + | ^^^^^^^^^^^^ help: to write this more concisely, try: `&*rmvec` + | + = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::explicit_iter_loop)]` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/explicit_iter_loop.rs:8:14 + | +LL | for _ in rmvec.iter_mut() {} + | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *rmvec` + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/large_stack_frames/clippy.toml b/tests/ui-toml/large_stack_frames/clippy.toml new file mode 100644 index 0000000000000..1b68801b97053 --- /dev/null +++ b/tests/ui-toml/large_stack_frames/clippy.toml @@ -0,0 +1 @@ +stack-size-threshold = 4294967296 diff --git a/tests/ui-toml/large_stack_frames/large_stack_frames.rs b/tests/ui-toml/large_stack_frames/large_stack_frames.rs new file mode 100644 index 0000000000000..39b3c571c5c91 --- /dev/null +++ b/tests/ui-toml/large_stack_frames/large_stack_frames.rs @@ -0,0 +1,17 @@ +#![warn(clippy::large_stack_frames)] + +// We use this helper function instead of writing [0; 4294967297] directly to represent a +// case that large_stack_arrays can't catch +fn create_array() -> [u8; N] { + [0; N] +} + +fn f() { + let _x = create_array::<4294967296>(); +} +fn f2() { + //~^ ERROR: this function allocates a large amount of stack space + let _x = create_array::<4294967297>(); +} + +fn main() {} diff --git a/tests/ui-toml/large_stack_frames/large_stack_frames.stderr b/tests/ui-toml/large_stack_frames/large_stack_frames.stderr new file mode 100644 index 0000000000000..ef7d15a3739a0 --- /dev/null +++ b/tests/ui-toml/large_stack_frames/large_stack_frames.stderr @@ -0,0 +1,15 @@ +error: this function allocates a large amount of stack space + --> $DIR/large_stack_frames.rs:12:1 + | +LL | / fn f2() { +LL | | +LL | | let _x = create_array::<4294967297>(); +LL | | } + | |_^ + | + = note: allocating large amounts of stack space can overflow the stack + = note: `-D clippy::large-stack-frames` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_stack_frames)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/large_types_passed_by_value/clippy.toml b/tests/ui-toml/large_types_passed_by_value/clippy.toml new file mode 100644 index 0000000000000..45bcbce1e3c53 --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/clippy.toml @@ -0,0 +1 @@ +pass-by-value-size-limit = 512 diff --git a/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed new file mode 100644 index 0000000000000..3c87c79cf2fcc --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed @@ -0,0 +1,7 @@ +#![warn(clippy::large_types_passed_by_value)] + +fn f(_v: [u8; 512]) {} +fn f2(_v: &[u8; 513]) {} +//~^ ERROR: this argument (513 byte) is passed by value + +fn main() {} diff --git a/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs new file mode 100644 index 0000000000000..0572373a61188 --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs @@ -0,0 +1,7 @@ +#![warn(clippy::large_types_passed_by_value)] + +fn f(_v: [u8; 512]) {} +fn f2(_v: [u8; 513]) {} +//~^ ERROR: this argument (513 byte) is passed by value + +fn main() {} diff --git a/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr new file mode 100644 index 0000000000000..6678a2b47214f --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr @@ -0,0 +1,11 @@ +error: this argument (513 byte) is passed by value, but might be more efficient if passed by reference (limit: 512 byte) + --> $DIR/large_types_passed_by_value.rs:4:11 + | +LL | fn f2(_v: [u8; 513]) {} + | ^^^^^^^^^ help: consider passing by reference instead: `&[u8; 513]` + | + = note: `-D clippy::large-types-passed-by-value` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_types_passed_by_value)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/manual_let_else/clippy.toml b/tests/ui-toml/manual_let_else/clippy.toml new file mode 100644 index 0000000000000..cdae1da011b0a --- /dev/null +++ b/tests/ui-toml/manual_let_else/clippy.toml @@ -0,0 +1 @@ +matches-for-let-else = "AllTypes" diff --git a/tests/ui-toml/manual_let_else/manual_let_else.fixed b/tests/ui-toml/manual_let_else/manual_let_else.fixed new file mode 100644 index 0000000000000..972f6aa40303d --- /dev/null +++ b/tests/ui-toml/manual_let_else/manual_let_else.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::manual_let_else)] + +enum Foo { + A(u8), + B, +} + +fn main() { + let Foo::A(x) = Foo::A(1) else { return }; +} diff --git a/tests/ui-toml/manual_let_else/manual_let_else.rs b/tests/ui-toml/manual_let_else/manual_let_else.rs new file mode 100644 index 0000000000000..fdaba4ad2a602 --- /dev/null +++ b/tests/ui-toml/manual_let_else/manual_let_else.rs @@ -0,0 +1,14 @@ +#![warn(clippy::manual_let_else)] + +enum Foo { + A(u8), + B, +} + +fn main() { + let x = match Foo::A(1) { + //~^ ERROR: this could be rewritten as `let...else` + Foo::A(x) => x, + Foo::B => return, + }; +} diff --git a/tests/ui-toml/manual_let_else/manual_let_else.stderr b/tests/ui-toml/manual_let_else/manual_let_else.stderr new file mode 100644 index 0000000000000..5c2c86c373189 --- /dev/null +++ b/tests/ui-toml/manual_let_else/manual_let_else.stderr @@ -0,0 +1,15 @@ +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:9:5 + | +LL | / let x = match Foo::A(1) { +LL | | +LL | | Foo::A(x) => x, +LL | | Foo::B => return, +LL | | }; + | |______^ help: consider writing: `let Foo::A(x) = Foo::A(1) else { return };` + | + = note: `-D clippy::manual-let-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_let_else)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/path_ends_with_ext/clippy.toml b/tests/ui-toml/path_ends_with_ext/clippy.toml new file mode 100644 index 0000000000000..40d7dfd938ce0 --- /dev/null +++ b/tests/ui-toml/path_ends_with_ext/clippy.toml @@ -0,0 +1 @@ +allowed-dotfiles = ["dot"] diff --git a/tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs b/tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs new file mode 100644 index 0000000000000..a34b15f4ac9ab --- /dev/null +++ b/tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs @@ -0,0 +1,9 @@ +#![warn(clippy::path_ends_with_ext)] + +use std::path::Path; + +fn f(p: &Path) { + p.ends_with(".dot"); +} + +fn main() {} diff --git a/tests/ui-toml/result_large_err/clippy.toml b/tests/ui-toml/result_large_err/clippy.toml new file mode 100644 index 0000000000000..df505ed9672a6 --- /dev/null +++ b/tests/ui-toml/result_large_err/clippy.toml @@ -0,0 +1 @@ +large-error-threshold = 512 diff --git a/tests/ui-toml/result_large_err/result_large_err.rs b/tests/ui-toml/result_large_err/result_large_err.rs new file mode 100644 index 0000000000000..dea4d61a96bfb --- /dev/null +++ b/tests/ui-toml/result_large_err/result_large_err.rs @@ -0,0 +1,10 @@ +#![warn(clippy::result_large_err)] + +fn f() -> Result<(), [u8; 511]> { + todo!() +} +fn f2() -> Result<(), [u8; 512]> { + //~^ ERROR: the `Err`-variant returned from this function is very large + todo!() +} +fn main() {} diff --git a/tests/ui-toml/result_large_err/result_large_err.stderr b/tests/ui-toml/result_large_err/result_large_err.stderr new file mode 100644 index 0000000000000..b0936319d1b9d --- /dev/null +++ b/tests/ui-toml/result_large_err/result_large_err.stderr @@ -0,0 +1,12 @@ +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:6:12 + | +LL | fn f2() -> Result<(), [u8; 512]> { + | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes + | + = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` + = note: `-D clippy::result-large-err` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::result_large_err)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/too_large_for_stack/boxed_local.rs b/tests/ui-toml/too_large_for_stack/boxed_local.rs new file mode 100644 index 0000000000000..2f02361220650 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/boxed_local.rs @@ -0,0 +1,5 @@ +fn f(x: Box<[u8; 500]>) {} +//~^ ERROR: local variable doesn't need to be boxed here +fn f2(x: Box<[u8; 501]>) {} + +fn main() {} diff --git a/tests/ui-toml/too_large_for_stack/boxed_local.stderr b/tests/ui-toml/too_large_for_stack/boxed_local.stderr new file mode 100644 index 0000000000000..2859a29f1b2a2 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/boxed_local.stderr @@ -0,0 +1,11 @@ +error: local variable doesn't need to be boxed here + --> $DIR/boxed_local.rs:1:6 + | +LL | fn f(x: Box<[u8; 500]>) {} + | ^ + | + = note: `-D clippy::boxed-local` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::boxed_local)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/too_large_for_stack/clippy.toml b/tests/ui-toml/too_large_for_stack/clippy.toml new file mode 100644 index 0000000000000..a9c42fca46807 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/clippy.toml @@ -0,0 +1 @@ +too-large-for-stack = 500 diff --git a/tests/ui-toml/too_large_for_stack/useless_vec.fixed b/tests/ui-toml/too_large_for_stack/useless_vec.fixed new file mode 100644 index 0000000000000..ebe92d9b59946 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/useless_vec.fixed @@ -0,0 +1,9 @@ +#![warn(clippy::useless_vec)] + +fn main() { + let x = [0u8; 500]; + //~^ ERROR: useless use of `vec!` + x.contains(&1); + let y = vec![0u8; 501]; + y.contains(&1); +} diff --git a/tests/ui-toml/too_large_for_stack/useless_vec.rs b/tests/ui-toml/too_large_for_stack/useless_vec.rs new file mode 100644 index 0000000000000..e2886a8ccd126 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/useless_vec.rs @@ -0,0 +1,9 @@ +#![warn(clippy::useless_vec)] + +fn main() { + let x = vec![0u8; 500]; + //~^ ERROR: useless use of `vec!` + x.contains(&1); + let y = vec![0u8; 501]; + y.contains(&1); +} diff --git a/tests/ui-toml/too_large_for_stack/useless_vec.stderr b/tests/ui-toml/too_large_for_stack/useless_vec.stderr new file mode 100644 index 0000000000000..923cded5eef18 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/useless_vec.stderr @@ -0,0 +1,11 @@ +error: useless use of `vec!` + --> $DIR/useless_vec.rs:4:13 + | +LL | let x = vec![0u8; 500]; + | ^^^^^^^^^^^^^^ help: you can use an array directly: `[0u8; 500]` + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/too_many_arguments/clippy.toml b/tests/ui-toml/too_many_arguments/clippy.toml new file mode 100644 index 0000000000000..15906305c891b --- /dev/null +++ b/tests/ui-toml/too_many_arguments/clippy.toml @@ -0,0 +1 @@ +too-many-arguments-threshold = 10 diff --git a/tests/ui-toml/too_many_arguments/too_many_arguments.rs b/tests/ui-toml/too_many_arguments/too_many_arguments.rs new file mode 100644 index 0000000000000..7b2d6897d3c2c --- /dev/null +++ b/tests/ui-toml/too_many_arguments/too_many_arguments.rs @@ -0,0 +1,7 @@ +#![warn(clippy::too_many_arguments)] + +fn not_too_many(p1: u8, p2: u8, p3: u8, p4: u8, p5: u8, p6: u8, p7: u8, p8: u8, p9: u8, p10: u8) {} +fn too_many(p1: u8, p2: u8, p3: u8, p4: u8, p5: u8, p6: u8, p7: u8, p8: u8, p9: u8, p10: u8, p11: u8) {} +//~^ ERROR: this function has too many arguments + +fn main() {} diff --git a/tests/ui-toml/too_many_arguments/too_many_arguments.stderr b/tests/ui-toml/too_many_arguments/too_many_arguments.stderr new file mode 100644 index 0000000000000..a52e1fcb9e3a8 --- /dev/null +++ b/tests/ui-toml/too_many_arguments/too_many_arguments.stderr @@ -0,0 +1,11 @@ +error: this function has too many arguments (11/10) + --> $DIR/too_many_arguments.rs:4:1 + | +LL | fn too_many(p1: u8, p2: u8, p3: u8, p4: u8, p5: u8, p6: u8, p7: u8, p8: u8, p9: u8, p10: u8, p11: u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::too-many-arguments` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::too_many_arguments)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/type_complexity/clippy.toml b/tests/ui-toml/type_complexity/clippy.toml new file mode 100644 index 0000000000000..bf2ffdd0e3071 --- /dev/null +++ b/tests/ui-toml/type_complexity/clippy.toml @@ -0,0 +1 @@ +type-complexity-threshold = 500 diff --git a/tests/ui-toml/type_complexity/type_complexity.rs b/tests/ui-toml/type_complexity/type_complexity.rs new file mode 100644 index 0000000000000..b95f5134347dc --- /dev/null +++ b/tests/ui-toml/type_complexity/type_complexity.rs @@ -0,0 +1,7 @@ +// 480 +fn f(_: (u8, (u8, (u8, (u8, (u8, (u8,))))))) {} +// 550 +fn f2(_: (u8, (u8, (u8, (u8, (u8, (u8, u8))))))) {} +//~^ ERROR: very complex type used + +fn main() {} diff --git a/tests/ui-toml/type_complexity/type_complexity.stderr b/tests/ui-toml/type_complexity/type_complexity.stderr new file mode 100644 index 0000000000000..8ca637f722251 --- /dev/null +++ b/tests/ui-toml/type_complexity/type_complexity.stderr @@ -0,0 +1,11 @@ +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:4:10 + | +LL | fn f2(_: (u8, (u8, (u8, (u8, (u8, (u8, u8))))))) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::type-complexity` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::type_complexity)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/type_repetition_in_bounds/clippy.toml b/tests/ui-toml/type_repetition_in_bounds/clippy.toml new file mode 100644 index 0000000000000..2f91866aa9367 --- /dev/null +++ b/tests/ui-toml/type_repetition_in_bounds/clippy.toml @@ -0,0 +1 @@ +max-trait-bounds = 5 diff --git a/tests/ui-toml/type_repetition_in_bounds/main.rs b/tests/ui-toml/type_repetition_in_bounds/main.rs new file mode 100644 index 0000000000000..2454c10382df7 --- /dev/null +++ b/tests/ui-toml/type_repetition_in_bounds/main.rs @@ -0,0 +1,18 @@ +#![warn(clippy::type_repetition_in_bounds)] + +fn f() +where + T: Copy + Clone + Sync + Send + ?Sized + Unpin, + T: PartialEq, +{ +} + +fn f2() +where + T: Copy + Clone + Sync + Send + ?Sized, + T: Unpin + PartialEq, + //~^ ERROR: this type has already been used as a bound predicate +{ +} + +fn main() {} diff --git a/tests/ui-toml/type_repetition_in_bounds/main.stderr b/tests/ui-toml/type_repetition_in_bounds/main.stderr new file mode 100644 index 0000000000000..2ae2984975f4b --- /dev/null +++ b/tests/ui-toml/type_repetition_in_bounds/main.stderr @@ -0,0 +1,12 @@ +error: this type has already been used as a bound predicate + --> $DIR/main.rs:13:5 + | +LL | T: Unpin + PartialEq, + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider combining the bounds: `T: Copy + Clone + Sync + Send + ?Sized + Unpin + PartialEq` + = note: `-D clippy::type-repetition-in-bounds` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::type_repetition_in_bounds)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/unnecessary_box_returns/clippy.toml b/tests/ui-toml/unnecessary_box_returns/clippy.toml new file mode 100644 index 0000000000000..7c3ffc2908f3d --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/clippy.toml @@ -0,0 +1 @@ +unnecessary-box-size = 64 diff --git a/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed new file mode 100644 index 0000000000000..413bc0bf1e378 --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed @@ -0,0 +1,11 @@ +#![warn(clippy::unnecessary_box_returns)] + +fn f() -> [u8; 64] { + //~^ ERROR: boxed return of the sized type `[u8; 64]` + todo!() +} +fn f2() -> Box<[u8; 65]> { + todo!() +} + +fn main() {} diff --git a/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs new file mode 100644 index 0000000000000..b44fbb5544856 --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs @@ -0,0 +1,11 @@ +#![warn(clippy::unnecessary_box_returns)] + +fn f() -> Box<[u8; 64]> { + //~^ ERROR: boxed return of the sized type `[u8; 64]` + todo!() +} +fn f2() -> Box<[u8; 65]> { + todo!() +} + +fn main() {} diff --git a/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr new file mode 100644 index 0000000000000..df9aa37ac10f4 --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr @@ -0,0 +1,12 @@ +error: boxed return of the sized type `[u8; 64]` + --> $DIR/unnecessary_box_returns.rs:3:11 + | +LL | fn f() -> Box<[u8; 64]> { + | ^^^^^^^^^^^^^ help: try: `[u8; 64]` + | + = help: changing this also requires a change to the return expressions in this function + = note: `-D clippy::unnecessary-box-returns` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_box_returns)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/verbose_bit_mask/clippy.toml b/tests/ui-toml/verbose_bit_mask/clippy.toml new file mode 100644 index 0000000000000..55a202eefb964 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/clippy.toml @@ -0,0 +1 @@ +verbose-bit-mask-threshold = 31 diff --git a/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed new file mode 100644 index 0000000000000..437692a4d78c4 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed @@ -0,0 +1,7 @@ +#![warn(clippy::verbose_bit_mask)] +fn main() { + let v: i32 = 0; + let _ = v & 0b11111 == 0; + let _ = v.trailing_zeros() >= 6; + //~^ ERROR: bit mask could be simplified +} diff --git a/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs new file mode 100644 index 0000000000000..ce102708055a5 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs @@ -0,0 +1,7 @@ +#![warn(clippy::verbose_bit_mask)] +fn main() { + let v: i32 = 0; + let _ = v & 0b11111 == 0; + let _ = v & 0b111111 == 0; + //~^ ERROR: bit mask could be simplified +} diff --git a/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr new file mode 100644 index 0000000000000..7377921b42ab1 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr @@ -0,0 +1,11 @@ +error: bit mask could be simplified with a call to `trailing_zeros` + --> $DIR/verbose_bit_mask.rs:5:13 + | +LL | let _ = v & 0b111111 == 0; + | ^^^^^^^^^^^^^^^^^ help: try: `v.trailing_zeros() >= 6` + | + = note: `-D clippy::verbose-bit-mask` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::verbose_bit_mask)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/wildcard_imports/clippy.toml b/tests/ui-toml/wildcard_imports/clippy.toml new file mode 100644 index 0000000000000..875aaeef6c935 --- /dev/null +++ b/tests/ui-toml/wildcard_imports/clippy.toml @@ -0,0 +1 @@ +warn-on-all-wildcard-imports = true diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed new file mode 100644 index 0000000000000..1752f48856c2b --- /dev/null +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed @@ -0,0 +1,11 @@ +#![warn(clippy::wildcard_imports)] + +mod prelude { + pub const FOO: u8 = 1; +} +use prelude::FOO; +//~^ ERROR: usage of wildcard import + +fn main() { + let _ = FOO; +} diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.rs b/tests/ui-toml/wildcard_imports/wildcard_imports.rs new file mode 100644 index 0000000000000..331c2c59c222f --- /dev/null +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.rs @@ -0,0 +1,11 @@ +#![warn(clippy::wildcard_imports)] + +mod prelude { + pub const FOO: u8 = 1; +} +use prelude::*; +//~^ ERROR: usage of wildcard import + +fn main() { + let _ = FOO; +} diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.stderr b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr new file mode 100644 index 0000000000000..13ec3a229ce91 --- /dev/null +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr @@ -0,0 +1,11 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:6:5 + | +LL | use prelude::*; + | ^^^^^^^^^^ help: try: `prelude::FOO` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]` + +error: aborting due to previous error + From 6e80db96ea933676ac9b36714e3af9aaa3eedc00 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 24 Sep 2023 16:29:26 +0200 Subject: [PATCH 104/149] use a smaller number in test that fits in usize for 32 bit --- tests/ui-toml/large_stack_frames/clippy.toml | 2 +- tests/ui-toml/large_stack_frames/large_stack_frames.rs | 4 ++-- tests/ui-toml/large_stack_frames/large_stack_frames.stderr | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui-toml/large_stack_frames/clippy.toml b/tests/ui-toml/large_stack_frames/clippy.toml index 1b68801b97053..584335dc28fb2 100644 --- a/tests/ui-toml/large_stack_frames/clippy.toml +++ b/tests/ui-toml/large_stack_frames/clippy.toml @@ -1 +1 @@ -stack-size-threshold = 4294967296 +stack-size-threshold = 1000 diff --git a/tests/ui-toml/large_stack_frames/large_stack_frames.rs b/tests/ui-toml/large_stack_frames/large_stack_frames.rs index 39b3c571c5c91..39798ffea4942 100644 --- a/tests/ui-toml/large_stack_frames/large_stack_frames.rs +++ b/tests/ui-toml/large_stack_frames/large_stack_frames.rs @@ -7,11 +7,11 @@ fn create_array() -> [u8; N] { } fn f() { - let _x = create_array::<4294967296>(); + let _x = create_array::<1000>(); } fn f2() { //~^ ERROR: this function allocates a large amount of stack space - let _x = create_array::<4294967297>(); + let _x = create_array::<1001>(); } fn main() {} diff --git a/tests/ui-toml/large_stack_frames/large_stack_frames.stderr b/tests/ui-toml/large_stack_frames/large_stack_frames.stderr index ef7d15a3739a0..67ee57ab672d4 100644 --- a/tests/ui-toml/large_stack_frames/large_stack_frames.stderr +++ b/tests/ui-toml/large_stack_frames/large_stack_frames.stderr @@ -3,7 +3,7 @@ error: this function allocates a large amount of stack space | LL | / fn f2() { LL | | -LL | | let _x = create_array::<4294967297>(); +LL | | let _x = create_array::<1001>(); LL | | } | |_^ | From c5fccb98ea8d853b7d332e077c3ed86401b160c7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 Sep 2023 14:15:48 +0200 Subject: [PATCH 105/149] work towards rejecting consts in patterns that do not implement PartialEq --- compiler/rustc_lint_defs/src/builtin.rs | 52 +++++++++++++++++++ compiler/rustc_mir_build/messages.ftl | 3 ++ compiler/rustc_mir_build/src/errors.rs | 6 +++ .../src/thir/pattern/const_to_pat.rs | 14 ++++- .../ui/consts/const_in_pattern/issue-65466.rs | 3 +- .../const_in_pattern/issue-65466.stderr | 23 ++++++++ ...rs => issue-72896-non-partial-eq-const.rs} | 3 +- .../issue-72896-non-partial-eq-const.stderr | 23 ++++++++ 8 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 tests/ui/consts/const_in_pattern/issue-65466.stderr rename tests/ui/match/{issue-72896.rs => issue-72896-non-partial-eq-const.rs} (78%) create mode 100644 tests/ui/match/issue-72896-non-partial-eq-const.stderr diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 1951db49e91ad..a749c52e2dd65 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2310,6 +2310,57 @@ declare_lint! { }; } +declare_lint! { + /// The `match_without_partial_eq` lint detects constants that are used in patterns, + /// whose type does not implement `PartialEq`. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(match_without_partial_eq)] + /// + /// trait EnumSetType { + /// type Repr; + /// } + /// + /// enum Enum8 { } + /// impl EnumSetType for Enum8 { + /// type Repr = u8; + /// } + /// + /// #[derive(PartialEq, Eq)] + /// struct EnumSet { + /// __enumset_underlying: T::Repr, + /// } + /// + /// const CONST_SET: EnumSet = EnumSet { __enumset_underlying: 3 }; + /// + /// fn main() { + /// match CONST_SET { + /// CONST_SET => { /* ok */ } + /// _ => panic!("match fell through?"), + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous versions of Rust accepted constants in patterns, even if those constants' types + /// did not have `PartialEq` implemented. The compiler falls back to comparing the value + /// field-by-field. In the future we'd like to ensure that pattern matching always + /// follows `PartialEq` semantics, so that trait bound will become a requirement for + /// matching on constants. + pub MATCH_WITHOUT_PARTIAL_EQ, + Warn, + "constant in pattern does not implement `PartialEq`", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, + reference: "issue #X ", + }; +} + declare_lint! { /// The `ambiguous_associated_items` lint detects ambiguity between /// [associated items] and [enum variants]. @@ -3389,6 +3440,7 @@ declare_lint_pass! { LOSSY_PROVENANCE_CASTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, MACRO_USE_EXTERN_CRATE, + MATCH_WITHOUT_PARTIAL_EQ, META_VARIABLE_MISUSE, MISSING_ABI, MISSING_FRAGMENT_SPECIFIER, diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 938f3edd31b05..ce021923f647f 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -229,6 +229,9 @@ mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type .suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown .help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern +mir_build_non_partial_eq_match = + to use a constant of type `{$non_peq_ty}` in a pattern, the type must implement `PartialEq` + mir_build_nontrivial_structural_match = to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 3ff3387a78114..bee5ac550ddce 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -748,6 +748,12 @@ pub struct NontrivialStructuralMatch<'tcx> { pub non_sm_ty: Ty<'tcx>, } +#[derive(LintDiagnostic)] +#[diag(mir_build_non_partial_eq_match)] +pub struct NonPartialEqMatch<'tcx> { + pub non_peq_ty: Ty<'tcx>, +} + #[derive(LintDiagnostic)] #[diag(mir_build_overlapping_range_endpoints)] #[note] diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 00d9fe72cfcb3..41f31beff778e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -16,8 +16,8 @@ use std::cell::Cell; use super::PatCtxt; use crate::errors::{ - FloatPattern, IndirectStructuralMatch, InvalidPattern, NontrivialStructuralMatch, - PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern, + FloatPattern, IndirectStructuralMatch, InvalidPattern, NonPartialEqMatch, + NontrivialStructuralMatch, PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern, }; impl<'a, 'tcx> PatCtxt<'a, 'tcx> { @@ -235,6 +235,16 @@ impl<'tcx> ConstToPat<'tcx> { PointerPattern, ); } + _ if !self.type_may_have_partial_eq_impl(cv.ty()) => { + // Value is structural-match but the type doesn't even implement `PartialEq`... + self.saw_const_match_lint.set(true); + self.tcx().emit_spanned_lint( + lint::builtin::MATCH_WITHOUT_PARTIAL_EQ, + self.id, + self.span, + NonPartialEqMatch { non_peq_ty: cv.ty() }, + ); + } _ => {} } } diff --git a/tests/ui/consts/const_in_pattern/issue-65466.rs b/tests/ui/consts/const_in_pattern/issue-65466.rs index 2b421f4c705ec..d45c32e170a6a 100644 --- a/tests/ui/consts/const_in_pattern/issue-65466.rs +++ b/tests/ui/consts/const_in_pattern/issue-65466.rs @@ -15,7 +15,8 @@ const C: &[O] = &[O::None]; fn main() { let x = O::None; match &[x][..] { - C => (), + C => (), //~WARN: the type must implement `PartialEq` + //~| previously accepted _ => (), } } diff --git a/tests/ui/consts/const_in_pattern/issue-65466.stderr b/tests/ui/consts/const_in_pattern/issue-65466.stderr new file mode 100644 index 0000000000000..89fddbf853d5f --- /dev/null +++ b/tests/ui/consts/const_in_pattern/issue-65466.stderr @@ -0,0 +1,23 @@ +warning: to use a constant of type `&[O]` in a pattern, the type must implement `PartialEq` + --> $DIR/issue-65466.rs:18:9 + | +LL | C => (), + | ^ + | + = 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 #X + = note: `#[warn(match_without_partial_eq)]` on by default + +warning: 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +warning: to use a constant of type `&[O]` in a pattern, the type must implement `PartialEq` + --> $DIR/issue-65466.rs:18:9 + | +LL | C => (), + | ^ + | + = 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 #X + = note: `#[warn(match_without_partial_eq)]` on by default + diff --git a/tests/ui/match/issue-72896.rs b/tests/ui/match/issue-72896-non-partial-eq-const.rs similarity index 78% rename from tests/ui/match/issue-72896.rs rename to tests/ui/match/issue-72896-non-partial-eq-const.rs index 3a8b82037310a..a3095f0be83c1 100644 --- a/tests/ui/match/issue-72896.rs +++ b/tests/ui/match/issue-72896-non-partial-eq-const.rs @@ -17,7 +17,8 @@ const CONST_SET: EnumSet = EnumSet { __enumset_underlying: 3 }; fn main() { match CONST_SET { - CONST_SET => { /* ok */ } + CONST_SET => { /* ok */ } //~WARN: must implement `PartialEq` + //~| previously accepted _ => panic!("match fell through?"), } } diff --git a/tests/ui/match/issue-72896-non-partial-eq-const.stderr b/tests/ui/match/issue-72896-non-partial-eq-const.stderr new file mode 100644 index 0000000000000..f32cf43953160 --- /dev/null +++ b/tests/ui/match/issue-72896-non-partial-eq-const.stderr @@ -0,0 +1,23 @@ +warning: to use a constant of type `EnumSet` in a pattern, the type must implement `PartialEq` + --> $DIR/issue-72896-non-partial-eq-const.rs:20:9 + | +LL | CONST_SET => { /* ok */ } + | ^^^^^^^^^ + | + = 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 #X + = note: `#[warn(match_without_partial_eq)]` on by default + +warning: 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +warning: to use a constant of type `EnumSet` in a pattern, the type must implement `PartialEq` + --> $DIR/issue-72896-non-partial-eq-const.rs:20:9 + | +LL | CONST_SET => { /* ok */ } + | ^^^^^^^^^ + | + = 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 #X + = note: `#[warn(match_without_partial_eq)]` on by default + From c3ed0c454eec4bb7fcf5b73f9d7584390fb692eb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 17 Sep 2023 11:04:26 +0200 Subject: [PATCH 106/149] make sure we always emit the no-PartialEq lint, even if there were other lints --- .../src/thir/pattern/const_to_pat.rs | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 41f31beff778e..a390691c6756c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -155,8 +155,9 @@ impl<'tcx> ConstToPat<'tcx> { }; if !self.saw_const_match_error.get() { - // If we were able to successfully convert the const to some pat, - // double-check that all types in the const implement `Structural`. + // If we were able to successfully convert the const to some pat (possibly with some + // lints, but no errors), double-check that all types in the const implement + // `Structural` and `PartialEq`. let structural = traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty()); @@ -192,8 +193,10 @@ impl<'tcx> ConstToPat<'tcx> { } else { let err = InvalidPattern { span: self.span, non_sm_ty }; self.tcx().sess.emit_err(err); - return Box::new(Pat { span: self.span, ty: cv.ty(), kind: PatKind::Wild }); } + // All branches above emitted an error. Don't print any more lints. + // The pattern we return is irrelevant since we errored. + return Box::new(Pat { span: self.span, ty: cv.ty(), kind: PatKind::Wild }); } else if !self.saw_const_match_lint.get() { if let Some(mir_structural_match_violation) = mir_structural_match_violation { match non_sm_ty.kind() { @@ -235,19 +238,20 @@ impl<'tcx> ConstToPat<'tcx> { PointerPattern, ); } - _ if !self.type_may_have_partial_eq_impl(cv.ty()) => { - // Value is structural-match but the type doesn't even implement `PartialEq`... - self.saw_const_match_lint.set(true); - self.tcx().emit_spanned_lint( - lint::builtin::MATCH_WITHOUT_PARTIAL_EQ, - self.id, - self.span, - NonPartialEqMatch { non_peq_ty: cv.ty() }, - ); - } _ => {} } } + + // Always check for `PartialEq`, even if we emitted other lints. (But not if there were + // any errors.) This ensures it shows up in cargo's future-compat reports as well. + if !self.type_may_have_partial_eq_impl(cv.ty()) { + self.tcx().emit_spanned_lint( + lint::builtin::MATCH_WITHOUT_PARTIAL_EQ, + self.id, + self.span, + NonPartialEqMatch { non_peq_ty: cv.ty() }, + ); + } } inlined_const_as_pat From 5aba8739d7e57a798a84fe27986d9025d8c8c967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Fri, 22 Sep 2023 23:19:39 +0200 Subject: [PATCH 107/149] Add assembly test to make sure that inlining works as expected when closures inherit target features --- .../closure-inherit-target-feature.rs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 tests/assembly/closure-inherit-target-feature.rs diff --git a/tests/assembly/closure-inherit-target-feature.rs b/tests/assembly/closure-inherit-target-feature.rs new file mode 100644 index 0000000000000..65728a1551667 --- /dev/null +++ b/tests/assembly/closure-inherit-target-feature.rs @@ -0,0 +1,58 @@ +// only-x86_64 +// assembly-output: emit-asm +// make sure the feature is not enabled at compile-time +// compile-flags: -C opt-level=3 -C target-feature=-sse4.1 -C llvm-args=-x86-asm-syntax=intel + +#![feature(target_feature_11)] +#![crate_type = "rlib"] + +use std::arch::x86_64::{__m128, _mm_blend_ps}; + +#[no_mangle] +pub unsafe fn sse41_blend_nofeature(x: __m128, y: __m128) -> __m128 { + let f = { + // check that _mm_blend_ps is not being inlined into the closure + // CHECK-LABEL: {{sse41_blend_nofeature.*closure.*:}} + // CHECK-NOT: blendps + // CHECK: {{call .*_mm_blend_ps.*}} + // CHECK-NOT: blendps + // CHECK: ret + #[inline(never)] |x, y| _mm_blend_ps(x, y, 0b0101) + }; + f(x, y) +} + +#[target_feature(enable = "sse4.1")] +pub fn sse41_blend_noinline(x: __m128, y: __m128) -> __m128 { + let f = { + // check that _mm_blend_ps is being inlined into the closure + // CHECK-LABEL: {{sse41_blend_noinline.*closure.*:}} + // CHECK-NOT: _mm_blend_ps + // CHECK: blendps + // CHECK-NOT: _mm_blend_ps + // CHECK: ret + #[inline(never)] |x, y| unsafe { + _mm_blend_ps(x, y, 0b0101) + } + }; + f(x, y) +} + +#[no_mangle] +#[target_feature(enable = "sse4.1")] +pub fn sse41_blend_doinline(x: __m128, y: __m128) -> __m128 { + // check that the closure and _mm_blend_ps are being inlined into the function + // CHECK-LABEL: sse41_blend_doinline: + // CHECK-NOT: {{sse41_blend_doinline.*closure.*}} + // CHECK-NOT: _mm_blend_ps + // CHECK: blendps + // CHECK-NOT: {{sse41_blend_doinline.*closure.*}} + // CHECK-NOT: _mm_blend_ps + // CHECK: ret + let f = { + #[inline] |x, y| unsafe { + _mm_blend_ps(x, y, 0b0101) + } + }; + f(x, y) +} From b589976606c7f03b2000bed41ddf6149d96c587b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 24 Sep 2023 16:37:19 +0200 Subject: [PATCH 108/149] use a must_hold variant for checking PartialEq --- .../src/thir/pattern/const_to_pat.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index a390691c6756c..ed20ed4a84f0b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -179,7 +179,7 @@ impl<'tcx> ConstToPat<'tcx> { } if let Some(non_sm_ty) = structural { - if !self.type_may_have_partial_eq_impl(cv.ty()) { + if !self.type_has_partial_eq_impl(cv.ty()) { if let ty::Adt(def, ..) = non_sm_ty.kind() { if def.is_union() { let err = UnionPattern { span: self.span }; @@ -244,7 +244,7 @@ impl<'tcx> ConstToPat<'tcx> { // Always check for `PartialEq`, even if we emitted other lints. (But not if there were // any errors.) This ensures it shows up in cargo's future-compat reports as well. - if !self.type_may_have_partial_eq_impl(cv.ty()) { + if !self.type_has_partial_eq_impl(cv.ty()) { self.tcx().emit_spanned_lint( lint::builtin::MATCH_WITHOUT_PARTIAL_EQ, self.id, @@ -258,7 +258,7 @@ impl<'tcx> ConstToPat<'tcx> { } #[instrument(level = "trace", skip(self), ret)] - fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool { + fn type_has_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool { // double-check there even *is* a semantic `PartialEq` to dispatch to. // // (If there isn't, then we can safely issue a hard @@ -273,8 +273,13 @@ impl<'tcx> ConstToPat<'tcx> { ty::TraitRef::new(self.tcx(), partial_eq_trait_id, [ty, ty]), ); - // FIXME: should this call a `predicate_must_hold` variant instead? - self.infcx.predicate_may_hold(&partial_eq_obligation) + // This *could* accept a type that isn't actually `PartialEq`, because region bounds get + // ignored. However that should be pretty much impossible since consts that do not depend on + // generics can only mention the `'static` lifetime, and how would one have a type that's + // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem + // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck + // can ensure that the type really implements `PartialEq`. + self.infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation) } fn field_pats( From b3e262acfd711f27c7450379d85092ec169388a7 Mon Sep 17 00:00:00 2001 From: jonboh Date: Sun, 24 Sep 2023 18:25:20 +0200 Subject: [PATCH 109/149] add ui-toml test --- tests/ui-toml/enum_variants_threshold0/clippy.toml | 1 + .../enum_variants_threshold0/enum_variants_name_threshold.rs | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 tests/ui-toml/enum_variants_threshold0/clippy.toml create mode 100644 tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs diff --git a/tests/ui-toml/enum_variants_threshold0/clippy.toml b/tests/ui-toml/enum_variants_threshold0/clippy.toml new file mode 100644 index 0000000000000..f85aade6ae87d --- /dev/null +++ b/tests/ui-toml/enum_variants_threshold0/clippy.toml @@ -0,0 +1 @@ +enum-variant-name-threshold = 0 diff --git a/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs b/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs new file mode 100644 index 0000000000000..6918d7528c160 --- /dev/null +++ b/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs @@ -0,0 +1,3 @@ +enum Actions {} + +fn main() {} From 3409ca65d835b858d9cefbe8949552f0f5ad0788 Mon Sep 17 00:00:00 2001 From: Florian Schmiderer Date: Sun, 17 Sep 2023 14:40:22 +0200 Subject: [PATCH 110/149] Add OwnedTargetMachine to manage llvm:TargetMachine. Uses pointers instead of &'static mut and provides safe interface to create/dispose it. --- .../src/back/owned_target_machine.rs | 101 ++++++++++++++++++ compiler/rustc_codegen_llvm/src/back/write.rs | 56 +++++----- compiler/rustc_codegen_llvm/src/context.rs | 4 +- compiler/rustc_codegen_llvm/src/lib.rs | 9 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 6 +- compiler/rustc_codegen_llvm/src/llvm_util.rs | 6 +- .../rustc_data_structures/src/small_c_str.rs | 6 ++ 7 files changed, 148 insertions(+), 40 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs new file mode 100644 index 0000000000000..707af1046bdf3 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -0,0 +1,101 @@ +use std::{ + ffi::{c_char, CStr}, + marker::PhantomData, + ops::Deref, + ptr::NonNull, +}; + +use rustc_data_structures::small_c_str::SmallCStr; + +use crate::{errors::LlvmError, llvm}; + +/// Responsible for safely creating and disposing llvm::TargetMachine via ffi functions. +/// Not cloneable as there is no clone function for llvm::TargetMachine. +#[repr(transparent)] +pub struct OwnedTargetMachine { + tm_unique: NonNull, + phantom: PhantomData, +} + +impl OwnedTargetMachine { + pub fn new( + triple: &CStr, + cpu: &CStr, + features: &CStr, + abi: &CStr, + model: llvm::CodeModel, + reloc: llvm::RelocModel, + level: llvm::CodeGenOptLevel, + use_soft_fp: bool, + function_sections: bool, + data_sections: bool, + unique_section_names: bool, + trap_unreachable: bool, + singletree: bool, + asm_comments: bool, + emit_stack_size_section: bool, + relax_elf_relocations: bool, + use_init_array: bool, + split_dwarf_file: &CStr, + debug_info_compression: &CStr, + force_emulated_tls: bool, + args_cstr_buff: &[u8], + ) -> Result> { + assert!(args_cstr_buff.len() > 0); + assert!( + *args_cstr_buff.last().unwrap() == 0, + "The last character must be a null terminator." + ); + + // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data + let tm_ptr = unsafe { + llvm::LLVMRustCreateTargetMachine( + triple.as_ptr(), + cpu.as_ptr(), + features.as_ptr(), + abi.as_ptr(), + model, + reloc, + level, + use_soft_fp, + function_sections, + data_sections, + unique_section_names, + trap_unreachable, + singletree, + asm_comments, + emit_stack_size_section, + relax_elf_relocations, + use_init_array, + split_dwarf_file.as_ptr(), + debug_info_compression.as_ptr(), + force_emulated_tls, + args_cstr_buff.as_ptr() as *const c_char, + args_cstr_buff.len(), + ) + }; + + NonNull::new(tm_ptr) + .map(|tm_unique| Self { tm_unique, phantom: PhantomData }) + .ok_or_else(|| LlvmError::CreateTargetMachine { triple: SmallCStr::from(triple) }) + } +} + +impl Deref for OwnedTargetMachine { + type Target = llvm::TargetMachine; + + fn deref(&self) -> &Self::Target { + // SAFETY: constructing ensures we have a valid pointer created by llvm::LLVMRustCreateTargetMachine + unsafe { self.tm_unique.as_ref() } + } +} + +impl Drop for OwnedTargetMachine { + fn drop(&mut self) { + // SAFETY: constructing ensures we have a valid pointer created by llvm::LLVMRustCreateTargetMachine + // OwnedTargetMachine is not copyable so there is no double free or use after free + unsafe { + llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_mut()); + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 1f394a7335c7e..9fb20c02c6bef 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -1,4 +1,5 @@ use crate::back::lto::ThinBuffer; +use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ selfprofile_after_pass_callback, selfprofile_before_pass_callback, LlvmSelfProfiler, }; @@ -98,7 +99,7 @@ pub fn write_output_file<'ll>( } } -pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm::TargetMachine { +pub fn create_informational_target_machine(sess: &Session) -> OwnedTargetMachine { let config = TargetMachineFactoryConfig { split_dwarf_file: None }; // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. @@ -107,7 +108,7 @@ pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm: .unwrap_or_else(|err| llvm_err(sess.diagnostic(), err).raise()) } -pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut llvm::TargetMachine { +pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTargetMachine { let split_dwarf_file = if tcx.sess.target_can_use_split_dwarf() { tcx.output_filenames(()).split_dwarf_path( tcx.sess.split_debuginfo(), @@ -259,34 +260,29 @@ pub fn target_machine_factory( path_mapping.map_prefix(config.split_dwarf_file.unwrap_or_default()).0; let split_dwarf_file = CString::new(split_dwarf_file.to_str().unwrap()).unwrap(); - let tm = unsafe { - llvm::LLVMRustCreateTargetMachine( - triple.as_ptr(), - cpu.as_ptr(), - features.as_ptr(), - abi.as_ptr(), - code_model, - reloc_model, - opt_level, - use_softfp, - ffunction_sections, - fdata_sections, - funique_section_names, - trap_unreachable, - singlethread, - asm_comments, - emit_stack_size_section, - relax_elf_relocations, - use_init_array, - split_dwarf_file.as_ptr(), - debuginfo_compression.as_ptr(), - force_emulated_tls, - args_cstr_buff.as_ptr() as *const c_char, - args_cstr_buff.len(), - ) - }; - - tm.ok_or_else(|| LlvmError::CreateTargetMachine { triple: triple.clone() }) + OwnedTargetMachine::new( + &triple, + &cpu, + &features, + &abi, + code_model, + reloc_model, + opt_level, + use_softfp, + ffunction_sections, + fdata_sections, + funique_section_names, + trap_unreachable, + singlethread, + asm_comments, + emit_stack_size_section, + relax_elf_relocations, + use_init_array, + &split_dwarf_file, + &debuginfo_compression, + force_emulated_tls, + &args_cstr_buff, + ) }) } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 8e8290279abd2..b4b2ab1e1f8a9 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -160,9 +160,9 @@ pub unsafe fn create_module<'ll>( // Ensure the data-layout values hardcoded remain the defaults. if sess.target.is_builtin { + // tm is disposed by its drop impl let tm = crate::back::write::create_informational_target_machine(tcx.sess); - llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm); - llvm::LLVMRustDisposeTargetMachine(tm); + llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, &tm); let llvm_data_layout = llvm::LLVMGetDataLayoutStr(llmod); let llvm_data_layout = str::from_utf8(CStr::from_ptr(llvm_data_layout).to_bytes()) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index fe87446f5c33a..59d1ea05d8a16 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -22,6 +22,7 @@ extern crate rustc_macros; #[macro_use] extern crate tracing; +use back::owned_target_machine::OwnedTargetMachine; use back::write::{create_informational_target_machine, create_target_machine}; use errors::ParseTargetMachineConfig; @@ -52,6 +53,7 @@ use std::io::Write; mod back { pub mod archive; pub mod lto; + pub mod owned_target_machine; mod profiling; pub mod write; } @@ -162,7 +164,7 @@ impl ExtraBackendMethods for LlvmCodegenBackend { impl WriteBackendMethods for LlvmCodegenBackend { type Module = ModuleLlvm; type ModuleBuffer = back::lto::ModuleBuffer; - type TargetMachine = &'static mut llvm::TargetMachine; + type TargetMachine = OwnedTargetMachine; type TargetMachineError = crate::errors::LlvmError<'static>; type ThinData = back::lto::ThinData; type ThinBuffer = back::lto::ThinBuffer; @@ -401,7 +403,9 @@ impl CodegenBackend for LlvmCodegenBackend { pub struct ModuleLlvm { llcx: &'static mut llvm::Context, llmod_raw: *const llvm::Module, - tm: &'static mut llvm::TargetMachine, + + // independent from llcx and llmod_raw, resources get disposed by drop impl + tm: OwnedTargetMachine, } unsafe impl Send for ModuleLlvm {} @@ -453,7 +457,6 @@ impl ModuleLlvm { impl Drop for ModuleLlvm { fn drop(&mut self) { unsafe { - llvm::LLVMRustDisposeTargetMachine(&mut *(self.tm as *mut _)); llvm::LLVMContextDispose(&mut *(self.llcx as *mut _)); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 2ebfdae39e805..36303b08107c8 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2112,6 +2112,8 @@ extern "C" { ); pub fn LLVMRustGetHostCPUName(len: *mut usize) -> *const c_char; + + // This function makes copies of pointed to data, so the data's lifetime may end after this function returns pub fn LLVMRustCreateTargetMachine( Triple: *const c_char, CPU: *const c_char, @@ -2135,9 +2137,9 @@ extern "C" { ForceEmulatedTls: bool, ArgsCstrBuff: *const c_char, ArgsCstrBuffLen: usize, - ) -> Option<&'static mut TargetMachine>; + ) -> *mut TargetMachine; - pub fn LLVMRustDisposeTargetMachine(T: &'static mut TargetMachine); + pub fn LLVMRustDisposeTargetMachine(T: *mut TargetMachine); pub fn LLVMRustAddLibraryInfo<'a>( PM: &PassManager<'a>, M: &'a Module, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index a76c9c9b7356c..7c8ef67ffd10e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -303,7 +303,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { // check that all features in a given smallvec are enabled for llvm_feature in to_llvm_features(sess, feature) { let cstr = SmallCStr::new(llvm_feature); - if !unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } { + if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } { return false; } } @@ -422,14 +422,14 @@ pub(crate) fn print(req: &PrintRequest, mut out: &mut dyn PrintBackendInfo, sess } unsafe { llvm::LLVMRustPrintTargetCPUs( - tm, + &tm, cpu_cstring.as_ptr(), callback, &mut out as *mut &mut dyn PrintBackendInfo as *mut c_void, ); } } - PrintKind::TargetFeatures => print_target_features(out, sess, tm), + PrintKind::TargetFeatures => print_target_features(out, sess, &tm), _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), } } diff --git a/compiler/rustc_data_structures/src/small_c_str.rs b/compiler/rustc_data_structures/src/small_c_str.rs index 719e4e3d97443..349fd7f9769d9 100644 --- a/compiler/rustc_data_structures/src/small_c_str.rs +++ b/compiler/rustc_data_structures/src/small_c_str.rs @@ -79,3 +79,9 @@ impl<'a> FromIterator<&'a str> for SmallCStr { Self { data } } } + +impl From<&ffi::CStr> for SmallCStr { + fn from(s: &ffi::CStr) -> Self { + Self { data: SmallVec::from_slice(s.to_bytes()) } + } +} From 0433e458dadc927e645f6307c0253a4b241dfd5b Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 24 Sep 2023 21:37:56 +0200 Subject: [PATCH 111/149] use first instead of get(0) Co-authored-by: Timo <30553356+y21@users.noreply.github.com> --- clippy_lints/src/enum_variants.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 85334d09d4b4a..e332f681b6dbd 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -167,7 +167,7 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n return; } - let first = match &def.variants.get(0) { + let first = match def.variants.first() { Some(variant) => variant.ident.name.as_str(), None => return, }; From 285e574b8ebb10ec329210dbcb1782433555d420 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 24 Sep 2023 20:05:27 +0000 Subject: [PATCH 112/149] Fix `.to_string()` of `proc_macro::Literal` (properly print cstr literals) --- library/proc_macro/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index d382fec93521a..f08a345db894d 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -1411,7 +1411,15 @@ impl Literal { let hashes = get_hashes_str(n); f(&["br", hashes, "\"", symbol, "\"", hashes, suffix]) } - _ => f(&[symbol, suffix]), + bridge::LitKind::CStr => f(&["c\"", symbol, "\"", suffix]), + bridge::LitKind::CStrRaw(n) => { + let hashes = get_hashes_str(n); + f(&["cr", hashes, "\"", symbol, "\"", hashes, suffix]) + } + + bridge::LitKind::Integer | bridge::LitKind::Float | bridge::LitKind::Err => { + f(&[symbol, suffix]) + } }) } } From 99a2fa17e684d4f3f625355561e0e8f9a101cc11 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 24 Sep 2023 20:24:33 +0000 Subject: [PATCH 113/149] Add a test for printing literals via `proc-macro` --- tests/ui/proc-macro/auxiliary/print-tokens.rs | 16 +++ tests/ui/proc-macro/literal-to-string.rs | 26 +++++ tests/ui/proc-macro/literal-to-string.stdout | 107 ++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 tests/ui/proc-macro/auxiliary/print-tokens.rs create mode 100644 tests/ui/proc-macro/literal-to-string.rs create mode 100644 tests/ui/proc-macro/literal-to-string.stdout diff --git a/tests/ui/proc-macro/auxiliary/print-tokens.rs b/tests/ui/proc-macro/auxiliary/print-tokens.rs new file mode 100644 index 0000000000000..3a5767edb153d --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/print-tokens.rs @@ -0,0 +1,16 @@ +// force-host +// no-prefer-dynamic +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree}; + +#[proc_macro] +pub fn print_tokens(input: TokenStream) -> TokenStream { + println!("{:#?}", input); + for token in input { + println!("{token}"); + } + TokenStream::new() +} diff --git a/tests/ui/proc-macro/literal-to-string.rs b/tests/ui/proc-macro/literal-to-string.rs new file mode 100644 index 0000000000000..494d17cbeeaff --- /dev/null +++ b/tests/ui/proc-macro/literal-to-string.rs @@ -0,0 +1,26 @@ +// check-pass +// edition: 2021 +#![feature(c_str_literals)] + +// aux-build: print-tokens.rs +extern crate print_tokens; + +fn main() { + print_tokens::print_tokens! { + 1 + 17u8 + 42. + 3.14f32 + b'a' + b'\xFF' + 'c' + '\x32' + "\"str\"" + r#""raw" str"# + r###"very ##"raw"## str"### + b"\"byte\" str" + br#""raw" "byte" str"# + c"\"c\" str" + cr#""raw" "c" str"# + } +} diff --git a/tests/ui/proc-macro/literal-to-string.stdout b/tests/ui/proc-macro/literal-to-string.stdout new file mode 100644 index 0000000000000..7b27fcf798b12 --- /dev/null +++ b/tests/ui/proc-macro/literal-to-string.stdout @@ -0,0 +1,107 @@ +TokenStream [ + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: #0 bytes(172..173), + }, + Literal { + kind: Integer, + symbol: "17", + suffix: Some("u8"), + span: #0 bytes(182..186), + }, + Literal { + kind: Float, + symbol: "42.", + suffix: None, + span: #0 bytes(195..198), + }, + Literal { + kind: Float, + symbol: "3.14", + suffix: Some("f32"), + span: #0 bytes(207..214), + }, + Literal { + kind: Byte, + symbol: "a", + suffix: None, + span: #0 bytes(223..227), + }, + Literal { + kind: Byte, + symbol: "\xFF", + suffix: None, + span: #0 bytes(236..243), + }, + Literal { + kind: Char, + symbol: "c", + suffix: None, + span: #0 bytes(252..255), + }, + Literal { + kind: Char, + symbol: "\x32", + suffix: None, + span: #0 bytes(264..270), + }, + Literal { + kind: Str, + symbol: "\\"str\\"", + suffix: None, + span: #0 bytes(279..288), + }, + Literal { + kind: StrRaw(1), + symbol: "\"raw\" str", + suffix: None, + span: #0 bytes(297..311), + }, + Literal { + kind: StrRaw(3), + symbol: "very ##\"raw\"## str", + suffix: None, + span: #0 bytes(320..347), + }, + Literal { + kind: ByteStr, + symbol: "\\"byte\\" str", + suffix: None, + span: #0 bytes(356..371), + }, + Literal { + kind: ByteStrRaw(1), + symbol: "\"raw\" \"byte\" str", + suffix: None, + span: #0 bytes(380..402), + }, + Literal { + kind: CStr, + symbol: "\\"c\\" str", + suffix: None, + span: #0 bytes(411..423), + }, + Literal { + kind: CStrRaw(1), + symbol: "\"raw\" \"c\" str", + suffix: None, + span: #0 bytes(432..451), + }, +] +1 +17u8 +42. +3.14f32 +b'a' +b'\xFF' +'c' +'\x32' +"\"str\"" +r#""raw" str"# +r###"very ##"raw"## str"### +b"\"byte\" str" +br#""raw" "byte" str"# +c"\"c\" str" +cr#""raw" "c" str"# From 4f86c69184adc53051a97c658ba9f691665799f5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 24 Sep 2023 23:30:09 +0200 Subject: [PATCH 114/149] fix ICE due to empty span and empty suggestions --- compiler/rustc_resolve/src/diagnostics.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 9b7fd76d10310..907a6b1c46c63 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2596,7 +2596,9 @@ fn show_candidates( ); if let [first, .., last] = &path[..] { let sp = first.ident.span.until(last.ident.span); - if sp.can_be_used_for_suggestions() { + // Our suggestion is empty, so make sure the span is not empty (or we'd ICE). + // Can happen for derive-generated spans. + if sp.can_be_used_for_suggestions() && !sp.is_empty() { err.span_suggestion_verbose( sp, format!("if you import `{}`, refer to it directly", last.ident), From ad509633a261999ba9d73aff908d48da77e0245f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 24 Sep 2023 23:38:07 +0200 Subject: [PATCH 115/149] ConstParamTy: require Eq --- .../src/deriving/bounds.rs | 2 +- library/core/src/marker.rs | 7 +++- .../const_param_ty_impl_no_structural_eq.rs | 2 ++ ...onst_param_ty_impl_no_structural_eq.stderr | 35 +++++++++++++++++-- 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index 2c8e6f99c6739..8027ca2e7bb47 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -41,7 +41,7 @@ pub fn expand_deriving_const_param_ty( path: path_std!(marker::ConstParamTy), skip_path_as_bound: false, needs_copy_as_bound_if_packed: false, - additional_bounds: Vec::new(), + additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))], supports_unions: false, methods: Vec::new(), associated_types: Vec::new(), diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 5ec751e5168bf..5ed82e26a0ae7 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -986,11 +986,16 @@ pub trait Tuple {} pub trait PointerLike {} /// A marker for types which can be used as types of `const` generic parameters. +/// +/// These types must have a proper equivalence relation (`Eq`) and it must be automatically +/// derived (`StructuralPartialEq`). There's a hard-coded check in the compiler ensuring +/// that all fields are also `ConstParamTy`, which implies that recursively, all fields +/// are `StructuralPartialEq`. #[lang = "const_param_ty"] #[unstable(feature = "adt_const_params", issue = "95174")] #[rustc_on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] #[allow(multiple_supertrait_upcastable)] -pub trait ConstParamTy: StructuralEq + StructuralPartialEq {} +pub trait ConstParamTy: StructuralEq + StructuralPartialEq + Eq {} /// Derive macro generating an impl of the trait `ConstParamTy`. #[rustc_builtin_macro] diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs index 08f7c5cb5423e..7174d1ec4f206 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs @@ -10,10 +10,12 @@ struct CantParam(ImplementsConstParamTy); impl std::marker::ConstParamTy for CantParam {} //~^ error: the type `CantParam` does not `#[derive(Eq)]` //~| error: the type `CantParam` does not `#[derive(PartialEq)]` +//~| the trait bound `CantParam: Eq` is not satisfied #[derive(std::marker::ConstParamTy)] //~^ error: the type `CantParamDerive` does not `#[derive(Eq)]` //~| error: the type `CantParamDerive` does not `#[derive(PartialEq)]` +//~| the trait bound `CantParamDerive: Eq` is not satisfied struct CantParamDerive(ImplementsConstParamTy); fn check() {} diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr index 43c5b96dc7cb8..2cf7cc07dbe5e 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr @@ -1,3 +1,17 @@ +error[E0277]: the trait bound `CantParam: Eq` is not satisfied + --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:36 + | +LL | impl std::marker::ConstParamTy for CantParam {} + | ^^^^^^^^^ the trait `Eq` is not implemented for `CantParam` + | +note: required by a bound in `ConstParamTy` + --> $SRC_DIR/core/src/marker.rs:LL:COL +help: consider annotating `CantParam` with `#[derive(Eq)]` + | +LL + #[derive(Eq)] +LL | struct CantParam(ImplementsConstParamTy); + | + error[E0277]: the type `CantParam` does not `#[derive(PartialEq)]` --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:36 | @@ -16,8 +30,23 @@ LL | impl std::marker::ConstParamTy for CantParam {} note: required by a bound in `ConstParamTy` --> $SRC_DIR/core/src/marker.rs:LL:COL +error[E0277]: the trait bound `CantParamDerive: Eq` is not satisfied + --> $DIR/const_param_ty_impl_no_structural_eq.rs:15:10 + | +LL | #[derive(std::marker::ConstParamTy)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `CantParamDerive` + | +note: required by a bound in `ConstParamTy` + --> $SRC_DIR/core/src/marker.rs:LL:COL + = note: this error originates in the derive macro `std::marker::ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `CantParamDerive` with `#[derive(Eq)]` + | +LL + #[derive(Eq)] +LL | struct CantParamDerive(ImplementsConstParamTy); + | + error[E0277]: the type `CantParamDerive` does not `#[derive(PartialEq)]` - --> $DIR/const_param_ty_impl_no_structural_eq.rs:14:10 + --> $DIR/const_param_ty_impl_no_structural_eq.rs:15:10 | LL | #[derive(std::marker::ConstParamTy)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `StructuralPartialEq` is not implemented for `CantParamDerive` @@ -27,7 +56,7 @@ note: required by a bound in `ConstParamTy` = note: this error originates in the derive macro `std::marker::ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the type `CantParamDerive` does not `#[derive(Eq)]` - --> $DIR/const_param_ty_impl_no_structural_eq.rs:14:10 + --> $DIR/const_param_ty_impl_no_structural_eq.rs:15:10 | LL | #[derive(std::marker::ConstParamTy)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `StructuralEq` is not implemented for `CantParamDerive` @@ -36,6 +65,6 @@ note: required by a bound in `ConstParamTy` --> $SRC_DIR/core/src/marker.rs:LL:COL = note: this error originates in the derive macro `std::marker::ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0277`. From a1d181d740df13972533475b4b35d59678b3ab50 Mon Sep 17 00:00:00 2001 From: Lenko Donchev Date: Wed, 5 Jul 2023 20:19:14 -0500 Subject: [PATCH 116/149] Added additional visit steps to visit_generic_param() in order to avoid ICE on no bound vars. --- .../src/collect/resolve_bound_vars.rs | 31 +++++++++---------- tests/ui/closures/issue-112547.rs | 15 +++++++++ tests/ui/closures/issue-112547.stderr | 23 ++++++++++++++ 3 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 tests/ui/closures/issue-112547.rs create mode 100644 tests/ui/closures/issue-112547.stderr diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index a39cfd7b6e169..d9207f2de52c0 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -856,22 +856,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { walk_list!(this, visit_generic_param, generics.params); - for param in generics.params { - match param.kind { - GenericParamKind::Lifetime { .. } => {} - GenericParamKind::Type { default, .. } => { - if let Some(ty) = default { - this.visit_ty(ty); - } - } - GenericParamKind::Const { ty, default } => { - this.visit_ty(ty); - if let Some(default) = default { - this.visit_body(this.tcx.hir().body(default.body)); - } - } - } - } walk_list!(this, visit_where_predicate, generics.predicates); }) } @@ -1000,6 +984,21 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // like implicit `?Sized` or const-param-has-ty predicates. } } + + match p.kind { + GenericParamKind::Lifetime { .. } => {} + GenericParamKind::Type { default, .. } => { + if let Some(ty) = default { + self.visit_ty(ty); + } + } + GenericParamKind::Const { ty, default } => { + self.visit_ty(ty); + if let Some(default) = default { + self.visit_body(self.tcx.hir().body(default.body)); + } + } + } } } diff --git a/tests/ui/closures/issue-112547.rs b/tests/ui/closures/issue-112547.rs new file mode 100644 index 0000000000000..8ecb2abccd4f9 --- /dev/null +++ b/tests/ui/closures/issue-112547.rs @@ -0,0 +1,15 @@ +#![feature(non_lifetime_binders)] + //~^ WARNING the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes + +pub fn bar() +where + for V: IntoIterator +//~^ ERROR cannot find type `V` in this scope [E0412] +{ +} + +fn main() { + bar(); +} diff --git a/tests/ui/closures/issue-112547.stderr b/tests/ui/closures/issue-112547.stderr new file mode 100644 index 0000000000000..d86b05dc6a7bf --- /dev/null +++ b/tests/ui/closures/issue-112547.stderr @@ -0,0 +1,23 @@ +error[E0412]: cannot find type `V` in this scope + --> $DIR/issue-112547.rs:8:4 + | +LL | }> V: IntoIterator + | ^ not found in this scope + | +help: you might be missing a type parameter + | +LL | pub fn bar() + | +++ + +warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-112547.rs:1:12 + | +LL | #![feature(non_lifetime_binders)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #108185 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0412`. From 5eb7604482f8f3264605c0e440861223576d6faf Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 25 Sep 2023 10:26:33 +0200 Subject: [PATCH 117/149] Bump nightly version -> 2023-09-25 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 9f5116eb73bb2..5ce22b65f0074 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-09-07" +channel = "nightly-2023-09-25" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 903add0c11a0205314bf49fff6851fea3478449c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 25 Sep 2023 10:31:51 +0200 Subject: [PATCH 118/149] Fix dogfood fallout --- clippy_lints/src/doc.rs | 3 ++- clippy_lints/src/raw_strings.rs | 2 +- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index bf2add6aa64f7..e789e0da6797a 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -459,7 +459,7 @@ struct Fragments<'a> { impl Fragments<'_> { fn span(self, cx: &LateContext<'_>, range: Range) -> Option { - source_span_for_markdown_range(cx.tcx, &self.doc, &range, &self.fragments) + source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments) } } @@ -513,6 +513,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; +#[allow(clippy::too_many_lines)] // Only a big match statement fn check_doc<'a, Events: Iterator, Range)>>( cx: &LateContext<'_>, valid_idents: &FxHashSet, diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index ba80e37d37ebd..2895595e03908 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -93,7 +93,7 @@ impl EarlyLintPass for RawStrings { diag.span_suggestion( start, "use a string literal instead", - format!("\"{}\"", str), + format!("\"{str}\""), Applicability::MachineApplicable, ); } else { diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 713158c3a55ae..d05d9e7640f0b 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -207,7 +207,7 @@ fn path_segment_certainty( // Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE. if cx.tcx.res_generics_def_id(path_segment.res).is_some() { let generics = cx.tcx.generics_of(def_id); - let count = generics.params.len() - generics.host_effect_index.is_some() as usize; + let count = generics.params.len() - usize::from(generics.host_effect_index.is_some()); let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 { Certainty::Certain(None) } else { From 6d331b7f03164126f46094d8e9c14d4591e79918 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 25 Sep 2023 11:29:11 +0200 Subject: [PATCH 119/149] Update Cargo.lock --- Cargo.lock | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2025e58a8d1d..48cf0c158dd8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -515,7 +515,6 @@ version = "0.1.74" dependencies = [ "clippy_lints", "clippy_utils", - "derive-new", "filetime", "futures", "if_chain", @@ -944,17 +943,6 @@ dependencies = [ "syn 2.0.29", ] -[[package]] -name = "derive-new" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive_builder" version = "0.12.0" From 025a2cda0ec511b6bc66f02dcddc408bf386e7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sat, 23 Sep 2023 03:25:52 +0200 Subject: [PATCH 120/149] rustdoc: correctly render ret ty of cross-crate async fns --- src/librustdoc/clean/mod.rs | 21 +++++---- src/librustdoc/clean/types.rs | 44 +++++++++---------- tests/rustdoc/inline_cross/async-fn.rs | 19 ++++++++ .../inline_cross/auxiliary/async-fn.rs | 18 ++++++++ .../inline_cross/auxiliary/impl_trait_aux.rs | 6 --- tests/rustdoc/inline_cross/impl_trait.rs | 8 ---- 6 files changed, 72 insertions(+), 44 deletions(-) create mode 100644 tests/rustdoc/inline_cross/async-fn.rs create mode 100644 tests/rustdoc/inline_cross/auxiliary/async-fn.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 190b1b038b68a..4b53f7a6b7c69 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1107,10 +1107,7 @@ fn clean_function<'tcx>( clean_args_from_types_and_names(cx, sig.decl.inputs, names) } }; - let mut decl = clean_fn_decl_with_args(cx, sig.decl, args); - if sig.header.is_async() { - decl.output = decl.sugared_async_return_type(); - } + let decl = clean_fn_decl_with_args(cx, sig.decl, Some(&sig.header), args); (generics, decl) }); Box::new(Function { decl, generics }) @@ -1161,12 +1158,16 @@ fn clean_args_from_types_and_body_id<'tcx>( fn clean_fn_decl_with_args<'tcx>( cx: &mut DocContext<'tcx>, decl: &hir::FnDecl<'tcx>, + header: Option<&hir::FnHeader>, args: Arguments, ) -> FnDecl { - let output = match decl.output { + let mut output = match decl.output { hir::FnRetTy::Return(typ) => clean_ty(typ, cx), hir::FnRetTy::DefaultReturn(..) => Type::Tuple(Vec::new()), }; + if let Some(header) = header && header.is_async() { + output = output.sugared_async_return_type(); + } FnDecl { inputs: args, output, c_variadic: decl.c_variadic } } @@ -1179,7 +1180,11 @@ fn clean_fn_decl_from_did_and_sig<'tcx>( // We assume all empty tuples are default return type. This theoretically can discard `-> ()`, // but shouldn't change any code meaning. - let output = clean_middle_ty(sig.output(), cx, None, None); + let mut output = clean_middle_ty(sig.output(), cx, None, None); + + if let Some(did) = did && cx.tcx.asyncness(did).is_async() { + output = output.sugared_async_return_type(); + } FnDecl { output, @@ -2566,7 +2571,7 @@ fn clean_bare_fn_ty<'tcx>( .map(|x| clean_generic_param(cx, None, x)) .collect(); let args = clean_args_from_types_and_names(cx, bare_fn.decl.inputs, bare_fn.param_names); - let decl = clean_fn_decl_with_args(cx, bare_fn.decl, args); + let decl = clean_fn_decl_with_args(cx, bare_fn.decl, None, args); (generic_params, decl) }); BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params } @@ -3077,7 +3082,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>( // NOTE: generics must be cleaned before args let generics = clean_generics(generics, cx); let args = clean_args_from_types_and_names(cx, decl.inputs, names); - let decl = clean_fn_decl_with_args(cx, decl, args); + let decl = clean_fn_decl_with_args(cx, decl, None, args); (generics, decl) }); ForeignFunctionItem(Box::new(Function { decl, generics })) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ef7794cc41e2c..d422a0b9a7f30 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1380,28 +1380,6 @@ impl FnDecl { pub(crate) fn self_type(&self) -> Option { self.inputs.values.get(0).and_then(|v| v.to_self()) } - - /// Returns the sugared return type for an async function. - /// - /// For example, if the return type is `impl std::future::Future`, this function - /// will return `i32`. - /// - /// # Panics - /// - /// This function will panic if the return type does not match the expected sugaring for async - /// functions. - pub(crate) fn sugared_async_return_type(&self) -> Type { - if let Type::ImplTrait(v) = &self.output && - let [GenericBound::TraitBound(PolyTrait { trait_, .. }, _ )] = &v[..] - { - let bindings = trait_.bindings().unwrap(); - let ret_ty = bindings[0].term(); - let ty = ret_ty.ty().expect("Unexpected constant return term"); - ty.clone() - } else { - panic!("unexpected desugaring of async function") - } - } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -1613,6 +1591,28 @@ impl Type { } } + /// Returns the sugared return type for an async function. + /// + /// For example, if the return type is `impl std::future::Future`, this function + /// will return `i32`. + /// + /// # Panics + /// + /// This function will panic if the return type does not match the expected sugaring for async + /// functions. + pub(crate) fn sugared_async_return_type(&self) -> Type { + if let Type::ImplTrait(v) = self && + let [GenericBound::TraitBound(PolyTrait { trait_, .. }, _ )] = &v[..] + { + let bindings = trait_.bindings().unwrap(); + let ret_ty = bindings[0].term(); + let ty = ret_ty.ty().expect("unexpected constant in async fn return term"); + ty.clone() + } else { + panic!("unexpected async fn return type") + } + } + /// Checks if this is a `T::Name` path for an associated type. pub(crate) fn is_assoc_ty(&self) -> bool { match self { diff --git a/tests/rustdoc/inline_cross/async-fn.rs b/tests/rustdoc/inline_cross/async-fn.rs new file mode 100644 index 0000000000000..95e175aabd07d --- /dev/null +++ b/tests/rustdoc/inline_cross/async-fn.rs @@ -0,0 +1,19 @@ +// Regression test for issue #115760. +// Check that we render the correct return type of free and +// associated async functions reexported from external crates. + +// aux-crate:async_fn=async-fn.rs +// edition: 2021 +#![crate_name = "user"] + +// @has user/fn.load.html +// @has - '//pre[@class="rust item-decl"]' "pub async fn load() -> i32" +pub use async_fn::load; + +// @has user/trait.Load.html +// @has - '//*[@id="tymethod.run"]' 'async fn run(&self) -> i32' +pub use async_fn::Load; + +// @has user/struct.Loader.html +// @has - '//*[@id="method.run"]' 'async fn run(&self) -> i32' +pub use async_fn::Loader; diff --git a/tests/rustdoc/inline_cross/auxiliary/async-fn.rs b/tests/rustdoc/inline_cross/auxiliary/async-fn.rs new file mode 100644 index 0000000000000..767564ed145f3 --- /dev/null +++ b/tests/rustdoc/inline_cross/auxiliary/async-fn.rs @@ -0,0 +1,18 @@ +#![feature(async_fn_in_trait)] +// edition: 2021 + +pub async fn load() -> i32 { + 0 +} + +pub trait Load { + async fn run(&self) -> i32; +} + +pub struct Loader; + +impl Load for Loader { + async fn run(&self) -> i32 { + 1 + } +} diff --git a/tests/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs b/tests/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs index 19433c9682bb0..42cfc3dc3197d 100644 --- a/tests/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs +++ b/tests/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs @@ -33,9 +33,3 @@ pub struct Foo; impl Foo { pub fn method<'a>(_x: impl Clone + Into> + 'a) {} } - -pub struct Bar; - -impl Bar { - pub async fn async_foo(&self) {} -} diff --git a/tests/rustdoc/inline_cross/impl_trait.rs b/tests/rustdoc/inline_cross/impl_trait.rs index b6a1552bc00ca..5c802c5148611 100644 --- a/tests/rustdoc/inline_cross/impl_trait.rs +++ b/tests/rustdoc/inline_cross/impl_trait.rs @@ -33,15 +33,7 @@ pub use impl_trait_aux::func4; // @!has - '//pre[@class="rust item-decl"]' 'where' pub use impl_trait_aux::func5; -// @has impl_trait/fn.async_fn.html -// @has - '//pre[@class="rust item-decl"]' "pub async fn async_fn()" -pub use impl_trait_aux::async_fn; - // @has impl_trait/struct.Foo.html // @has - '//*[@id="method.method"]//h4[@class="code-header"]' "pub fn method<'a>(_x: impl Clone + Into> + 'a)" // @!has - '//*[@id="method.method"]//h4[@class="code-header"]' 'where' pub use impl_trait_aux::Foo; - -// @has impl_trait/struct.Bar.html -// @has - '//*[@id="method.async_foo"]' "pub async fn async_foo(" -pub use impl_trait_aux::Bar; From 2ffef3949e4f04a1621ba47058bef6f78443abc4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 25 Sep 2023 14:30:26 +0000 Subject: [PATCH 121/149] Fix jq in CI --- src/ci/scripts/setup-environment.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/scripts/setup-environment.sh b/src/ci/scripts/setup-environment.sh index 0bc35f932832c..d3c55a4d6b4aa 100755 --- a/src/ci/scripts/setup-environment.sh +++ b/src/ci/scripts/setup-environment.sh @@ -10,7 +10,7 @@ source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" # Load extra environment variables vars="${EXTRA_VARIABLES-}" -echo "${vars}" | jq '' >/dev/null # Validate JSON and exit on errors +echo "${vars}" | jq '.' >/dev/null # Validate JSON and exit on errors for key in $(echo "${vars}" | jq "keys[]" -r); do # On Windows, for whatever reason, $key contains the BOM character in it, # and that messes up `jq ".${key}"`. This line strips the BOM from the key. From e02a139a23690bfd18dff8b8ec9c9b7161118507 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 14 Sep 2023 15:42:28 +0000 Subject: [PATCH 122/149] Eliminate `with_tables` helper --- compiler/rustc_smir/src/rustc_internal/mod.rs | 59 +------------------ compiler/rustc_smir/src/rustc_smir/mod.rs | 37 ++++++------ 2 files changed, 18 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 10ee5af86c627..e32cd8da1299a 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -8,10 +8,7 @@ use std::ops::{ControlFlow, Index}; use crate::rustc_internal; use crate::stable_mir::CompilerError; -use crate::{ - rustc_smir::Tables, - stable_mir::{self, with}, -}; +use crate::{rustc_smir::Tables, stable_mir}; use rustc_driver::{Callbacks, Compilation, RunCompiler}; use rustc_interface::{interface, Queries}; use rustc_middle::mir::interpret::AllocId; @@ -19,60 +16,6 @@ use rustc_middle::ty::TyCtxt; pub use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::Span; -fn with_tables(mut f: impl FnMut(&mut Tables<'_>) -> R) -> R { - let mut ret = None; - with(|tables| tables.rustc_tables(&mut |t| ret = Some(f(t)))); - ret.unwrap() -} - -pub fn item_def_id(item: &stable_mir::CrateItem) -> DefId { - with_tables(|t| t[item.0]) -} - -pub fn crate_item(did: DefId) -> stable_mir::CrateItem { - with_tables(|t| t.crate_item(did)) -} - -pub fn adt_def(did: DefId) -> stable_mir::ty::AdtDef { - with_tables(|t| t.adt_def(did)) -} - -pub fn foreign_def(did: DefId) -> stable_mir::ty::ForeignDef { - with_tables(|t| t.foreign_def(did)) -} - -pub fn fn_def(did: DefId) -> stable_mir::ty::FnDef { - with_tables(|t| t.fn_def(did)) -} - -pub fn closure_def(did: DefId) -> stable_mir::ty::ClosureDef { - with_tables(|t| t.closure_def(did)) -} - -pub fn generator_def(did: DefId) -> stable_mir::ty::GeneratorDef { - with_tables(|t| t.generator_def(did)) -} - -pub fn alias_def(did: DefId) -> stable_mir::ty::AliasDef { - with_tables(|t| t.alias_def(did)) -} - -pub fn param_def(did: DefId) -> stable_mir::ty::ParamDef { - with_tables(|t| t.param_def(did)) -} - -pub fn br_named_def(did: DefId) -> stable_mir::ty::BrNamedDef { - with_tables(|t| t.br_named_def(did)) -} - -pub fn trait_def(did: DefId) -> stable_mir::ty::TraitDef { - with_tables(|t| t.trait_def(did)) -} - -pub fn impl_def(did: DefId) -> stable_mir::ty::ImplDef { - with_tables(|t| t.impl_def(did)) -} - impl<'tcx> Index for Tables<'tcx> { type Output = DefId; diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index b575445c4d5d2..5620c924e4cfd 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -7,7 +7,7 @@ //! //! For now, we are developing everything inside `rustc`, thus, we keep this module private. -use crate::rustc_internal::{self, opaque}; +use crate::rustc_internal::opaque; use crate::stable_mir::mir::{CopyNonOverlapping, UserTypeProjection, VariantIdx}; use crate::stable_mir::ty::{ FloatTy, GenericParamDef, IntTy, Movability, RigidTy, Span, TyKind, UintTy, @@ -276,7 +276,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { place.stable(tables), ), ThreadLocalRef(def_id) => { - stable_mir::mir::Rvalue::ThreadLocalRef(rustc_internal::crate_item(*def_id)) + stable_mir::mir::Rvalue::ThreadLocalRef(tables.crate_item(*def_id)) } AddressOf(mutability, place) => { stable_mir::mir::Rvalue::AddressOf(mutability.stable(tables), place.stable(tables)) @@ -739,7 +739,7 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { mir::AggregateKind::Tuple => stable_mir::mir::AggregateKind::Tuple, mir::AggregateKind::Adt(def_id, var_idx, generic_arg, user_ty_index, field_idx) => { stable_mir::mir::AggregateKind::Adt( - rustc_internal::adt_def(*def_id), + tables.adt_def(*def_id), var_idx.index(), generic_arg.stable(tables), user_ty_index.map(|idx| idx.index()), @@ -748,13 +748,13 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { } mir::AggregateKind::Closure(def_id, generic_arg) => { stable_mir::mir::AggregateKind::Closure( - rustc_internal::closure_def(*def_id), + tables.closure_def(*def_id), generic_arg.stable(tables), ) } mir::AggregateKind::Generator(def_id, generic_arg, movability) => { stable_mir::mir::AggregateKind::Generator( - rustc_internal::generator_def(*def_id), + tables.generator_def(*def_id), generic_arg.stable(tables), movability.stable(tables), ) @@ -964,13 +964,13 @@ impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> { impl<'tcx> Stable<'tcx> for ty::BoundTyKind { type T = stable_mir::ty::BoundTyKind; - fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use stable_mir::ty::BoundTyKind; match self { ty::BoundTyKind::Anon => BoundTyKind::Anon, ty::BoundTyKind::Param(def_id, symbol) => { - BoundTyKind::Param(rustc_internal::param_def(*def_id), symbol.to_string()) + BoundTyKind::Param(tables.param_def(*def_id), symbol.to_string()) } } } @@ -985,7 +985,7 @@ impl<'tcx> Stable<'tcx> for ty::BoundRegionKind { match self { ty::BoundRegionKind::BrAnon => BoundRegionKind::BrAnon, ty::BoundRegionKind::BrNamed(def_id, symbol) => { - BoundRegionKind::BrNamed(rustc_internal::br_named_def(*def_id), symbol.to_string()) + BoundRegionKind::BrNamed(tables.br_named_def(*def_id), symbol.to_string()) } ty::BoundRegionKind::BrEnv => BoundRegionKind::BrEnv, } @@ -1072,12 +1072,10 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> { ty::Uint(uint_ty) => TyKind::RigidTy(RigidTy::Uint(uint_ty.stable(tables))), ty::Float(float_ty) => TyKind::RigidTy(RigidTy::Float(float_ty.stable(tables))), ty::Adt(adt_def, generic_args) => TyKind::RigidTy(RigidTy::Adt( - rustc_internal::adt_def(adt_def.did()), + tables.adt_def(adt_def.did()), generic_args.stable(tables), )), - ty::Foreign(def_id) => { - TyKind::RigidTy(RigidTy::Foreign(rustc_internal::foreign_def(*def_id))) - } + ty::Foreign(def_id) => TyKind::RigidTy(RigidTy::Foreign(tables.foreign_def(*def_id))), ty::Str => TyKind::RigidTy(RigidTy::Str), ty::Array(ty, constant) => { TyKind::RigidTy(RigidTy::Array(tables.intern_ty(*ty), constant.stable(tables))) @@ -1091,10 +1089,9 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> { tables.intern_ty(*ty), mutbl.stable(tables), )), - ty::FnDef(def_id, generic_args) => TyKind::RigidTy(RigidTy::FnDef( - rustc_internal::fn_def(*def_id), - generic_args.stable(tables), - )), + ty::FnDef(def_id, generic_args) => { + TyKind::RigidTy(RigidTy::FnDef(tables.fn_def(*def_id), generic_args.stable(tables))) + } ty::FnPtr(poly_fn_sig) => TyKind::RigidTy(RigidTy::FnPtr(poly_fn_sig.stable(tables))), ty::Dynamic(existential_predicates, region, dyn_kind) => { TyKind::RigidTy(RigidTy::Dynamic( @@ -1107,11 +1104,11 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> { )) } ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( - rustc_internal::closure_def(*def_id), + tables.closure_def(*def_id), generic_args.stable(tables), )), ty::Generator(def_id, generic_args, movability) => TyKind::RigidTy(RigidTy::Generator( - rustc_internal::generator_def(*def_id), + tables.generator_def(*def_id), generic_args.stable(tables), movability.stable(tables), )), @@ -1223,7 +1220,7 @@ impl<'tcx> Stable<'tcx> for ty::TraitDef { use stable_mir::ty::TraitDecl; TraitDecl { - def_id: rustc_internal::trait_def(self.def_id), + def_id: tables.trait_def(self.def_id), unsafety: self.unsafety.stable(tables), paren_sugar: self.paren_sugar, has_auto_impl: self.has_auto_impl, @@ -1272,7 +1269,7 @@ impl<'tcx> Stable<'tcx> for ty::TraitRef<'tcx> { fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use stable_mir::ty::TraitRef; - TraitRef { def_id: rustc_internal::trait_def(self.def_id), args: self.args.stable(tables) } + TraitRef { def_id: tables.trait_def(self.def_id), args: self.args.stable(tables) } } } From 55b6f649024b372af901b3412a6d36c4284ccf3d Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 14 Sep 2023 15:43:30 +0000 Subject: [PATCH 123/149] Eliminate escape hatch --- compiler/rustc_smir/src/rustc_internal/mod.rs | 9 +++++++++ compiler/rustc_smir/src/rustc_smir/mod.rs | 9 +++++---- compiler/rustc_smir/src/stable_mir/mod.rs | 10 +++++----- compiler/rustc_smir/src/stable_mir/ty.rs | 7 ++++--- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index e32cd8da1299a..f60332fc2267e 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -25,6 +25,15 @@ impl<'tcx> Index for Tables<'tcx> { } } +impl<'tcx> Index for Tables<'tcx> { + type Output = Span; + + #[inline(always)] + fn index(&self, index: stable_mir::ty::Span) -> &Self::Output { + &self.spans[index.0] + } +} + impl<'tcx> Tables<'tcx> { pub fn crate_item(&mut self, did: DefId) -> stable_mir::CrateItem { stable_mir::CrateItem(self.create_def_id(did)) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 5620c924e4cfd..9caf3de6e06ab 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -44,6 +44,11 @@ impl<'tcx> Context for Tables<'tcx> { self.tcx.def_path_str(self[def_id]) } + fn print_span(&self, span: stable_mir::ty::Span) -> String { + self.tcx.sess.source_map().span_to_diagnostic_string(self[span]) + } + + fn span_of_an_item(&mut self, def_id: stable_mir::DefId) -> Span { self.tcx.def_span(self[def_id]).stable(self) } @@ -104,10 +109,6 @@ impl<'tcx> Context for Tables<'tcx> { } } - fn rustc_tables(&mut self, f: &mut dyn FnMut(&mut Tables<'_>)) { - f(self) - } - fn ty_kind(&mut self, ty: crate::stable_mir::ty::Ty) -> TyKind { self.types[ty.0].clone().stable(self) } diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs index 3c86cb4038a0a..3c9287c4e727e 100644 --- a/compiler/rustc_smir/src/stable_mir/mod.rs +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -15,10 +15,11 @@ use std::cell::Cell; use std::fmt; use std::fmt::Debug; +use crate::rustc_internal::Opaque; + use self::ty::{ GenericPredicates, Generics, ImplDef, ImplTrait, Span, TraitDecl, TraitDef, Ty, TyKind, }; -use crate::rustc_smir::Tables; pub mod fold; pub mod mir; @@ -79,6 +80,8 @@ pub struct Crate { pub is_local: bool, } +pub type DefKind = Opaque; + /// Holds information about an item in the crate. /// For now, it only stores the item DefId. Use functions inside `rustc_internal` module to /// use this item. @@ -161,6 +164,7 @@ pub trait Context { /// Prints the name of given `DefId` fn name_of_def_id(&self, def_id: DefId) -> String; + fn print_span(&self, span: Span) -> String; /// `Span` of an item fn span_of_an_item(&mut self, def_id: DefId) -> Span; @@ -169,10 +173,6 @@ pub trait Context { /// Create a new `Ty` from scratch without information from rustc. fn mk_ty(&mut self, kind: TyKind) -> Ty; - - /// HACK: Until we have fully stable consumers, we need an escape hatch - /// to get `DefId`s out of `CrateItem`s. - fn rustc_tables(&mut self, f: &mut dyn FnMut(&mut Tables<'_>)); } // A thread local variable that stores a pointer to the tables mapping between TyCtxt diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index eb9967b3888eb..3f665212f2680 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -40,9 +40,10 @@ pub struct Span(pub(crate) usize); impl Debug for Span { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut span = None; - with(|context| context.rustc_tables(&mut |tables| span = Some(tables.spans[self.0]))); - f.write_fmt(format_args!("{:?}", &span.unwrap())) + f.debug_struct("Span") + .field("id", &self.0) + .field("repr", &with(|cx| cx.print_span(*self))) + .finish() } } From 33998a9751bf75e7e7b853038f09985855645949 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 18 Sep 2023 11:02:26 +0000 Subject: [PATCH 124/149] Fix test by adding a stable way to get an opaque DefKind --- compiler/rustc_smir/src/rustc_smir/mod.rs | 13 +++++++++++++ compiler/rustc_smir/src/stable_mir/mod.rs | 10 ++++++++++ tests/ui-fulldeps/stable-mir/crate-info.rs | 22 ++++++++++------------ 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 9caf3de6e06ab..3c6442396a8d2 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -13,6 +13,7 @@ use crate::stable_mir::ty::{ FloatTy, GenericParamDef, IntTy, Movability, RigidTy, Span, TyKind, UintTy, }; use crate::stable_mir::{self, CompilerError, Context}; +use hir::def::DefKind; use rustc_hir as hir; use rustc_middle::mir; use rustc_middle::mir::interpret::{alloc_range, AllocId}; @@ -48,6 +49,9 @@ impl<'tcx> Context for Tables<'tcx> { self.tcx.sess.source_map().span_to_diagnostic_string(self[span]) } + fn def_kind(&mut self, def_id: stable_mir::DefId) -> stable_mir::DefKind { + self.tcx.def_kind(self[def_id]).stable(self) + } fn span_of_an_item(&mut self, def_id: stable_mir::DefId) -> Span { self.tcx.def_span(self[def_id]).stable(self) @@ -1519,3 +1523,12 @@ impl From for CompilerError { CompilerError::CompilationFailed } } + +impl<'tcx> Stable<'tcx> for DefKind { + type T = stable_mir::DefKind; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + // FIXME: add a real implementation of stable DefKind + opaque(self) + } +} diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs index 3c9287c4e727e..312cb9ec0b264 100644 --- a/compiler/rustc_smir/src/stable_mir/mod.rs +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -96,6 +96,14 @@ impl CrateItem { pub fn span(&self) -> Span { with(|cx| cx.span_of_an_item(self.0)) } + + pub fn name(&self) -> String { + with(|cx| cx.name_of_def_id(self.0)) + } + + pub fn kind(&self) -> DefKind { + with(|cx| cx.def_kind(self.0)) + } } /// Return the function where execution starts if the current @@ -165,6 +173,8 @@ pub trait Context { fn name_of_def_id(&self, def_id: DefId) -> String; fn print_span(&self, span: Span) -> String; + + fn def_kind(&mut self, def_id: DefId) -> DefKind; /// `Span` of an item fn span_of_an_item(&mut self, def_id: DefId) -> Span; diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs index a11720c4b5542..3532256b0b86d 100644 --- a/tests/ui-fulldeps/stable-mir/crate-info.rs +++ b/tests/ui-fulldeps/stable-mir/crate-info.rs @@ -27,7 +27,7 @@ use std::ops::ControlFlow; const CRATE_NAME: &str = "input"; /// This function uses the Stable MIR APIs to get information about the test crate. -fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> { +fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> { // Get the local crate using stable_mir API. let local = stable_mir::local_crate(); assert_eq!(&local.name, CRATE_NAME); @@ -36,12 +36,12 @@ fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> { // Find items in the local crate. let items = stable_mir::all_local_items(); - assert!(get_item(tcx, &items, (DefKind::Fn, "foo::bar")).is_some()); + assert!(get_item(&items, (DefKind::Fn, "foo::bar")).is_some()); // Find the `std` crate. assert!(stable_mir::find_crate("std").is_some()); - let bar = get_item(tcx, &items, (DefKind::Fn, "bar")).unwrap(); + let bar = get_item(&items, (DefKind::Fn, "bar")).unwrap(); let body = bar.body(); assert_eq!(body.locals.len(), 2); assert_eq!(body.blocks.len(), 1); @@ -56,7 +56,7 @@ fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> { other => panic!("{other:?}"), } - let foo_bar = get_item(tcx, &items, (DefKind::Fn, "foo_bar")).unwrap(); + let foo_bar = get_item(&items, (DefKind::Fn, "foo_bar")).unwrap(); let body = foo_bar.body(); assert_eq!(body.locals.len(), 7); assert_eq!(body.blocks.len(), 4); @@ -66,7 +66,7 @@ fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> { other => panic!("{other:?}"), } - let types = get_item(tcx, &items, (DefKind::Fn, "types")).unwrap(); + let types = get_item(&items, (DefKind::Fn, "types")).unwrap(); let body = types.body(); assert_eq!(body.locals.len(), 6); assert_matches!( @@ -96,7 +96,7 @@ fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> { )) ); - let drop = get_item(tcx, &items, (DefKind::Fn, "drop")).unwrap(); + let drop = get_item(&items, (DefKind::Fn, "drop")).unwrap(); let body = drop.body(); assert_eq!(body.blocks.len(), 2); let block = &body.blocks[0]; @@ -105,7 +105,7 @@ fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> { other => panic!("{other:?}"), } - let assert = get_item(tcx, &items, (DefKind::Fn, "assert")).unwrap(); + let assert = get_item(&items, (DefKind::Fn, "assert")).unwrap(); let body = assert.body(); assert_eq!(body.blocks.len(), 2); let block = &body.blocks[0]; @@ -114,7 +114,7 @@ fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> { other => panic!("{other:?}"), } - let monomorphic = get_item(tcx, &items, (DefKind::Fn, "monomorphic")).unwrap(); + let monomorphic = get_item(&items, (DefKind::Fn, "monomorphic")).unwrap(); for block in monomorphic.body().blocks { match &block.terminator { stable_mir::mir::Terminator::Call { func, .. } => match func { @@ -154,7 +154,7 @@ fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> { } } - let foo_const = get_item(tcx, &items, (DefKind::Const, "FOO")).unwrap(); + let foo_const = get_item(&items, (DefKind::Const, "FOO")).unwrap(); // Ensure we don't panic trying to get the body of a constant. foo_const.body(); @@ -163,13 +163,11 @@ fn test_stable_mir(tcx: TyCtxt<'_>) -> ControlFlow<()> { // Use internal API to find a function in a crate. fn get_item<'a>( - tcx: TyCtxt, items: &'a stable_mir::CrateItems, item: (DefKind, &str), ) -> Option<&'a stable_mir::CrateItem> { items.iter().find(|crate_item| { - let def_id = rustc_internal::item_def_id(crate_item); - tcx.def_kind(def_id) == item.0 && tcx.def_path_str(def_id) == item.1 + crate_item.kind().to_string() == format!("{:?}", item.0) && crate_item.name() == item.1 }) } From 19f1d782d51d980ccb5c448e6a415188323b30ce Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 18 Sep 2023 14:47:39 +0000 Subject: [PATCH 125/149] Move `Opaque` to `stable_mir` --- compiler/rustc_smir/src/rustc_internal/mod.rs | 21 ------------------ compiler/rustc_smir/src/rustc_smir/mod.rs | 3 +-- compiler/rustc_smir/src/stable_mir/fold.rs | 11 +++++----- .../rustc_smir/src/stable_mir/mir/body.rs | 2 +- compiler/rustc_smir/src/stable_mir/mod.rs | 22 +++++++++++++++++-- compiler/rustc_smir/src/stable_mir/ty.rs | 3 +-- compiler/rustc_smir/src/stable_mir/visitor.rs | 11 +++++----- 7 files changed, 35 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index f60332fc2267e..6d55676c6457f 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -3,7 +3,6 @@ //! For that, we define APIs that will temporarily be public to 3P that exposes rustc internal APIs //! until stable MIR is complete. -use std::fmt::Debug; use std::ops::{ControlFlow, Index}; use crate::rustc_internal; @@ -136,26 +135,6 @@ pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) { ); } -/// A type that provides internal information but that can still be used for debug purpose. -#[derive(Clone)] -pub struct Opaque(String); - -impl std::fmt::Display for Opaque { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::fmt::Debug for Opaque { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } -} - -pub(crate) fn opaque(value: &T) -> Opaque { - Opaque(format!("{value:?}")) -} - pub struct StableMir where B: Send, diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 3c6442396a8d2..690cc97c74801 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -7,12 +7,11 @@ //! //! For now, we are developing everything inside `rustc`, thus, we keep this module private. -use crate::rustc_internal::opaque; use crate::stable_mir::mir::{CopyNonOverlapping, UserTypeProjection, VariantIdx}; use crate::stable_mir::ty::{ FloatTy, GenericParamDef, IntTy, Movability, RigidTy, Span, TyKind, UintTy, }; -use crate::stable_mir::{self, CompilerError, Context}; +use crate::stable_mir::{self, opaque, CompilerError, Context}; use hir::def::DefKind; use rustc_hir as hir; use rustc_middle::mir; diff --git a/compiler/rustc_smir/src/stable_mir/fold.rs b/compiler/rustc_smir/src/stable_mir/fold.rs index 831cfb40a1542..d09386cff3a2a 100644 --- a/compiler/rustc_smir/src/stable_mir/fold.rs +++ b/compiler/rustc_smir/src/stable_mir/fold.rs @@ -1,10 +1,11 @@ use std::ops::ControlFlow; -use crate::rustc_internal::Opaque; - -use super::ty::{ - Allocation, Binder, Const, ConstDef, ConstantKind, ExistentialPredicate, FnSig, GenericArgKind, - GenericArgs, Promoted, RigidTy, TermKind, Ty, TyKind, UnevaluatedConst, +use super::{ + ty::{ + Allocation, Binder, Const, ConstDef, ConstantKind, ExistentialPredicate, FnSig, + GenericArgKind, GenericArgs, Promoted, RigidTy, TermKind, Ty, TyKind, UnevaluatedConst, + }, + Opaque, }; pub trait Folder: Sized { diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs index 449ca4b8145ed..c288df3c2b6c7 100644 --- a/compiler/rustc_smir/src/stable_mir/mir/body.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs @@ -1,7 +1,7 @@ -use crate::rustc_internal::Opaque; use crate::stable_mir::ty::{ AdtDef, ClosureDef, Const, GeneratorDef, GenericArgs, Movability, Region, }; +use crate::stable_mir::Opaque; use crate::stable_mir::{self, ty::Ty, Span}; #[derive(Clone, Debug)] diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs index 312cb9ec0b264..3679a20a72dcf 100644 --- a/compiler/rustc_smir/src/stable_mir/mod.rs +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -15,8 +15,6 @@ use std::cell::Cell; use std::fmt; use std::fmt::Debug; -use crate::rustc_internal::Opaque; - use self::ty::{ GenericPredicates, Generics, ImplDef, ImplTrait, Span, TraitDecl, TraitDef, Ty, TyKind, }; @@ -210,3 +208,23 @@ pub(crate) fn with(f: impl FnOnce(&mut dyn Context) -> R) -> R { f(unsafe { *(ptr as *mut &mut dyn Context) }) }) } + +/// A type that provides internal information but that can still be used for debug purpose. +#[derive(Clone)] +pub struct Opaque(String); + +impl std::fmt::Display for Opaque { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::fmt::Debug for Opaque { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } +} + +pub(crate) fn opaque(value: &T) -> Opaque { + Opaque(format!("{value:?}")) +} diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 3f665212f2680..92ff47114085d 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -1,9 +1,8 @@ use super::{ mir::Safety, mir::{Body, Mutability}, - with, AllocId, DefId, + with, AllocId, DefId, Opaque, }; -use crate::rustc_internal::Opaque; use std::fmt::{self, Debug, Formatter}; #[derive(Copy, Clone)] diff --git a/compiler/rustc_smir/src/stable_mir/visitor.rs b/compiler/rustc_smir/src/stable_mir/visitor.rs index c86063d2ed6eb..d6a31c99c1e92 100644 --- a/compiler/rustc_smir/src/stable_mir/visitor.rs +++ b/compiler/rustc_smir/src/stable_mir/visitor.rs @@ -1,10 +1,11 @@ use std::ops::ControlFlow; -use crate::rustc_internal::Opaque; - -use super::ty::{ - Allocation, Binder, Const, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs, - Promoted, RigidTy, TermKind, Ty, UnevaluatedConst, +use super::{ + ty::{ + Allocation, Binder, Const, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, + GenericArgs, Promoted, RigidTy, TermKind, Ty, UnevaluatedConst, + }, + Opaque, }; pub trait Visitor: Sized { From a38e98371bf2c2e81dd8f01da21330b87622dd3a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 14 Sep 2023 15:50:11 +0000 Subject: [PATCH 126/149] Split out the stable part of smir into its own crate to prevent accidental usage of forever unstable things --- Cargo.lock | 11 ++++- compiler/rustc/Cargo.toml | 1 + compiler/rustc_smir/Cargo.toml | 26 ++++-------- compiler/rustc_smir/src/lib.rs | 16 +------ compiler/rustc_smir/src/rustc_internal/mod.rs | 6 +-- compiler/rustc_smir/src/rustc_smir/alloc.rs | 8 ++-- compiler/rustc_smir/src/rustc_smir/mod.rs | 17 ++------ compiler/stable_mir/Cargo.toml | 8 ++++ compiler/{rustc_smir => stable_mir}/README.md | 0 .../rust-toolchain.toml | 0 .../src/stable_mir => stable_mir/src}/fold.rs | 11 +++-- .../mod.rs => stable_mir/src/lib.rs} | 42 ++++++++++++------- .../src/stable_mir => stable_mir/src}/mir.rs | 0 .../stable_mir => stable_mir/src}/mir/body.rs | 22 ++++------ .../src/stable_mir => stable_mir/src}/ty.rs | 33 ++++++++------- .../stable_mir => stable_mir/src}/visitor.rs | 11 +++-- src/bootstrap/compile.rs | 5 ++- .../stable-mir/compilation-result.rs | 3 +- tests/ui-fulldeps/stable-mir/crate-info.rs | 8 ++-- triagebot.toml | 4 ++ 20 files changed, 115 insertions(+), 117 deletions(-) create mode 100644 compiler/stable_mir/Cargo.toml rename compiler/{rustc_smir => stable_mir}/README.md (100%) rename compiler/{rustc_smir => stable_mir}/rust-toolchain.toml (100%) rename compiler/{rustc_smir/src/stable_mir => stable_mir/src}/fold.rs (97%) rename compiler/{rustc_smir/src/stable_mir/mod.rs => stable_mir/src/lib.rs} (87%) rename compiler/{rustc_smir/src/stable_mir => stable_mir/src}/mir.rs (100%) rename compiler/{rustc_smir/src/stable_mir => stable_mir/src}/mir/body.rs (94%) rename compiler/{rustc_smir/src/stable_mir => stable_mir/src}/ty.rs (94%) rename compiler/{rustc_smir/src/stable_mir => stable_mir/src}/visitor.rs (96%) diff --git a/Cargo.lock b/Cargo.lock index c60a37e24c310..48f71e85626df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3257,6 +3257,7 @@ dependencies = [ "rustc_driver", "rustc_driver_impl", "rustc_smir", + "stable_mir", ] [[package]] @@ -4421,7 +4422,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "scoped-tls", + "stable_mir", "tracing", ] @@ -4958,6 +4959,14 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stable_mir" +version = "0.1.0-preview" +dependencies = [ + "scoped-tls", + "tracing", +] + [[package]] name = "stacker" version = "0.1.15" diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 41003ad83f311..dcb165f9fdbf7 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -13,6 +13,7 @@ rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } # Make sure rustc_smir ends up in the sysroot, because this # crate is intended to be used by stable MIR consumers, which are not in-tree rustc_smir = { path = "../rustc_smir" } +stable_mir = { path = "../stable_mir" } [dependencies.jemalloc-sys] version = "0.5.0" diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml index 21ec904e43c58..4c29f743708ac 100644 --- a/compiler/rustc_smir/Cargo.toml +++ b/compiler/rustc_smir/Cargo.toml @@ -4,24 +4,14 @@ version = "0.0.0" edition = "2021" [dependencies] -# Use optional dependencies for rustc_* in order to support building this crate separately. -rustc_hir = { path = "../rustc_hir", optional = true } -rustc_middle = { path = "../rustc_middle", optional = true } -rustc_span = { path = "../rustc_span", optional = true } -rustc_target = { path = "../rustc_target", optional = true } -rustc_driver = { path = "../rustc_driver", optional = true } -rustc_interface = { path = "../rustc_interface", optional = true} -rustc_session = {path = "../rustc_session", optional = true} +rustc_hir = { path = "../rustc_hir" } +rustc_middle = { path = "../rustc_middle" } +rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } +rustc_driver = { path = "../rustc_driver" } +rustc_interface = { path = "../rustc_interface" } +rustc_session = {path = "../rustc_session" } tracing = "0.1" -scoped-tls = "1.0" +stable_mir = {path = "../stable_mir" } [features] -default = [ - "rustc_hir", - "rustc_middle", - "rustc_span", - "rustc_target", - "rustc_driver", - "rustc_interface", - "rustc_session", -] diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs index 8cb533c8d6726..b6c36678db52f 100644 --- a/compiler/rustc_smir/src/lib.rs +++ b/compiler/rustc_smir/src/lib.rs @@ -10,26 +10,12 @@ html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(allow(unused_variables), deny(warnings))) )] -#![cfg_attr(not(feature = "default"), feature(rustc_private))] +#![feature(rustc_private)] #![feature(ptr_metadata)] #![feature(type_alias_impl_trait)] // Used to define opaque types. #![feature(intra_doc_pointers)] -// Declare extern rustc_* crates to enable building this crate separately from the compiler. -#[cfg(not(feature = "default"))] -extern crate rustc_hir; -#[cfg(not(feature = "default"))] -extern crate rustc_middle; -#[cfg(not(feature = "default"))] -extern crate rustc_span; -#[cfg(not(feature = "default"))] -extern crate rustc_target; - pub mod rustc_internal; -pub mod stable_mir; // Make this module private for now since external users should not call these directly. mod rustc_smir; - -#[macro_use] -extern crate scoped_tls; diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 6d55676c6457f..441aafd1257ba 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -6,14 +6,14 @@ use std::ops::{ControlFlow, Index}; use crate::rustc_internal; -use crate::stable_mir::CompilerError; -use crate::{rustc_smir::Tables, stable_mir}; +use crate::rustc_smir::Tables; use rustc_driver::{Callbacks, Compilation, RunCompiler}; use rustc_interface::{interface, Queries}; use rustc_middle::mir::interpret::AllocId; use rustc_middle::ty::TyCtxt; pub use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::Span; +use stable_mir::CompilerError; impl<'tcx> Index for Tables<'tcx> { type Output = DefId; @@ -129,7 +129,7 @@ pub fn crate_num(item: &stable_mir::Crate) -> CrateNum { } pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) { - crate::stable_mir::run( + stable_mir::run( Tables { tcx, def_ids: vec![], alloc_ids: vec![], spans: vec![], types: vec![] }, f, ); diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs index d8766cf8ce23e..63a2a145069ee 100644 --- a/compiler/rustc_smir/src/rustc_smir/alloc.rs +++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs @@ -3,11 +3,9 @@ use rustc_middle::mir::{ ConstValue, }; -use crate::{ - rustc_smir::{Stable, Tables}, - stable_mir::mir::Mutability, - stable_mir::ty::{Allocation, ProvenanceMap}, -}; +use crate::rustc_smir::{Stable, Tables}; +use stable_mir::mir::Mutability; +use stable_mir::ty::{Allocation, ProvenanceMap}; /// Creates new empty `Allocation` from given `Align`. fn new_empty_allocation(align: rustc_target::abi::Align) -> Allocation { diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 690cc97c74801..3d9f03f60dee1 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -7,19 +7,16 @@ //! //! For now, we are developing everything inside `rustc`, thus, we keep this module private. -use crate::stable_mir::mir::{CopyNonOverlapping, UserTypeProjection, VariantIdx}; -use crate::stable_mir::ty::{ - FloatTy, GenericParamDef, IntTy, Movability, RigidTy, Span, TyKind, UintTy, -}; -use crate::stable_mir::{self, opaque, CompilerError, Context}; use hir::def::DefKind; use rustc_hir as hir; use rustc_middle::mir; use rustc_middle::mir::interpret::{alloc_range, AllocId}; use rustc_middle::ty::{self, Ty, TyCtxt, Variance}; use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc_span::ErrorGuaranteed; use rustc_target::abi::FieldIdx; +use stable_mir::mir::{CopyNonOverlapping, UserTypeProjection, VariantIdx}; +use stable_mir::ty::{FloatTy, GenericParamDef, IntTy, Movability, RigidTy, Span, TyKind, UintTy}; +use stable_mir::{self, opaque, Context}; use tracing::debug; mod alloc; @@ -112,7 +109,7 @@ impl<'tcx> Context for Tables<'tcx> { } } - fn ty_kind(&mut self, ty: crate::stable_mir::ty::Ty) -> TyKind { + fn ty_kind(&mut self, ty: stable_mir::ty::Ty) -> TyKind { self.types[ty.0].clone().stable(self) } @@ -1517,12 +1514,6 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span { } } -impl From for CompilerError { - fn from(_error: ErrorGuaranteed) -> Self { - CompilerError::CompilationFailed - } -} - impl<'tcx> Stable<'tcx> for DefKind { type T = stable_mir::DefKind; diff --git a/compiler/stable_mir/Cargo.toml b/compiler/stable_mir/Cargo.toml new file mode 100644 index 0000000000000..c61e217bf9f0a --- /dev/null +++ b/compiler/stable_mir/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "stable_mir" +version = "0.1.0-preview" +edition = "2021" + +[dependencies] +tracing = "0.1" +scoped-tls = "1.0" diff --git a/compiler/rustc_smir/README.md b/compiler/stable_mir/README.md similarity index 100% rename from compiler/rustc_smir/README.md rename to compiler/stable_mir/README.md diff --git a/compiler/rustc_smir/rust-toolchain.toml b/compiler/stable_mir/rust-toolchain.toml similarity index 100% rename from compiler/rustc_smir/rust-toolchain.toml rename to compiler/stable_mir/rust-toolchain.toml diff --git a/compiler/rustc_smir/src/stable_mir/fold.rs b/compiler/stable_mir/src/fold.rs similarity index 97% rename from compiler/rustc_smir/src/stable_mir/fold.rs rename to compiler/stable_mir/src/fold.rs index d09386cff3a2a..16ae62311aaf0 100644 --- a/compiler/rustc_smir/src/stable_mir/fold.rs +++ b/compiler/stable_mir/src/fold.rs @@ -1,11 +1,10 @@ use std::ops::ControlFlow; -use super::{ - ty::{ - Allocation, Binder, Const, ConstDef, ConstantKind, ExistentialPredicate, FnSig, - GenericArgKind, GenericArgs, Promoted, RigidTy, TermKind, Ty, TyKind, UnevaluatedConst, - }, - Opaque, +use crate::Opaque; + +use super::ty::{ + Allocation, Binder, Const, ConstDef, ConstantKind, ExistentialPredicate, FnSig, GenericArgKind, + GenericArgs, Promoted, RigidTy, TermKind, Ty, TyKind, UnevaluatedConst, }; pub trait Folder: Sized { diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/stable_mir/src/lib.rs similarity index 87% rename from compiler/rustc_smir/src/stable_mir/mod.rs rename to compiler/stable_mir/src/lib.rs index 3679a20a72dcf..104985493efc1 100644 --- a/compiler/rustc_smir/src/stable_mir/mod.rs +++ b/compiler/stable_mir/src/lib.rs @@ -1,15 +1,21 @@ -//! Module that implements the public interface to the Stable MIR. +//! The WIP stable interface to rustc internals. //! -//! This module shall contain all type definitions and APIs that we expect third-party tools to invoke to -//! interact with the compiler. +//! For more information see //! -//! The goal is to eventually move this module to its own crate which shall be published on -//! [crates.io](https://crates.io). +//! # Note +//! +//! This API is still completely unstable and subject to change. + +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", + test(attr(allow(unused_variables), deny(warnings))) +)] //! -//! ## Note: +//! This crate shall contain all type definitions and APIs that we expect third-party tools to invoke to +//! interact with the compiler. //! -//! There shouldn't be any direct references to internal compiler constructs in this module. -//! If you need an internal construct, consider using `rustc_internal` or `rustc_smir`. +//! The goal is to eventually be published on +//! [crates.io](https://crates.io). use std::cell::Cell; use std::fmt; @@ -19,6 +25,9 @@ use self::ty::{ GenericPredicates, Generics, ImplDef, ImplTrait, Span, TraitDecl, TraitDef, Ty, TyKind, }; +#[macro_use] +extern crate scoped_tls; + pub mod fold; pub mod mir; pub mod ty; @@ -32,11 +41,11 @@ pub type CrateNum = usize; /// A unique identification number for each item accessible for the current compilation unit. #[derive(Clone, Copy, PartialEq, Eq)] -pub struct DefId(pub(crate) usize); +pub struct DefId(pub usize); impl Debug for DefId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("DefId:") + f.debug_struct("DefId") .field("id", &self.0) .field("name", &with(|cx| cx.name_of_def_id(*self))) .finish() @@ -45,7 +54,7 @@ impl Debug for DefId { /// A unique identification number for each provenance #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct AllocId(pub(crate) usize); +pub struct AllocId(pub usize); /// A list of crate items. pub type CrateItems = Vec; @@ -73,7 +82,7 @@ pub enum CompilerError { /// Holds information about a crate. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Crate { - pub(crate) id: CrateNum, + pub id: CrateNum, pub name: Symbol, pub is_local: bool, } @@ -84,7 +93,7 @@ pub type DefKind = Opaque; /// For now, it only stores the item DefId. Use functions inside `rustc_internal` module to /// use this item. #[derive(Clone, PartialEq, Eq, Debug)] -pub struct CrateItem(pub(crate) DefId); +pub struct CrateItem(pub DefId); impl CrateItem { pub fn body(&self) -> mir::Body { @@ -170,9 +179,12 @@ pub trait Context { /// Prints the name of given `DefId` fn name_of_def_id(&self, def_id: DefId) -> String; + /// Prints a human readable form of `Span` fn print_span(&self, span: Span) -> String; + /// Prints the kind of given `DefId` fn def_kind(&mut self, def_id: DefId) -> DefKind; + /// `Span` of an item fn span_of_an_item(&mut self, def_id: DefId) -> Span; @@ -200,7 +212,7 @@ pub fn run(mut context: impl Context, f: impl FnOnce()) { /// Loads the current context and calls a function with it. /// Do not nest these, as that will ICE. -pub(crate) fn with(f: impl FnOnce(&mut dyn Context) -> R) -> R { +pub fn with(f: impl FnOnce(&mut dyn Context) -> R) -> R { assert!(TLV.is_set()); TLV.with(|tlv| { let ptr = tlv.get(); @@ -225,6 +237,6 @@ impl std::fmt::Debug for Opaque { } } -pub(crate) fn opaque(value: &T) -> Opaque { +pub fn opaque(value: &T) -> Opaque { Opaque(format!("{value:?}")) } diff --git a/compiler/rustc_smir/src/stable_mir/mir.rs b/compiler/stable_mir/src/mir.rs similarity index 100% rename from compiler/rustc_smir/src/stable_mir/mir.rs rename to compiler/stable_mir/src/mir.rs diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/stable_mir/src/mir/body.rs similarity index 94% rename from compiler/rustc_smir/src/stable_mir/mir/body.rs rename to compiler/stable_mir/src/mir/body.rs index c288df3c2b6c7..6f8f7b06fa393 100644 --- a/compiler/rustc_smir/src/stable_mir/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -1,8 +1,6 @@ -use crate::stable_mir::ty::{ - AdtDef, ClosureDef, Const, GeneratorDef, GenericArgs, Movability, Region, -}; -use crate::stable_mir::Opaque; -use crate::stable_mir::{self, ty::Ty, Span}; +use crate::ty::{AdtDef, ClosureDef, Const, GeneratorDef, GenericArgs, Movability, Region}; +use crate::Opaque; +use crate::{ty::Ty, Span}; #[derive(Clone, Debug)] pub struct Body { @@ -135,7 +133,7 @@ pub enum AsyncGeneratorKind { } pub(crate) type LocalDefId = Opaque; -/// [`rustc_middle::mir::Coverage`] is heavily tied to internal details of the +/// The rustc coverage data structures are heavily tied to internal details of the /// coverage implementation that are likely to change, and are unlikely to be /// useful to third-party tools for the foreseeable future. pub(crate) type Coverage = Opaque; @@ -215,7 +213,7 @@ pub enum Rvalue { /// generator lowering, `Generator` aggregate kinds are disallowed too. Aggregate(AggregateKind, Vec), - /// * `Offset` has the same semantics as [`offset`](pointer::offset), except that the second + /// * `Offset` has the same semantics as `<*const T>::offset`, except that the second /// parameter may be a `usize` as well. /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats, /// raw pointers, or function pointers and return a `bool`. The types of the operands must be @@ -245,16 +243,14 @@ pub enum Rvalue { /// deref operation, immediately followed by one or more projections. CopyForDeref(Place), - /// Computes the discriminant of the place, returning it as an integer of type - /// [`discriminant_ty`]. Returns zero for types without discriminant. + /// Computes the discriminant of the place, returning it as an integer. + /// Returns zero for types without discriminant. /// /// The validity requirements for the underlying value are undecided for this rvalue, see /// [#91095]. Note too that the value of the discriminant is not the same thing as the - /// variant index; use [`discriminant_for_variant`] to convert. + /// variant index; /// - /// [`discriminant_ty`]: rustc_middle::ty::Ty::discriminant_ty /// [#91095]: https://github.com/rust-lang/rust/issues/91095 - /// [`discriminant_for_variant`]: rustc_middle::ty::Ty::discriminant_for_variant Discriminant(Place), /// Yields the length of the place, as a `usize`. @@ -295,7 +291,7 @@ pub enum Rvalue { /// /// **Needs clarification**: Are there weird additional semantics here related to the runtime /// nature of this operation? - ThreadLocalRef(stable_mir::CrateItem), + ThreadLocalRef(crate::CrateItem), /// Computes a value as described by the operation. NullaryOp(NullOp, Ty), diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/stable_mir/src/ty.rs similarity index 94% rename from compiler/rustc_smir/src/stable_mir/ty.rs rename to compiler/stable_mir/src/ty.rs index 92ff47114085d..82007e3068340 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -1,8 +1,9 @@ use super::{ mir::Safety, mir::{Body, Mutability}, - with, AllocId, DefId, Opaque, + with, AllocId, DefId, }; +use crate::Opaque; use std::fmt::{self, Debug, Formatter}; #[derive(Copy, Clone)] @@ -33,9 +34,9 @@ pub struct Const { } type Ident = Opaque; -pub(crate) type Region = Opaque; +pub type Region = Opaque; #[derive(Clone, Copy, PartialEq, Eq)] -pub struct Span(pub(crate) usize); +pub struct Span(pub usize); impl Debug for Span { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -110,10 +111,10 @@ pub enum Movability { } #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct ForeignDef(pub(crate) DefId); +pub struct ForeignDef(pub DefId); #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct FnDef(pub(crate) DefId); +pub struct FnDef(pub DefId); impl FnDef { pub fn body(&self) -> Body { @@ -122,34 +123,34 @@ impl FnDef { } #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct ClosureDef(pub(crate) DefId); +pub struct ClosureDef(pub DefId); #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct GeneratorDef(pub(crate) DefId); +pub struct GeneratorDef(pub DefId); #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct ParamDef(pub(crate) DefId); +pub struct ParamDef(pub DefId); #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct BrNamedDef(pub(crate) DefId); +pub struct BrNamedDef(pub DefId); #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct AdtDef(pub(crate) DefId); +pub struct AdtDef(pub DefId); #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct AliasDef(pub(crate) DefId); +pub struct AliasDef(pub DefId); #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct TraitDef(pub(crate) DefId); +pub struct TraitDef(pub DefId); #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct GenericDef(pub(crate) DefId); +pub struct GenericDef(pub DefId); #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct ConstDef(pub(crate) DefId); +pub struct ConstDef(pub DefId); #[derive(Clone, PartialEq, Eq, Debug)] -pub struct ImplDef(pub(crate) DefId); +pub struct ImplDef(pub DefId); #[derive(Clone, Debug)] pub struct GenericArgs(pub Vec); @@ -333,7 +334,7 @@ pub type Bytes = Vec>; pub type Size = usize; #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct Prov(pub(crate) AllocId); +pub struct Prov(pub AllocId); pub type Align = u64; pub type Promoted = u32; pub type InitMaskMaterialized = Vec; diff --git a/compiler/rustc_smir/src/stable_mir/visitor.rs b/compiler/stable_mir/src/visitor.rs similarity index 96% rename from compiler/rustc_smir/src/stable_mir/visitor.rs rename to compiler/stable_mir/src/visitor.rs index d6a31c99c1e92..9c3b4cd994a48 100644 --- a/compiler/rustc_smir/src/stable_mir/visitor.rs +++ b/compiler/stable_mir/src/visitor.rs @@ -1,11 +1,10 @@ use std::ops::ControlFlow; -use super::{ - ty::{ - Allocation, Binder, Const, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, - GenericArgs, Promoted, RigidTy, TermKind, Ty, UnevaluatedConst, - }, - Opaque, +use crate::Opaque; + +use super::ty::{ + Allocation, Binder, Const, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs, + Promoted, RigidTy, TermKind, Ty, UnevaluatedConst, }; pub trait Visitor: Sized { diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 4f19ffa83dbe8..292ccc5780fae 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -1789,7 +1789,10 @@ pub fn run_cargo( // During check builds we need to keep crate metadata keep = true; } else if rlib_only_metadata { - if filename.contains("jemalloc_sys") || filename.contains("rustc_smir") { + if filename.contains("jemalloc_sys") + || filename.contains("rustc_smir") + || filename.contains("stable_mir") + { // jemalloc_sys and rustc_smir are not linked into librustc_driver.so, // so we need to distribute them as rlib to be able to use them. keep |= filename.ends_with(".rlib"); diff --git a/tests/ui-fulldeps/stable-mir/compilation-result.rs b/tests/ui-fulldeps/stable-mir/compilation-result.rs index 23a9e2a064cb6..3ec1519fb13c4 100644 --- a/tests/ui-fulldeps/stable-mir/compilation-result.rs +++ b/tests/ui-fulldeps/stable-mir/compilation-result.rs @@ -11,9 +11,10 @@ extern crate rustc_middle; extern crate rustc_smir; +extern crate stable_mir; use rustc_middle::ty::TyCtxt; -use rustc_smir::{rustc_internal, stable_mir}; +use rustc_smir::rustc_internal; use std::io::Write; use std::ops::ControlFlow; diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs index 3532256b0b86d..ce4ee3c2463eb 100644 --- a/tests/ui-fulldeps/stable-mir/crate-info.rs +++ b/tests/ui-fulldeps/stable-mir/crate-info.rs @@ -13,13 +13,13 @@ extern crate rustc_hir; extern crate rustc_middle; extern crate rustc_smir; +extern crate stable_mir; use rustc_hir::def::DefKind; use rustc_middle::ty::TyCtxt; -use rustc_smir::{ - rustc_internal, - stable_mir::{self, fold::Foldable}, -}; +use rustc_smir::rustc_internal; + +use stable_mir::fold::Foldable; use std::assert_matches::assert_matches; use std::io::Write; use std::ops::ControlFlow; diff --git a/triagebot.toml b/triagebot.toml index dbced481993a9..648997ad6f0d6 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -542,6 +542,10 @@ cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"] message = "This PR changes Stable MIR" cc = ["@oli-obk", "@celinval", "@spastorino"] +[mentions."compiler/stable_mir"] +message = "This PR changes Stable MIR" +cc = ["@oli-obk", "@celinval", "@spastorino"] + [mentions."compiler/rustc_target/src/spec"] message = """ These commits modify **compiler targets**. From 411e431c7077cd875cb0ca89d1f61c4e5c55a795 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 25 Sep 2023 15:02:10 +0000 Subject: [PATCH 127/149] Rebase fallout --- compiler/rustc_smir/src/rustc_smir/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 3d9f03f60dee1..5ff17613b4e56 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -980,7 +980,7 @@ impl<'tcx> Stable<'tcx> for ty::BoundTyKind { impl<'tcx> Stable<'tcx> for ty::BoundRegionKind { type T = stable_mir::ty::BoundRegionKind; - fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use stable_mir::ty::BoundRegionKind; match self { From 07e9d1cc2467179c7d7e3a97142804256c15788f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 25 Sep 2023 17:38:39 +0200 Subject: [PATCH 128/149] Fix typo in rustdoc unstable features doc --- src/doc/rustdoc/src/unstable-features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 0d3f6338af45d..7473b09920fba 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -375,7 +375,7 @@ library, as an equivalent command-line argument is provided to `rustc` when buil This feature allows you to generate an index-page with a given markdown file. A good example of it is the [rust documentation index](https://doc.rust-lang.org/nightly/index.html). -With this, you'll have a page which you can custom as much as you want at the top of your crates. +With this, you'll have a page which you can customize as much as you want at the top of your crates. Using `index-page` option enables `enable-index-page` option as well. From f6d4950fee86c162ad0716b85b2c43111916c2ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 22 Sep 2023 22:17:12 +0000 Subject: [PATCH 129/149] Point at previous breaks that have the expected type --- compiler/rustc_hir_typeck/src/demand.rs | 45 +++++++++++++++++++++++-- tests/ui/loops/loop-break-value.stderr | 11 +++++- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index c08629a526926..6047461e0a618 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -530,7 +530,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // When encountering a type error on the value of a `break`, try to point at the reason for the // expected type. - fn annotate_loop_expected_due_to_inference( + pub fn annotate_loop_expected_due_to_inference( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, @@ -540,11 +540,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; }; let mut parent_id = self.tcx.hir().parent_id(expr.hir_id); + let mut parent; loop { // Climb the HIR tree to see if the current `Expr` is part of a `break;` statement. - let Some(hir::Node::Expr(parent)) = self.tcx.hir().find(parent_id) else { + let Some(hir::Node::Expr(p)) = self.tcx.hir().find(parent_id) else { break; }; + parent = p; parent_id = self.tcx.hir().parent_id(parent.hir_id); let hir::ExprKind::Break(destination, _) = parent.kind else { continue; @@ -582,6 +584,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, format!("this loop is expected to be of type `{expected}`"), ); + } else { + // Locate all other `break` statements within the same `loop` that might + // have affected inference. + struct FindBreaks<'tcx> { + destination: hir::Destination, + uses: Vec<&'tcx hir::Expr<'tcx>>, + } + impl<'tcx> Visitor<'tcx> for FindBreaks<'tcx> { + fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::Break(destination, _) = ex.kind + && self.destination.label == destination.label + { + self.uses.push(ex); + } + hir::intravisit::walk_expr(self, ex); + } + } + let mut expr_finder = FindBreaks { destination, uses: vec![] }; + expr_finder.visit_expr(parent); + for ex in expr_finder.uses { + let hir::ExprKind::Break(_, val) = ex.kind else { + continue; + }; + let ty = match val { + Some(val) => { + match self.typeck_results.borrow().expr_ty_adjusted_opt(val) { + None => continue, + Some(ty) => ty, + } + } + None => self.tcx.types.unit, + }; + if self.can_eq(self.param_env, ty, expected) { + err.span_label( + ex.span, + format!("expected because of this `break`"), + ); + } + } } break; } diff --git a/tests/ui/loops/loop-break-value.stderr b/tests/ui/loops/loop-break-value.stderr index 76d12b7c1b397..dfea1f53d0de9 100644 --- a/tests/ui/loops/loop-break-value.stderr +++ b/tests/ui/loops/loop-break-value.stderr @@ -142,6 +142,9 @@ LL | let val: ! = loop { break break; }; error[E0308]: mismatched types --> $DIR/loop-break-value.rs:11:19 | +LL | break "asdf"; + | ------------ expected because of this `break` +LL | } else { LL | break 123; | ^^^ expected `&str`, found integer @@ -176,7 +179,11 @@ error[E0308]: mismatched types --> $DIR/loop-break-value.rs:80:15 | LL | break (break, break); - | ^^^^^^^^^^^^^^ expected `()`, found `(!, !)` + | ^-----^^-----^ + | || | + | || expected because of this `break` + | |expected because of this `break` + | expected `()`, found `(!, !)` | = note: expected unit type `()` found tuple `(!, !)` @@ -184,6 +191,8 @@ LL | break (break, break); error[E0308]: mismatched types --> $DIR/loop-break-value.rs:85:15 | +LL | break; + | ----- expected because of this `break` LL | break 2; | ^ expected `()`, found integer From 2e0ad2025fbae45f58149c321d2492a36808fea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 22 Sep 2023 22:20:00 +0000 Subject: [PATCH 130/149] Handle yet another case of `break` type mismatch --- compiler/rustc_hir_typeck/src/expr.rs | 7 ++++++- tests/ui/loops/loop-break-value.stderr | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 4ad14ce3059f3..5fc8b1f9fbe50 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -43,7 +43,10 @@ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::error::TypeError::FieldMisMatch; +use rustc_middle::ty::error::{ + ExpectedFound, + TypeError::{FieldMisMatch, Sorts}, +}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitableExt}; use rustc_session::errors::ExprParenthesesNeeded; @@ -664,6 +667,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_mismatched_types_on_tail( &mut err, expr, ty, e_ty, target_id, ); + let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty })); + self.annotate_loop_expected_due_to_inference(&mut err, expr, error); if let Some(val) = ty_kind_suggestion(ty) { let label = destination .label diff --git a/tests/ui/loops/loop-break-value.stderr b/tests/ui/loops/loop-break-value.stderr index dfea1f53d0de9..9c76266a9debd 100644 --- a/tests/ui/loops/loop-break-value.stderr +++ b/tests/ui/loops/loop-break-value.stderr @@ -134,7 +134,10 @@ error[E0308]: mismatched types --> $DIR/loop-break-value.rs:4:31 | LL | let val: ! = loop { break break; }; - | ^^^^^ expected `!`, found `()` + | --- ---- ^^^^^ expected `!`, found `()` + | | | + | | this loop is expected to be of type `!` + | expected because of this assignment | = note: expected type `!` found unit type `()` From de377624be86c95374991fc7ac612daa9e81b2c7 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:00:44 -0400 Subject: [PATCH 131/149] Update books --- src/doc/embedded-book | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- src/doc/rustc-dev-guide | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/embedded-book b/src/doc/embedded-book index 99ad2847b865e..eac173690b8cc 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit 99ad2847b865e96d8ae7b333d3ee96963557e621 +Subproject commit eac173690b8cc99094e1d88bd49dd61127fbd285 diff --git a/src/doc/nomicon b/src/doc/nomicon index e3f3af69dce71..ddfa421448768 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit e3f3af69dce71cd37a785bccb7e58449197d940c +Subproject commit ddfa4214487686e91b21aa29afb972c08a8f0d5b diff --git a/src/doc/reference b/src/doc/reference index ee7c676fd6e28..5262e1c3b43a2 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit ee7c676fd6e287459cb407337652412c990686c0 +Subproject commit 5262e1c3b43a2c489df8f6717683a44c7a2260fd diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index 08bb147d51e81..a13b7c28ed705 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 08bb147d51e815b96e8db7ba4cf870f201c11ff8 +Subproject commit a13b7c28ed705891c681ce5417b3d1cdb12cecd1 From a1d6fc43403fadc5564af50d6212d45d9aace84d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 25 Sep 2023 18:28:22 +0200 Subject: [PATCH 132/149] rename lint; add tracking issue --- compiler/rustc_lint_defs/src/builtin.rs | 10 +++++----- .../rustc_mir_build/src/thir/pattern/const_to_pat.rs | 2 +- tests/ui/consts/const_in_pattern/issue-65466.stderr | 8 ++++---- tests/ui/match/issue-72896-non-partial-eq-const.stderr | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a749c52e2dd65..00d07cbb2e398 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2311,13 +2311,13 @@ declare_lint! { } declare_lint! { - /// The `match_without_partial_eq` lint detects constants that are used in patterns, + /// The `const_patterns_without_partial_eq` lint detects constants that are used in patterns, /// whose type does not implement `PartialEq`. /// /// ### Example /// /// ```rust,compile_fail - /// #![deny(match_without_partial_eq)] + /// #![deny(const_patterns_without_partial_eq)] /// /// trait EnumSetType { /// type Repr; @@ -2352,12 +2352,12 @@ declare_lint! { /// field-by-field. In the future we'd like to ensure that pattern matching always /// follows `PartialEq` semantics, so that trait bound will become a requirement for /// matching on constants. - pub MATCH_WITHOUT_PARTIAL_EQ, + pub CONST_PATTERNS_WITHOUT_PARTIAL_EQ, Warn, "constant in pattern does not implement `PartialEq`", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #X ", + reference: "issue #116122 ", }; } @@ -3407,6 +3407,7 @@ declare_lint_pass! { CONFLICTING_REPR_HINTS, CONST_EVALUATABLE_UNCHECKED, CONST_ITEM_MUTATION, + CONST_PATTERNS_WITHOUT_PARTIAL_EQ, DEAD_CODE, DEPRECATED, DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, @@ -3440,7 +3441,6 @@ declare_lint_pass! { LOSSY_PROVENANCE_CASTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, MACRO_USE_EXTERN_CRATE, - MATCH_WITHOUT_PARTIAL_EQ, META_VARIABLE_MISUSE, MISSING_ABI, MISSING_FRAGMENT_SPECIFIER, diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index ed20ed4a84f0b..4758ace73b61c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -246,7 +246,7 @@ impl<'tcx> ConstToPat<'tcx> { // any errors.) This ensures it shows up in cargo's future-compat reports as well. if !self.type_has_partial_eq_impl(cv.ty()) { self.tcx().emit_spanned_lint( - lint::builtin::MATCH_WITHOUT_PARTIAL_EQ, + lint::builtin::CONST_PATTERNS_WITHOUT_PARTIAL_EQ, self.id, self.span, NonPartialEqMatch { non_peq_ty: cv.ty() }, diff --git a/tests/ui/consts/const_in_pattern/issue-65466.stderr b/tests/ui/consts/const_in_pattern/issue-65466.stderr index 89fddbf853d5f..9c80cb3a849c0 100644 --- a/tests/ui/consts/const_in_pattern/issue-65466.stderr +++ b/tests/ui/consts/const_in_pattern/issue-65466.stderr @@ -5,8 +5,8 @@ LL | C => (), | ^ | = 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 #X - = note: `#[warn(match_without_partial_eq)]` on by default + = note: for more information, see issue #116122 + = note: `#[warn(const_patterns_without_partial_eq)]` on by default warning: 1 warning emitted @@ -18,6 +18,6 @@ LL | C => (), | ^ | = 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 #X - = note: `#[warn(match_without_partial_eq)]` on by default + = note: for more information, see issue #116122 + = note: `#[warn(const_patterns_without_partial_eq)]` on by default diff --git a/tests/ui/match/issue-72896-non-partial-eq-const.stderr b/tests/ui/match/issue-72896-non-partial-eq-const.stderr index f32cf43953160..a7fc0cfc05457 100644 --- a/tests/ui/match/issue-72896-non-partial-eq-const.stderr +++ b/tests/ui/match/issue-72896-non-partial-eq-const.stderr @@ -5,8 +5,8 @@ LL | CONST_SET => { /* ok */ } | ^^^^^^^^^ | = 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 #X - = note: `#[warn(match_without_partial_eq)]` on by default + = note: for more information, see issue #116122 + = note: `#[warn(const_patterns_without_partial_eq)]` on by default warning: 1 warning emitted @@ -18,6 +18,6 @@ LL | CONST_SET => { /* ok */ } | ^^^^^^^^^ | = 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 #X - = note: `#[warn(match_without_partial_eq)]` on by default + = note: for more information, see issue #116122 + = note: `#[warn(const_patterns_without_partial_eq)]` on by default From 109284996763bb778b07325386110ea5095cd82d Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 25 Sep 2023 17:08:40 +0000 Subject: [PATCH 133/149] Use Vec::retain in remove_dead_blocks. --- compiler/rustc_mir_transform/src/simplify.rs | 43 +++++++++++--------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index b7a51cfd61966..2795cf1570287 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -29,6 +29,7 @@ use crate::MirPass; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_index::bit_set::BitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_middle::mir::coverage::*; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -345,24 +346,22 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let basic_blocks = body.basic_blocks.as_mut(); let source_scopes = &body.source_scopes; - let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); - let mut used_blocks = 0; - for alive_index in reachable.iter() { - let alive_index = alive_index.index(); - replacements[alive_index] = BasicBlock::new(used_blocks); - if alive_index != used_blocks { - // Swap the next alive block data with the current available slot. Since - // alive_index is non-decreasing this is a valid operation. - basic_blocks.raw.swap(alive_index, used_blocks); - } - used_blocks += 1; - } - if tcx.sess.instrument_coverage() { - save_unreachable_coverage(basic_blocks, source_scopes, used_blocks); + save_unreachable_coverage(basic_blocks, source_scopes, &reachable); } - basic_blocks.raw.truncate(used_blocks); + let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); + let mut orig_index = 0; + let mut used_index = 0; + basic_blocks.raw.retain(|_| { + let keep = reachable.contains(BasicBlock::new(orig_index)); + if keep { + replacements[orig_index] = BasicBlock::new(used_index); + used_index += 1; + } + orig_index += 1; + keep + }); for block in basic_blocks { for target in block.terminator_mut().successors_mut() { @@ -404,11 +403,12 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { fn save_unreachable_coverage( basic_blocks: &mut IndexSlice>, source_scopes: &IndexSlice>, - first_dead_block: usize, + reachable: &BitSet, ) { // Identify instances that still have some live coverage counters left. let mut live = FxHashSet::default(); - for basic_block in &basic_blocks.raw[0..first_dead_block] { + for bb in reachable.iter() { + let basic_block = &basic_blocks[bb]; for statement in &basic_block.statements { let StatementKind::Coverage(coverage) = &statement.kind else { continue }; let CoverageKind::Counter { .. } = coverage.kind else { continue }; @@ -417,7 +417,8 @@ fn save_unreachable_coverage( } } - for block in &mut basic_blocks.raw[..first_dead_block] { + for bb in reachable.iter() { + let block = &mut basic_blocks[bb]; for statement in &mut block.statements { let StatementKind::Coverage(_) = &statement.kind else { continue }; let instance = statement.source_info.scope.inlined_instance(source_scopes); @@ -433,7 +434,11 @@ fn save_unreachable_coverage( // Retain coverage for instances that still have some live counters left. let mut retained_coverage = Vec::new(); - for dead_block in &basic_blocks.raw[first_dead_block..] { + for dead_block in basic_blocks.indices() { + if reachable.contains(dead_block) { + continue; + } + let dead_block = &basic_blocks[dead_block]; for statement in &dead_block.statements { let StatementKind::Coverage(coverage) = &statement.kind else { continue }; let Some(code_region) = &coverage.code_region else { continue }; From 91544e6a937b4c2c7181578533e6d10991559c14 Mon Sep 17 00:00:00 2001 From: Florian Schmiderer Date: Sat, 9 Sep 2023 14:00:24 +0200 Subject: [PATCH 134/149] Pass name of object file to LLVM so it can correctly emit S_OBJNAME --- .../src/back/owned_target_machine.rs | 2 ++ compiler/rustc_codegen_llvm/src/back/write.rs | 19 ++++++++++++++----- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_codegen_ssa/src/back/write.rs | 9 ++++++++- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 4 ++++ 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 707af1046bdf3..36484c3c3fc44 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -37,6 +37,7 @@ impl OwnedTargetMachine { relax_elf_relocations: bool, use_init_array: bool, split_dwarf_file: &CStr, + output_obj_file: &CStr, debug_info_compression: &CStr, force_emulated_tls: bool, args_cstr_buff: &[u8], @@ -68,6 +69,7 @@ impl OwnedTargetMachine { relax_elf_relocations, use_init_array, split_dwarf_file.as_ptr(), + output_obj_file.as_ptr(), debug_info_compression.as_ptr(), force_emulated_tls, args_cstr_buff.as_ptr() as *const c_char, diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 9fb20c02c6bef..c778a6e017fac 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -100,7 +100,7 @@ pub fn write_output_file<'ll>( } pub fn create_informational_target_machine(sess: &Session) -> OwnedTargetMachine { - let config = TargetMachineFactoryConfig { split_dwarf_file: None }; + let config = TargetMachineFactoryConfig { split_dwarf_file: None, output_obj_file: None }; // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. let features = llvm_util::global_llvm_features(sess, false); @@ -118,7 +118,11 @@ pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTargetMach } else { None }; - let config = TargetMachineFactoryConfig { split_dwarf_file }; + + let output_obj_file = + Some(tcx.output_filenames(()).temp_path(OutputType::Object, Some(mod_name))); + let config = TargetMachineFactoryConfig { split_dwarf_file, output_obj_file }; + target_machine_factory( &tcx.sess, tcx.backend_optimization_level(()), @@ -256,9 +260,13 @@ pub fn target_machine_factory( let debuginfo_compression = SmallCStr::new(&debuginfo_compression); Arc::new(move |config: TargetMachineFactoryConfig| { - let split_dwarf_file = - path_mapping.map_prefix(config.split_dwarf_file.unwrap_or_default()).0; - let split_dwarf_file = CString::new(split_dwarf_file.to_str().unwrap()).unwrap(); + let path_to_cstring_helper = |path: Option| -> CString { + let path = path_mapping.map_prefix(path.unwrap_or_default()).0; + CString::new(path.to_str().unwrap()).unwrap() + }; + + let split_dwarf_file = path_to_cstring_helper(config.split_dwarf_file); + let output_obj_file = path_to_cstring_helper(config.output_obj_file); OwnedTargetMachine::new( &triple, @@ -279,6 +287,7 @@ pub fn target_machine_factory( relax_elf_relocations, use_init_array, &split_dwarf_file, + &output_obj_file, &debuginfo_compression, force_emulated_tls, &args_cstr_buff, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 36303b08107c8..a038b3af03dd6 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2133,6 +2133,7 @@ extern "C" { RelaxELFRelocations: bool, UseInitArray: bool, SplitDwarfFile: *const c_char, + OutputObjFile: *const c_char, DebugInfoCompression: *const c_char, ForceEmulatedTls: bool, ArgsCstrBuff: *const c_char, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 3bf98c46dea7b..f192747c8abaa 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -286,6 +286,10 @@ pub struct TargetMachineFactoryConfig { /// so the path to the dwarf object has to be provided when we create the target machine. /// This can be ignored by backends which do not need it for their Split DWARF support. pub split_dwarf_file: Option, + + /// The name of the output object file. Used for setting OutputFilenames in target options + /// so that LLVM can emit the CodeView S_OBJNAME record in pdb files + pub output_obj_file: Option, } impl TargetMachineFactoryConfig { @@ -302,7 +306,10 @@ impl TargetMachineFactoryConfig { } else { None }; - TargetMachineFactoryConfig { split_dwarf_file } + + let output_obj_file = + Some(cgcx.output_filenames.temp_path(OutputType::Object, Some(module_name))); + TargetMachineFactoryConfig { split_dwarf_file, output_obj_file } } } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 461b5290e69d1..b729c40228b96 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -416,6 +416,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( bool RelaxELFRelocations, bool UseInitArray, const char *SplitDwarfFile, + const char *OutputObjFile, const char *DebugInfoCompression, bool ForceEmulatedTls, const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) { @@ -448,6 +449,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( if (SplitDwarfFile) { Options.MCOptions.SplitDwarfFile = SplitDwarfFile; } + if (OutputObjFile) { + Options.ObjectFilenameForDebug = OutputObjFile; + } #if LLVM_VERSION_GE(16, 0) if (!strcmp("zlib", DebugInfoCompression) && llvm::compression::zlib::isAvailable()) { Options.CompressDebugSections = DebugCompressionType::Zlib; From 17b313fb5734df8186071d4e30e46673098bb6a9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 25 Sep 2023 17:40:37 +0000 Subject: [PATCH 135/149] Only prevent field projections into opaque types, not types containing opaque types --- .../src/mem_categorization.rs | 2 +- .../opaque-cast-field-access-in-future.rs | 27 +++++++++++++++++++ .../opaque-cast-field-access-in-future.stderr | 9 +++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/ui/impl-trait/opaque-cast-field-access-in-future.rs create mode 100644 tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 7fb1dc2347e05..9574da0212f58 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -464,7 +464,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // Opaque types can't have field projections, but we can instead convert // the current place in-place (heh) to the hidden type, and then apply all // follow up projections on that. - if node_ty != place_ty && place_ty.has_opaque_types() { + if node_ty != place_ty && matches!(place_ty.kind(), ty::Alias(ty::Opaque, ..)) { projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty }); } projections.push(Projection { kind, ty }); diff --git a/tests/ui/impl-trait/opaque-cast-field-access-in-future.rs b/tests/ui/impl-trait/opaque-cast-field-access-in-future.rs new file mode 100644 index 0000000000000..3e3bc09a62aa8 --- /dev/null +++ b/tests/ui/impl-trait/opaque-cast-field-access-in-future.rs @@ -0,0 +1,27 @@ +// edition: 2021 + +use std::future::Future; + +async fn bop() { + fold(run(), |mut foo| async move { + &mut foo.bar; + }) +} + +fn fold(_: Foo, f: F) +where + F: FnMut(Foo) -> Fut, +{ + loop {} +} + +struct Foo { + bar: Vec, +} + +fn run() -> Foo> { + //~^ ERROR type annotations needed + loop {} +} + +fn main() {} diff --git a/tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr b/tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr new file mode 100644 index 0000000000000..ee4343b110f26 --- /dev/null +++ b/tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/opaque-cast-field-access-in-future.rs:22:17 + | +LL | fn run() -> Foo> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. From 58adfd84e29c268214928373ba61f8afbce75acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 25 Sep 2023 18:21:52 +0000 Subject: [PATCH 136/149] Account for more cases of nested `loop`s for `break` type mismatches --- compiler/rustc_hir_typeck/src/demand.rs | 30 ++++++++++------ .../src/fn_ctxt/suggestions.rs | 4 +++ tests/ui/loops/loop-break-value.rs | 24 +++++++++++++ tests/ui/loops/loop-break-value.stderr | 35 +++++++++++++++---- tests/ui/type/type-error-break-tail.stderr | 3 +- 5 files changed, 78 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 6047461e0a618..0ff4bd4f5e4fa 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -541,17 +541,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut parent_id = self.tcx.hir().parent_id(expr.hir_id); let mut parent; - loop { + 'outer: loop { // Climb the HIR tree to see if the current `Expr` is part of a `break;` statement. - let Some(hir::Node::Expr(p)) = self.tcx.hir().find(parent_id) else { + let Some( + hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Semi(&ref p), .. }) + | hir::Node::Expr(&ref p), + ) = self.tcx.hir().find(parent_id) + else { break; }; parent = p; - parent_id = self.tcx.hir().parent_id(parent.hir_id); + parent_id = self.tcx.hir().parent_id(parent_id); let hir::ExprKind::Break(destination, _) = parent.kind else { continue; }; - let mut parent_id = parent.hir_id; + let mut parent_id = parent_id; + let mut direct = false; loop { // Climb the HIR tree to find the (desugared) `loop` this `break` corresponds to. let parent = match self.tcx.hir().find(parent_id) { @@ -567,14 +572,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { parent_id = self.tcx.hir().parent_id(*hir_id); parent } - Some(hir::Node::Block(hir::Block { .. })) => { + Some(hir::Node::Block(_)) => { parent_id = self.tcx.hir().parent_id(parent_id); parent } _ => break, }; + if let hir::ExprKind::Loop(..) = parent.kind { + // When you have `'a: loop { break; }`, the `break` corresponds to the labeled + // loop, so we need to account for that. + direct = !direct; + } if let hir::ExprKind::Loop(_, label, _, span) = parent.kind - && destination.label == label + && (destination.label == label || direct) { if let Some((reason_span, message)) = self.maybe_get_coercion_reason(parent_id, parent.span) @@ -588,20 +598,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Locate all other `break` statements within the same `loop` that might // have affected inference. struct FindBreaks<'tcx> { - destination: hir::Destination, + label: Option, uses: Vec<&'tcx hir::Expr<'tcx>>, } impl<'tcx> Visitor<'tcx> for FindBreaks<'tcx> { fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { if let hir::ExprKind::Break(destination, _) = ex.kind - && self.destination.label == destination.label + && self.label == destination.label { self.uses.push(ex); } hir::intravisit::walk_expr(self, ex); } } - let mut expr_finder = FindBreaks { destination, uses: vec![] }; + let mut expr_finder = FindBreaks { label, uses: vec![] }; expr_finder.visit_expr(parent); for ex in expr_finder.uses { let hir::ExprKind::Break(_, val) = ex.kind else { @@ -624,7 +634,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - break; + break 'outer; } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 41f815a812a0a..abb689892183c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -65,6 +65,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr = expr.peel_drop_temps(); self.suggest_missing_semicolon(err, expr, expected, false); let mut pointing_at_return_type = false; + if let hir::ExprKind::Break(..) = expr.kind { + // `break` type mismatches provide better context for tail `loop` expressions. + return false; + } if let Some((fn_id, fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { pointing_at_return_type = self.suggest_missing_return_type( err, diff --git a/tests/ui/loops/loop-break-value.rs b/tests/ui/loops/loop-break-value.rs index f334f9d464a66..e38a5aa29eb4e 100644 --- a/tests/ui/loops/loop-break-value.rs +++ b/tests/ui/loops/loop-break-value.rs @@ -95,6 +95,30 @@ fn main() { break LOOP; //~^ ERROR cannot find value `LOOP` in this scope } + + let _ = 'a: loop { + loop { + break; // This doesn't affect the expected break type of the 'a loop + loop { + loop { + break 'a 1; + } + } + } + break; //~ ERROR mismatched types + }; + let _ = 'a: loop { + loop { + break; // This doesn't affect the expected break type of the 'a loop + loop { + loop { + break 'a 1; + } + } + } + break 'a; //~ ERROR mismatched types + }; + loop { // point at the return type break 2; //~ ERROR mismatched types } diff --git a/tests/ui/loops/loop-break-value.stderr b/tests/ui/loops/loop-break-value.stderr index 9c76266a9debd..39d75ad1490c4 100644 --- a/tests/ui/loops/loop-break-value.stderr +++ b/tests/ui/loops/loop-break-value.stderr @@ -164,11 +164,6 @@ LL | break "asdf"; error[E0308]: mismatched types --> $DIR/loop-break-value.rs:21:31 | -LL | let _: i32 = 'outer_loop: loop { - | - ---- this loop is expected to be of type `i32` - | | - | expected because of this assignment -LL | loop { LL | break 'outer_loop "nope"; | ^^^^^^ expected `i32`, found `&str` @@ -202,6 +197,8 @@ LL | break 2; error[E0308]: mismatched types --> $DIR/loop-break-value.rs:90:9 | +LL | break 2; + | ------- expected because of this `break` LL | break; | ^^^^^ | | @@ -209,7 +206,31 @@ LL | break; | help: give it a value of the expected type: `break value` error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:99:15 + --> $DIR/loop-break-value.rs:108:9 + | +LL | break 'a 1; + | ---------- expected because of this `break` +... +LL | break; + | ^^^^^ + | | + | expected integer, found `()` + | help: give it a value of the expected type: `break value` + +error[E0308]: mismatched types + --> $DIR/loop-break-value.rs:119:9 + | +LL | break 'a 1; + | ---------- expected because of this `break` +... +LL | break 'a; + | ^^^^^^^^ + | | + | expected integer, found `()` + | help: give it a value of the expected type: `break 'a value` + +error[E0308]: mismatched types + --> $DIR/loop-break-value.rs:123:15 | LL | fn main() { | - expected `()` because of this return type @@ -219,7 +240,7 @@ LL | loop { // point at the return type LL | break 2; | ^ expected `()`, found integer -error: aborting due to 18 previous errors; 1 warning emitted +error: aborting due to 20 previous errors; 1 warning emitted Some errors have detailed explanations: E0308, E0425, E0571. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/type/type-error-break-tail.stderr b/tests/ui/type/type-error-break-tail.stderr index 16dc6475c6f9f..4bae046de86ea 100644 --- a/tests/ui/type/type-error-break-tail.stderr +++ b/tests/ui/type/type-error-break-tail.stderr @@ -2,8 +2,9 @@ error[E0308]: mismatched types --> $DIR/type-error-break-tail.rs:3:20 | LL | fn loop_ending() -> i32 { - | --- expected `i32` because of return type + | --- expected `i32` because of this return type LL | loop { + | ---- this loop is expected to be of type `i32` LL | if false { break; } | ^^^^^ | | From f54db7c3a927ec20dc0c20f9cd41b8e9be345bea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 25 Sep 2023 21:40:40 +0200 Subject: [PATCH 137/149] Gate and validate #[rustc_safe_intrinsic] --- .../src/error_codes/E0094.md | 4 +-- .../src/error_codes/E0211.md | 4 +-- compiler/rustc_feature/src/builtin_attrs.rs | 5 +++- compiler/rustc_passes/messages.ftl | 4 +++ compiler/rustc_passes/src/check_attr.rs | 26 +++++++++++++++++++ compiler/rustc_passes/src/errors.rs | 9 +++++++ tests/ui/error-codes/E0094.rs | 2 +- tests/ui/extern/extern-with-type-bounds.rs | 2 +- .../intrinsics/feature-gate-safe-intrinsic.rs | 6 +++++ .../feature-gate-safe-intrinsic.stderr | 20 ++++++++++++++ tests/ui/intrinsics/intrinsic-alignment.rs | 2 +- tests/ui/repr/16-bit-repr-c-enum.rs | 2 +- tests/ui/structs-enums/rec-align-u32.rs | 2 +- tests/ui/structs-enums/rec-align-u64.rs | 2 +- 14 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 tests/ui/intrinsics/feature-gate-safe-intrinsic.rs create mode 100644 tests/ui/intrinsics/feature-gate-safe-intrinsic.stderr diff --git a/compiler/rustc_error_codes/src/error_codes/E0094.md b/compiler/rustc_error_codes/src/error_codes/E0094.md index 67a8c3678c5ff..d8c1a3cb55cb5 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0094.md +++ b/compiler/rustc_error_codes/src/error_codes/E0094.md @@ -3,7 +3,7 @@ An invalid number of generic parameters was passed to an intrinsic function. Erroneous code example: ```compile_fail,E0094 -#![feature(intrinsics)] +#![feature(intrinsics, rustc_attrs)] #![allow(internal_features)] extern "rust-intrinsic" { @@ -18,7 +18,7 @@ and verify with the function declaration in the Rust source code. Example: ``` -#![feature(intrinsics)] +#![feature(intrinsics, rustc_attrs)] #![allow(internal_features)] extern "rust-intrinsic" { diff --git a/compiler/rustc_error_codes/src/error_codes/E0211.md b/compiler/rustc_error_codes/src/error_codes/E0211.md index 70f14fffae673..19a482f6c93e4 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0211.md +++ b/compiler/rustc_error_codes/src/error_codes/E0211.md @@ -4,7 +4,7 @@ You used a function or type which doesn't fit the requirements for where it was used. Erroneous code examples: ```compile_fail -#![feature(intrinsics)] +#![feature(intrinsics, rustc_attrs)] #![allow(internal_features)] extern "rust-intrinsic" { @@ -41,7 +41,7 @@ impl Foo { For the first code example, please check the function definition. Example: ``` -#![feature(intrinsics)] +#![feature(intrinsics, rustc_attrs)] #![allow(internal_features)] extern "rust-intrinsic" { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 04ebe22a9eb8a..18397af565fa0 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -537,7 +537,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ allow_internal_unsafe, Normal, template!(Word), WarnFollowing, "allow_internal_unsafe side-steps the unsafe_code lint", ), - ungated!(rustc_safe_intrinsic, Normal, template!(Word), DuplicatesOk), rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing, "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ through unstable paths"), @@ -806,6 +805,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing, r#"`rustc_doc_primitive` is a rustc internal attribute"#, ), + rustc_attr!( + rustc_safe_intrinsic, Normal, template!(Word), WarnFollowing, + "the `#[rustc_safe_intrinsic]` attribute is used internally to mark intrinsics as safe" + ), // ========================================================================== // Internal attributes, Testing: diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index dc0776c5dade9..214c6d70960e5 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -648,6 +648,10 @@ passes_rustc_lint_opt_ty = `#[rustc_lint_opt_ty]` should be applied to a struct .label = not a struct +passes_rustc_safe_intrinsic = + attribute should be applied to intrinsic functions + .label = not an intrinsic function + passes_rustc_std_internal_symbol = attribute should be applied to functions or statics .label = not a function or static diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 12118a0268bab..0296215c8f91d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -195,6 +195,9 @@ impl CheckAttrVisitor<'_> { | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target), sym::link_ordinal => self.check_link_ordinal(&attr, span, target), sym::rustc_confusables => self.check_confusables(&attr, target), + sym::rustc_safe_intrinsic => { + self.check_rustc_safe_intrinsic(hir_id, attr, span, target) + } _ => true, }; @@ -2042,6 +2045,29 @@ impl CheckAttrVisitor<'_> { } } + fn check_rustc_safe_intrinsic( + &self, + hir_id: HirId, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + let hir = self.tcx.hir(); + + if let Target::ForeignFn = target + && let Some(parent) = hir.opt_parent_id(hir_id) + && let hir::Node::Item(Item { + kind: ItemKind::ForeignMod { abi: Abi::RustIntrinsic | Abi::PlatformIntrinsic, .. }, + .. + }) = hir.get(parent) + { + return true; + } + + self.tcx.sess.emit_err(errors::RustcSafeIntrinsic { attr_span: attr.span, span }); + false + } + fn check_rustc_std_internal_symbol( &self, attr: &Attribute, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 2ec6a0b924140..bcf5abbfe7d9d 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -620,6 +620,15 @@ pub struct RustcAllowConstFnUnstable { pub span: Span, } +#[derive(Diagnostic)] +#[diag(passes_rustc_safe_intrinsic)] +pub struct RustcSafeIntrinsic { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(passes_rustc_std_internal_symbol)] pub struct RustcStdInternalSymbol { diff --git a/tests/ui/error-codes/E0094.rs b/tests/ui/error-codes/E0094.rs index a2ec932c12455..97ebcff99dc94 100644 --- a/tests/ui/error-codes/E0094.rs +++ b/tests/ui/error-codes/E0094.rs @@ -1,4 +1,4 @@ -#![feature(intrinsics)] +#![feature(intrinsics, rustc_attrs)] extern "rust-intrinsic" { #[rustc_safe_intrinsic] diff --git a/tests/ui/extern/extern-with-type-bounds.rs b/tests/ui/extern/extern-with-type-bounds.rs index a72aa4171a103..99e9801fd4010 100644 --- a/tests/ui/extern/extern-with-type-bounds.rs +++ b/tests/ui/extern/extern-with-type-bounds.rs @@ -1,4 +1,4 @@ -#![feature(intrinsics)] +#![feature(intrinsics, rustc_attrs)] extern "rust-intrinsic" { // Real example from libcore diff --git a/tests/ui/intrinsics/feature-gate-safe-intrinsic.rs b/tests/ui/intrinsics/feature-gate-safe-intrinsic.rs new file mode 100644 index 0000000000000..ffaa4d771d99b --- /dev/null +++ b/tests/ui/intrinsics/feature-gate-safe-intrinsic.rs @@ -0,0 +1,6 @@ +#[rustc_safe_intrinsic] +//~^ ERROR the `#[rustc_safe_intrinsic]` attribute is used internally to mark intrinsics as safe +//~| ERROR attribute should be applied to intrinsic functions +fn safe() {} + +fn main() {} diff --git a/tests/ui/intrinsics/feature-gate-safe-intrinsic.stderr b/tests/ui/intrinsics/feature-gate-safe-intrinsic.stderr new file mode 100644 index 0000000000000..8aeb56598ec1c --- /dev/null +++ b/tests/ui/intrinsics/feature-gate-safe-intrinsic.stderr @@ -0,0 +1,20 @@ +error[E0658]: the `#[rustc_safe_intrinsic]` attribute is used internally to mark intrinsics as safe + --> $DIR/feature-gate-safe-intrinsic.rs:1:1 + | +LL | #[rustc_safe_intrinsic] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + +error: attribute should be applied to intrinsic functions + --> $DIR/feature-gate-safe-intrinsic.rs:1:1 + | +LL | #[rustc_safe_intrinsic] + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn safe() {} + | ------------ not an intrinsic function + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/intrinsics/intrinsic-alignment.rs b/tests/ui/intrinsics/intrinsic-alignment.rs index 30b8a21269a9c..6f9df64417e8e 100644 --- a/tests/ui/intrinsics/intrinsic-alignment.rs +++ b/tests/ui/intrinsics/intrinsic-alignment.rs @@ -1,7 +1,7 @@ // run-pass // ignore-wasm32-bare seems not important to test here -#![feature(intrinsics)] +#![feature(intrinsics, rustc_attrs)] mod rusti { extern "rust-intrinsic" { diff --git a/tests/ui/repr/16-bit-repr-c-enum.rs b/tests/ui/repr/16-bit-repr-c-enum.rs index 2acfde4be46d9..d4fea2b192b48 100644 --- a/tests/ui/repr/16-bit-repr-c-enum.rs +++ b/tests/ui/repr/16-bit-repr-c-enum.rs @@ -5,7 +5,7 @@ // [avr] compile-flags: --target=avr-unknown-gnu-atmega328 --crate-type=rlib // [msp430] needs-llvm-components: msp430 // [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib -#![feature(no_core, lang_items, intrinsics, staged_api)] +#![feature(no_core, lang_items, intrinsics, staged_api, rustc_attrs)] #![no_core] #![crate_type = "lib"] #![stable(feature = "", since = "")] diff --git a/tests/ui/structs-enums/rec-align-u32.rs b/tests/ui/structs-enums/rec-align-u32.rs index ee704198d193b..b3c323d2a29f5 100644 --- a/tests/ui/structs-enums/rec-align-u32.rs +++ b/tests/ui/structs-enums/rec-align-u32.rs @@ -3,7 +3,7 @@ #![allow(unused_unsafe)] // Issue #2303 -#![feature(intrinsics)] +#![feature(intrinsics, rustc_attrs)] use std::mem; diff --git a/tests/ui/structs-enums/rec-align-u64.rs b/tests/ui/structs-enums/rec-align-u64.rs index c3b201fb1c66d..de008bcc01d29 100644 --- a/tests/ui/structs-enums/rec-align-u64.rs +++ b/tests/ui/structs-enums/rec-align-u64.rs @@ -5,7 +5,7 @@ // Issue #2303 -#![feature(intrinsics)] +#![feature(intrinsics, rustc_attrs)] use std::mem; From 2c507cae363a71f16069bbf602aa50e00f3ceec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Mon, 25 Sep 2023 03:27:25 +0200 Subject: [PATCH 138/149] Rename `cold_path` to `outline` --- compiler/rustc_arena/src/lib.rs | 5 +++-- compiler/rustc_data_structures/src/lib.rs | 3 ++- compiler/rustc_data_structures/src/profiling.rs | 4 ++-- compiler/rustc_data_structures/src/sync/worker_local.rs | 8 ++------ compiler/rustc_query_system/src/query/plumbing.rs | 4 ++-- compiler/rustc_span/src/lib.rs | 4 ++-- 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index f14463fe94029..23fdd272ffd33 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -37,9 +37,10 @@ use std::ptr::{self, NonNull}; use std::slice; use std::{cmp, intrinsics}; +/// This calls the passed function while ensuring it won't be inlined into the caller. #[inline(never)] #[cold] -fn cold_path R, R>(f: F) -> R { +fn outline R, R>(f: F) -> R { f() } @@ -600,7 +601,7 @@ impl DroplessArena { unsafe { self.write_from_iter(iter, len, mem) } } (_, _) => { - cold_path(move || -> &mut [T] { + outline(move || -> &mut [T] { let mut vec: SmallVec<[_; 8]> = iter.collect(); if vec.is_empty() { return &mut []; diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 7d037ddfa986c..461ec3a90ed97 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -51,9 +51,10 @@ use std::fmt; pub use rustc_index::static_assert_size; +/// This calls the passed function while ensuring it won't be inlined into the caller. #[inline(never)] #[cold] -pub fn cold_path R, R>(f: F) -> R { +pub fn outline R, R>(f: F) -> R { f() } diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 3c76c2b79911a..e688feb5fe176 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -81,8 +81,8 @@ //! //! [mm]: https://github.com/rust-lang/measureme/ -use crate::cold_path; use crate::fx::FxHashMap; +use crate::outline; use std::borrow::Borrow; use std::collections::hash_map::Entry; @@ -697,7 +697,7 @@ impl<'a> TimingGuard<'a> { #[inline] pub fn finish_with_query_invocation_id(self, query_invocation_id: QueryInvocationId) { if let Some(guard) = self.0 { - cold_path(|| { + outline(|| { let event_id = StringId::new_virtual(query_invocation_id.0); let event_id = EventId::from_virtual(event_id); guard.finish_with_override_event_id(event_id); diff --git a/compiler/rustc_data_structures/src/sync/worker_local.rs b/compiler/rustc_data_structures/src/sync/worker_local.rs index 1f838cc4648dc..ffafdba13cea1 100644 --- a/compiler/rustc_data_structures/src/sync/worker_local.rs +++ b/compiler/rustc_data_structures/src/sync/worker_local.rs @@ -6,7 +6,7 @@ use std::ptr; use std::sync::Arc; #[cfg(parallel_compiler)] -use {crate::cold_path, crate::sync::CacheAligned}; +use {crate::outline, crate::sync::CacheAligned}; /// A pointer to the `RegistryData` which uniquely identifies a registry. /// This identifier can be reused if the registry gets freed. @@ -25,11 +25,7 @@ impl RegistryId { fn verify(self) -> usize { let (id, index) = THREAD_DATA.with(|data| (data.registry_id.get(), data.index.get())); - if id == self { - index - } else { - cold_path(|| panic!("Unable to verify registry association")) - } + if id == self { index } else { outline(|| panic!("Unable to verify registry association")) } } } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index f93edffca79e3..ae8414ebba6d7 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -18,7 +18,7 @@ use rustc_data_structures::sharded::Sharded; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lock; #[cfg(parallel_compiler)] -use rustc_data_structures::{cold_path, sync}; +use rustc_data_structures::{outline, sync}; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError}; use rustc_span::{Span, DUMMY_SP}; use std::cell::Cell; @@ -265,7 +265,7 @@ where match result { Ok(()) => { let Some((v, index)) = query.query_cache(qcx).lookup(&key) else { - cold_path(|| { + outline(|| { // We didn't find the query result in the query cache. Check if it was // poisoned due to a panic instead. let lock = query.query_state(qcx).active.get_shard_by_value(&key).lock(); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 68724c480376a..c58fdbcb5e1b6 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -33,7 +33,7 @@ extern crate rustc_macros; #[macro_use] extern crate tracing; -use rustc_data_structures::{cold_path, AtomicRef}; +use rustc_data_structures::{outline, AtomicRef}; use rustc_macros::HashStable_Generic; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -1592,7 +1592,7 @@ impl SourceFile { return &lines[..]; } - cold_path(|| { + outline(|| { self.convert_diffs_to_lines_frozen(); if let Some(SourceFileLines::Lines(lines)) = self.lines.get() { return &lines[..]; From 3747ef5d6f66525fd42d38b068fd81f163adb482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 25 Sep 2023 21:57:22 +0000 Subject: [PATCH 139/149] Handle all arbitrary `loop` nesting in `break` type errors --- compiler/rustc_hir_typeck/src/demand.rs | 27 ++++++-- tests/ui/loops/loop-break-value.rs | 36 +++++++++++ tests/ui/loops/loop-break-value.stderr | 82 ++++++++++++++++++++++++- 3 files changed, 137 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 0ff4bd4f5e4fa..31e773585d196 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -583,7 +583,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // loop, so we need to account for that. direct = !direct; } - if let hir::ExprKind::Loop(_, label, _, span) = parent.kind + if let hir::ExprKind::Loop(block, label, _, span) = parent.kind && (destination.label == label || direct) { if let Some((reason_span, message)) = @@ -594,25 +594,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, format!("this loop is expected to be of type `{expected}`"), ); + break 'outer; } else { // Locate all other `break` statements within the same `loop` that might // have affected inference. struct FindBreaks<'tcx> { label: Option, uses: Vec<&'tcx hir::Expr<'tcx>>, + nest_depth: usize, } impl<'tcx> Visitor<'tcx> for FindBreaks<'tcx> { fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { + let nest_depth = self.nest_depth; + if let hir::ExprKind::Loop(_, label, _, _) = ex.kind { + if label == self.label { + // Account for `'a: loop { 'a: loop {...} }`. + return; + } + self.nest_depth += 1; + } if let hir::ExprKind::Break(destination, _) = ex.kind - && self.label == destination.label + && (self.label == destination.label + // Account for `loop { 'a: loop { loop { break; } } }`. + || destination.label.is_none() && self.nest_depth == 0) { self.uses.push(ex); } hir::intravisit::walk_expr(self, ex); + self.nest_depth = nest_depth; } } - let mut expr_finder = FindBreaks { label, uses: vec![] }; - expr_finder.visit_expr(parent); + let mut expr_finder = FindBreaks { label, uses: vec![], nest_depth: 0 }; + expr_finder.visit_block(block); + let mut exit = false; for ex in expr_finder.uses { let hir::ExprKind::Break(_, val) = ex.kind else { continue; @@ -631,10 +645,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ex.span, format!("expected because of this `break`"), ); + exit = true; } } + if exit { + break 'outer; + } } - break 'outer; } } } diff --git a/tests/ui/loops/loop-break-value.rs b/tests/ui/loops/loop-break-value.rs index e38a5aa29eb4e..c35200520cb5d 100644 --- a/tests/ui/loops/loop-break-value.rs +++ b/tests/ui/loops/loop-break-value.rs @@ -107,6 +107,7 @@ fn main() { } break; //~ ERROR mismatched types }; + let _ = 'a: loop { loop { break; // This doesn't affect the expected break type of the 'a loop @@ -119,6 +120,41 @@ fn main() { break 'a; //~ ERROR mismatched types }; + loop { + break; + let _ = loop { + break 2; + loop { + break; + } + }; + break 2; //~ ERROR mismatched types + } + + 'a: loop { + break; + let _ = 'a: loop { + //~^ WARNING label name `'a` shadows a label name that is already in scope + break 2; + loop { + break 'a; //~ ERROR mismatched types + } + }; + break 2; //~ ERROR mismatched types + } + + 'a: loop { + break; + let _ = 'a: loop { + //~^ WARNING label name `'a` shadows a label name that is already in scope + break 'a 2; + loop { + break 'a; //~ ERROR mismatched types + } + }; + break 2; //~ ERROR mismatched types + }; + loop { // point at the return type break 2; //~ ERROR mismatched types } diff --git a/tests/ui/loops/loop-break-value.stderr b/tests/ui/loops/loop-break-value.stderr index 39d75ad1490c4..4c312c2fe3807 100644 --- a/tests/ui/loops/loop-break-value.stderr +++ b/tests/ui/loops/loop-break-value.stderr @@ -1,3 +1,21 @@ +warning: label name `'a` shadows a label name that is already in scope + --> $DIR/loop-break-value.rs:136:17 + | +LL | 'a: loop { + | -- first declared here +LL | break; +LL | let _ = 'a: loop { + | ^^ label `'a` already in scope + +warning: label name `'a` shadows a label name that is already in scope + --> $DIR/loop-break-value.rs:148:17 + | +LL | 'a: loop { + | -- first declared here +LL | break; +LL | let _ = 'a: loop { + | ^^ label `'a` already in scope + error[E0425]: cannot find value `LOOP` in this scope --> $DIR/loop-break-value.rs:95:15 | @@ -164,12 +182,19 @@ LL | break "asdf"; error[E0308]: mismatched types --> $DIR/loop-break-value.rs:21:31 | +LL | let _: i32 = 'outer_loop: loop { + | - ---- this loop is expected to be of type `i32` + | | + | expected because of this assignment +LL | loop { LL | break 'outer_loop "nope"; | ^^^^^^ expected `i32`, found `&str` error[E0308]: mismatched types --> $DIR/loop-break-value.rs:73:26 | +LL | break; + | ----- expected because of this `break` LL | break 'c 123; | ^^^ expected `()`, found integer @@ -218,7 +243,7 @@ LL | break; | help: give it a value of the expected type: `break value` error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:119:9 + --> $DIR/loop-break-value.rs:120:9 | LL | break 'a 1; | ---------- expected because of this `break` @@ -230,7 +255,58 @@ LL | break 'a; | help: give it a value of the expected type: `break 'a value` error[E0308]: mismatched types - --> $DIR/loop-break-value.rs:123:15 + --> $DIR/loop-break-value.rs:131:15 + | +LL | break; + | ----- expected because of this `break` +... +LL | break 2; + | ^ expected `()`, found integer + +error[E0308]: mismatched types + --> $DIR/loop-break-value.rs:140:17 + | +LL | break 2; + | ------- expected because of this `break` +LL | loop { +LL | break 'a; + | ^^^^^^^^ + | | + | expected integer, found `()` + | help: give it a value of the expected type: `break 'a value` + +error[E0308]: mismatched types + --> $DIR/loop-break-value.rs:143:15 + | +LL | break; + | ----- expected because of this `break` +... +LL | break 2; + | ^ expected `()`, found integer + +error[E0308]: mismatched types + --> $DIR/loop-break-value.rs:152:17 + | +LL | break 'a 2; + | ---------- expected because of this `break` +LL | loop { +LL | break 'a; + | ^^^^^^^^ + | | + | expected integer, found `()` + | help: give it a value of the expected type: `break 'a value` + +error[E0308]: mismatched types + --> $DIR/loop-break-value.rs:155:15 + | +LL | break; + | ----- expected because of this `break` +... +LL | break 2; + | ^ expected `()`, found integer + +error[E0308]: mismatched types + --> $DIR/loop-break-value.rs:159:15 | LL | fn main() { | - expected `()` because of this return type @@ -240,7 +316,7 @@ LL | loop { // point at the return type LL | break 2; | ^ expected `()`, found integer -error: aborting due to 20 previous errors; 1 warning emitted +error: aborting due to 25 previous errors; 3 warnings emitted Some errors have detailed explanations: E0308, E0425, E0571. For more information about an error, try `rustc --explain E0308`. From ddb3b7e70a6252a029059bbecfafd087d0415e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 25 Sep 2023 22:10:08 +0000 Subject: [PATCH 140/149] Use verbose suggestion for `break` without value --- compiler/rustc_hir_typeck/src/expr.rs | 12 ++--- tests/ui/issues/issue-27042.stderr | 10 ++-- tests/ui/loops/loop-break-value.stderr | 50 +++++++++++-------- .../ui/loops/loop-labeled-break-value.stderr | 30 ++++++----- .../ui/loops/loop-properly-diverging-2.stderr | 10 ++-- tests/ui/never_type/issue-52443.stderr | 10 ++-- tests/ui/type/type-error-break-tail.stderr | 10 ++-- 7 files changed, 76 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 5fc8b1f9fbe50..eead4da5e3ef8 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -670,14 +670,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty })); self.annotate_loop_expected_due_to_inference(&mut err, expr, error); if let Some(val) = ty_kind_suggestion(ty) { - let label = destination - .label - .map(|l| format!(" {}", l.ident)) - .unwrap_or_else(String::new); - err.span_suggestion( - expr.span, + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), "give it a value of the expected type", - format!("break{label} {val}"), + format!(" {val}"), Applicability::HasPlaceholders, ); } @@ -722,7 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ... except when we try to 'break rust;'. // ICE this expression in particular (see #43162). if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind { - if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust { + if let [segment] = path.segments && segment.ident.name == sym::rust { fatally_break_rust(self.tcx); } } diff --git a/tests/ui/issues/issue-27042.stderr b/tests/ui/issues/issue-27042.stderr index 59ef28481d0e6..69eedeb8025a6 100644 --- a/tests/ui/issues/issue-27042.stderr +++ b/tests/ui/issues/issue-27042.stderr @@ -12,10 +12,12 @@ error[E0308]: mismatched types --> $DIR/issue-27042.rs:6:16 | LL | loop { break }; - | ^^^^^ - | | - | expected `i32`, found `()` - | help: give it a value of the expected type: `break 42` + | ^^^^^ expected `i32`, found `()` + | +help: give it a value of the expected type + | +LL | loop { break 42 }; + | ++ error[E0308]: mismatched types --> $DIR/issue-27042.rs:8:9 diff --git a/tests/ui/loops/loop-break-value.stderr b/tests/ui/loops/loop-break-value.stderr index 4c312c2fe3807..6c83bc7575c37 100644 --- a/tests/ui/loops/loop-break-value.stderr +++ b/tests/ui/loops/loop-break-value.stderr @@ -225,10 +225,12 @@ error[E0308]: mismatched types LL | break 2; | ------- expected because of this `break` LL | break; - | ^^^^^ - | | - | expected integer, found `()` - | help: give it a value of the expected type: `break value` + | ^^^^^ expected integer, found `()` + | +help: give it a value of the expected type + | +LL | break value; + | +++++ error[E0308]: mismatched types --> $DIR/loop-break-value.rs:108:9 @@ -237,10 +239,12 @@ LL | break 'a 1; | ---------- expected because of this `break` ... LL | break; - | ^^^^^ - | | - | expected integer, found `()` - | help: give it a value of the expected type: `break value` + | ^^^^^ expected integer, found `()` + | +help: give it a value of the expected type + | +LL | break value; + | +++++ error[E0308]: mismatched types --> $DIR/loop-break-value.rs:120:9 @@ -249,10 +253,12 @@ LL | break 'a 1; | ---------- expected because of this `break` ... LL | break 'a; - | ^^^^^^^^ - | | - | expected integer, found `()` - | help: give it a value of the expected type: `break 'a value` + | ^^^^^^^^ expected integer, found `()` + | +help: give it a value of the expected type + | +LL | break 'a value; + | +++++ error[E0308]: mismatched types --> $DIR/loop-break-value.rs:131:15 @@ -270,10 +276,12 @@ LL | break 2; | ------- expected because of this `break` LL | loop { LL | break 'a; - | ^^^^^^^^ - | | - | expected integer, found `()` - | help: give it a value of the expected type: `break 'a value` + | ^^^^^^^^ expected integer, found `()` + | +help: give it a value of the expected type + | +LL | break 'a value; + | +++++ error[E0308]: mismatched types --> $DIR/loop-break-value.rs:143:15 @@ -291,10 +299,12 @@ LL | break 'a 2; | ---------- expected because of this `break` LL | loop { LL | break 'a; - | ^^^^^^^^ - | | - | expected integer, found `()` - | help: give it a value of the expected type: `break 'a value` + | ^^^^^^^^ expected integer, found `()` + | +help: give it a value of the expected type + | +LL | break 'a value; + | +++++ error[E0308]: mismatched types --> $DIR/loop-break-value.rs:155:15 diff --git a/tests/ui/loops/loop-labeled-break-value.stderr b/tests/ui/loops/loop-labeled-break-value.stderr index aa04d330f25d7..f233670e8c4c1 100644 --- a/tests/ui/loops/loop-labeled-break-value.stderr +++ b/tests/ui/loops/loop-labeled-break-value.stderr @@ -2,28 +2,34 @@ error[E0308]: mismatched types --> $DIR/loop-labeled-break-value.rs:3:29 | LL | let _: i32 = loop { break }; - | ^^^^^ - | | - | expected `i32`, found `()` - | help: give it a value of the expected type: `break 42` + | ^^^^^ expected `i32`, found `()` + | +help: give it a value of the expected type + | +LL | let _: i32 = loop { break 42 }; + | ++ error[E0308]: mismatched types --> $DIR/loop-labeled-break-value.rs:6:37 | LL | let _: i32 = 'inner: loop { break 'inner }; - | ^^^^^^^^^^^^ - | | - | expected `i32`, found `()` - | help: give it a value of the expected type: `break 'inner 42` + | ^^^^^^^^^^^^ expected `i32`, found `()` + | +help: give it a value of the expected type + | +LL | let _: i32 = 'inner: loop { break 'inner 42 }; + | ++ error[E0308]: mismatched types --> $DIR/loop-labeled-break-value.rs:9:45 | LL | let _: i32 = 'inner2: loop { loop { break 'inner2 } }; - | ^^^^^^^^^^^^^ - | | - | expected `i32`, found `()` - | help: give it a value of the expected type: `break 'inner2 42` + | ^^^^^^^^^^^^^ expected `i32`, found `()` + | +help: give it a value of the expected type + | +LL | let _: i32 = 'inner2: loop { loop { break 'inner2 42 } }; + | ++ error: aborting due to 3 previous errors diff --git a/tests/ui/loops/loop-properly-diverging-2.stderr b/tests/ui/loops/loop-properly-diverging-2.stderr index 5030a2935b9d7..dc60657ee142b 100644 --- a/tests/ui/loops/loop-properly-diverging-2.stderr +++ b/tests/ui/loops/loop-properly-diverging-2.stderr @@ -2,10 +2,12 @@ error[E0308]: mismatched types --> $DIR/loop-properly-diverging-2.rs:2:23 | LL | let x: i32 = loop { break }; - | ^^^^^ - | | - | expected `i32`, found `()` - | help: give it a value of the expected type: `break 42` + | ^^^^^ expected `i32`, found `()` + | +help: give it a value of the expected type + | +LL | let x: i32 = loop { break 42 }; + | ++ error: aborting due to previous error diff --git a/tests/ui/never_type/issue-52443.stderr b/tests/ui/never_type/issue-52443.stderr index 99dfce8690343..59292ed68a306 100644 --- a/tests/ui/never_type/issue-52443.stderr +++ b/tests/ui/never_type/issue-52443.stderr @@ -33,10 +33,12 @@ error[E0308]: mismatched types --> $DIR/issue-52443.rs:4:17 | LL | [(); loop { break }]; - | ^^^^^ - | | - | expected `usize`, found `()` - | help: give it a value of the expected type: `break 42` + | ^^^^^ expected `usize`, found `()` + | +help: give it a value of the expected type + | +LL | [(); loop { break 42 }]; + | ++ error[E0015]: cannot convert `RangeFrom` into an iterator in constants --> $DIR/issue-52443.rs:9:21 diff --git a/tests/ui/type/type-error-break-tail.stderr b/tests/ui/type/type-error-break-tail.stderr index 4bae046de86ea..9a02bc28752ae 100644 --- a/tests/ui/type/type-error-break-tail.stderr +++ b/tests/ui/type/type-error-break-tail.stderr @@ -6,10 +6,12 @@ LL | fn loop_ending() -> i32 { LL | loop { | ---- this loop is expected to be of type `i32` LL | if false { break; } - | ^^^^^ - | | - | expected `i32`, found `()` - | help: give it a value of the expected type: `break 42` + | ^^^^^ expected `i32`, found `()` + | +help: give it a value of the expected type + | +LL | if false { break 42; } + | ++ error: aborting due to previous error From d6ce9ce115e8d0859e378b411966905c8b815845 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 26 Sep 2023 02:15:32 +0000 Subject: [PATCH 141/149] Don't store lazyness in DefKind --- .../src/diagnostics/region_name.rs | 2 +- compiler/rustc_hir/src/def.rs | 10 ++-- compiler/rustc_hir/src/target.rs | 2 +- .../rustc_hir_analysis/src/astconv/mod.rs | 17 ++---- .../rustc_hir_analysis/src/check/check.rs | 2 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 10 +--- compiler/rustc_hir_analysis/src/collect.rs | 1 + .../src/collect/resolve_bound_vars.rs | 4 +- .../rustc_hir_analysis/src/collect/type_of.rs | 22 ++++++++ .../src/variance/constraints.rs | 10 ++-- .../rustc_hir_analysis/src/variance/mod.rs | 6 +- .../rustc_hir_analysis/src/variance/terms.rs | 6 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 5 +- .../src/mem_categorization.rs | 5 +- .../infer/error_reporting/need_type_info.rs | 4 +- compiler/rustc_lint/src/builtin.rs | 6 +- .../src/rmeta/decoder/cstore_impl.rs | 1 + compiler/rustc_metadata/src/rmeta/encoder.rs | 30 +++++----- compiler/rustc_metadata/src/rmeta/mod.rs | 1 + compiler/rustc_metadata/src/rmeta/table.rs | 3 +- compiler/rustc_middle/src/hir/map/mod.rs | 4 +- compiler/rustc_middle/src/query/mod.rs | 10 +++- compiler/rustc_middle/src/ty/adt.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 2 +- compiler/rustc_middle/src/ty/diagnostics.rs | 4 +- compiler/rustc_middle/src/ty/print/pretty.rs | 6 +- compiler/rustc_middle/src/ty/sty.rs | 4 +- compiler/rustc_middle/src/ty/util.rs | 2 +- compiler/rustc_middle/src/values.rs | 2 +- .../rustc_mir_build/src/thir/pattern/mod.rs | 2 +- .../rustc_monomorphize/src/polymorphize.rs | 2 +- compiler/rustc_passes/src/abi_test.rs | 2 +- compiler/rustc_passes/src/dead.rs | 4 +- compiler/rustc_passes/src/layout_test.rs | 2 +- compiler/rustc_privacy/src/lib.rs | 6 +- compiler/rustc_query_system/src/query/job.rs | 4 +- .../rustc_resolve/src/build_reduced_graph.rs | 7 +-- compiler/rustc_resolve/src/late.rs | 6 +- .../rustc_resolve/src/late/diagnostics.rs | 4 +- .../src/solve/project_goals.rs | 2 +- compiler/rustc_ty_utils/src/implied_bounds.rs | 4 +- compiler/rustc_ty_utils/src/opaque_types.rs | 8 +-- src/librustdoc/clean/inline.rs | 2 +- src/librustdoc/clean/mod.rs | 6 +- src/librustdoc/formats/item_type.rs | 2 +- .../passes/collect_intra_doc_links.rs | 2 +- .../clippy_lints/src/init_numbered_fields.rs | 2 +- src/tools/clippy/clippy_utils/src/lib.rs | 2 +- .../clippy_utils/src/ty/type_certainty/mod.rs | 2 +- .../type-alias-bounds-are-enforced.rs | 8 +-- .../type-alias-bounds-are-enforced.stderr | 55 ------------------- 51 files changed, 128 insertions(+), 189 deletions(-) delete mode 100644 tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 30b47d076e27e..56437fba16771 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -516,7 +516,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { // be the same as those of the ADT. // FIXME: We should be able to do something similar to // match_adt_and_segment in this case. - Res::Def(DefKind::TyAlias { .. }, _) => (), + Res::Def(DefKind::TyAlias, _) => (), _ => { if let Some(last_segment) = path.segments.last() { if let Some(highlight) = self.match_adt_and_segment( diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 64271309664ec..3a4eb90f7f963 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -61,9 +61,7 @@ pub enum DefKind { Variant, Trait, /// Type alias: `type Foo = Bar;` - TyAlias { - lazy: bool, - }, + TyAlias, /// Type from an `extern` block. ForeignTy, /// Trait alias: `trait IntIterator = Iterator;` @@ -143,7 +141,7 @@ impl DefKind { DefKind::Ctor(CtorOf::Struct, CtorKind::Fn) => "tuple struct", DefKind::Ctor(CtorOf::Struct, CtorKind::Const) => "unit struct", DefKind::OpaqueTy => "opaque type", - DefKind::TyAlias { .. } => "type alias", + DefKind::TyAlias => "type alias", DefKind::TraitAlias => "trait alias", DefKind::AssocTy => "associated type", DefKind::Union => "union", @@ -199,7 +197,7 @@ impl DefKind { | DefKind::Variant | DefKind::Trait | DefKind::OpaqueTy - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy @@ -250,7 +248,7 @@ impl DefKind { | DefKind::Enum | DefKind::Variant | DefKind::Trait - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 644c4d8265dd5..0d65ddb5642af 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -101,7 +101,7 @@ impl Target { DefKind::Mod => Target::Mod, DefKind::ForeignMod => Target::ForeignMod, DefKind::GlobalAsm => Target::GlobalAsm, - DefKind::TyAlias { .. } => Target::TyAlias, + DefKind::TyAlias => Target::TyAlias, DefKind::OpaqueTy => Target::OpaqueTy, DefKind::Enum => Target::Enum, DefKind::Struct => Target::Struct, diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 106cd223d302e..e35d47f16d82a 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -909,23 +909,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let tcx = self.tcx(); let args = self.ast_path_args_for_ty(span, did, item_segment); - if let DefKind::TyAlias { lazy: true } = tcx.def_kind(did) { + if let DefKind::TyAlias = tcx.def_kind(did) + && tcx.type_alias_is_lazy(did) + { // Type aliases defined in crates that have the // feature `lazy_type_alias` enabled get encoded as a type alias that normalization will // then actually instantiate the where bounds of. let alias_ty = tcx.mk_alias_ty(did, args); Ty::new_alias(tcx, ty::Weak, alias_ty) } else { - let ty = tcx.at(span).type_of(did); - if ty.skip_binder().has_opaque_types() { - // Type aliases referring to types that contain opaque types (but aren't just directly - // referencing a single opaque type) get encoded as a type alias that normalization will - // then actually instantiate the where bounds of. - let alias_ty = tcx.mk_alias_ty(did, args); - Ty::new_alias(tcx, ty::Weak, alias_ty) - } else { - ty.instantiate(tcx, args) - } + tcx.at(span).type_of(did).instantiate(tcx, args) } } @@ -2164,7 +2157,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } Res::Def( DefKind::Enum - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::Struct | DefKind::Union | DefKind::ForeignTy, diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 564206f9e4bec..eb1f48d23ff77 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -640,7 +640,7 @@ fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) { check_opaque(tcx, id); } } - DefKind::TyAlias { .. } => { + DefKind::TyAlias => { let pty_ty = tcx.type_of(id.owner_id).instantiate_identity(); let generics = tcx.generics_of(id.owner_id); check_type_params_are_used(tcx, &generics, pty_ty); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 00a684f296325..b7b162ce27bc8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -246,9 +246,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { // `ForeignItem`s are handled separately. hir::ItemKind::ForeignMod { .. } => {} hir::ItemKind::TyAlias(hir_ty, ast_generics) => { - if tcx.features().lazy_type_alias - || tcx.type_of(item.owner_id).skip_binder().has_opaque_types() - { + if tcx.type_alias_is_lazy(item.owner_id) { // Bounds of lazy type aliases and of eager ones that contain opaque types are respected. // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`. check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow); @@ -1711,10 +1709,8 @@ fn check_variances_for_type_defn<'tcx>( } } ItemKind::TyAlias(..) => { - let ty = tcx.type_of(item.owner_id).instantiate_identity(); - - if tcx.features().lazy_type_alias || ty.has_opaque_types() { - if ty.references_error() { + if tcx.type_alias_is_lazy(item.owner_id) { + if tcx.type_of(item.owner_id).skip_binder().references_error() { return; } } else { diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 01e40c62a8bff..cd37221ae6f80 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -58,6 +58,7 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { type_of: type_of::type_of, type_of_opaque: type_of::type_of_opaque, + type_alias_is_lazy: type_of::type_alias_is_lazy, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, generics_of: generics_of::generics_of, diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index d9207f2de52c0..eb4466449a09f 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1519,7 +1519,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { DefKind::Struct | DefKind::Union | DefKind::Enum - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::Trait, def_id, ) if depth == 0 => Some(def_id), @@ -2029,7 +2029,7 @@ fn is_late_bound_map( hir::TyKind::Path(hir::QPath::Resolved( None, - hir::Path { res: Res::Def(DefKind::TyAlias { .. }, alias_def), segments, span }, + hir::Path { res: Res::Def(DefKind::TyAlias, alias_def), segments, span }, )) => { // See comments on `ConstrainedCollectorPostAstConv` for why this arm does not just consider // args to be unconstrained. diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index d12337687e2e1..ae62119b1825f 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -623,3 +623,25 @@ fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) { .emit(); } } + +pub fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool { + use hir::intravisit::Visitor; + if tcx.features().lazy_type_alias { + return true; + } + struct HasTait { + has_type_alias_impl_trait: bool, + } + impl<'tcx> Visitor<'tcx> for HasTait { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { + if let hir::TyKind::OpaqueDef(..) = t.kind { + self.has_type_alias_impl_trait = true; + } else { + hir::intravisit::walk_ty(self, t); + } + } + } + let mut has_tait = HasTait { has_type_alias_impl_trait: false }; + has_tait.visit_ty(tcx.hir().expect_item(def_id).expect_ty_alias().0); + has_tait.has_type_alias_impl_trait +} diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 9aac7160f2efb..61d9c989e2fd9 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -6,7 +6,7 @@ use hir::def_id::{DefId, LocalDefId}; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; use super::terms::VarianceTerm::*; @@ -78,9 +78,7 @@ pub fn add_constraints_from_crate<'a, 'tcx>( } } DefKind::Fn | DefKind::AssocFn => constraint_cx.build_constraints_for_item(def_id), - DefKind::TyAlias { lazy } - if lazy || tcx.type_of(def_id).instantiate_identity().has_opaque_types() => - { + DefKind::TyAlias if tcx.type_alias_is_lazy(def_id) => { constraint_cx.build_constraints_for_item(def_id) } _ => {} @@ -110,8 +108,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { // The type as returned by `type_of` is the underlying type and generally not a weak projection. // Therefore we need to check the `DefKind` first. - if let DefKind::TyAlias { lazy } = tcx.def_kind(def_id) - && (lazy || ty.has_opaque_types()) + if let DefKind::TyAlias = tcx.def_kind(def_id) + && tcx.type_alias_is_lazy(def_id) { self.add_constraints_from_ty(current_item, ty, self.covariant); return; diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index d69d7ff904acc..85e0000ab474e 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -8,7 +8,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, CrateVariancesMap, GenericArgsRef, Ty, TyCtxt}; -use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable}; use std::ops::ControlFlow; /// Defines the `TermsContext` basically houses an arena where we can @@ -56,9 +56,7 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] { let crate_map = tcx.crate_variances(()); return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]); } - DefKind::TyAlias { lazy } - if lazy || tcx.type_of(item_def_id).instantiate_identity().has_opaque_types() => - { + DefKind::TyAlias if tcx.type_alias_is_lazy(item_def_id) => { // These are inferred. let crate_map = tcx.crate_variances(()); return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]); diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index 1a8ec5f085333..275df24956ccc 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -12,7 +12,7 @@ use rustc_arena::DroplessArena; use rustc_hir::def::DefKind; use rustc_hir::def_id::{LocalDefId, LocalDefIdMap}; -use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, TyCtxt}; use std::fmt; use self::VarianceTerm::*; @@ -97,9 +97,7 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>( } } DefKind::Fn | DefKind::AssocFn => terms_cx.add_inferreds_for_item(def_id), - DefKind::TyAlias { lazy } - if lazy || tcx.type_of(def_id).instantiate_identity().has_opaque_types() => - { + DefKind::TyAlias if tcx.type_alias_is_lazy(def_id) => { terms_cx.add_inferreds_for_item(def_id) } _ => {} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 37ea94d821e7a..c0332a48be3df 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1370,10 +1370,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => bug!("unexpected type: {:?}", ty.normalized), }, - Res::Def( - DefKind::Struct | DefKind::Union | DefKind::TyAlias { .. } | DefKind::AssocTy, - _, - ) + Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => match ty.normalized.ty_adt_def() { Some(adt) if !adt.is_enum() => { diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 7fb1dc2347e05..4ea53c7eaaeca 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -557,10 +557,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { Ok(adt_def.variant_index_with_ctor_id(variant_ctor_id)) } Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _) - | Res::Def( - DefKind::Struct | DefKind::Union | DefKind::TyAlias { .. } | DefKind::AssocTy, - _, - ) + | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | Res::SelfCtor(..) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => { diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index bcaa4091c3a3d..a9029a8cec020 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -918,7 +918,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // // See the `need_type_info/issue-103053.rs` test for // a example. - if !matches!(path.res, Res::Def(DefKind::TyAlias { .. }, _)) => { + if !matches!(path.res, Res::Def(DefKind::TyAlias, _)) => { if let Some(ty) = self.opt_node_type(expr.hir_id) && let ty::Adt(_, args) = ty.kind() { @@ -1047,7 +1047,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { ) => { if tcx.res_generics_def_id(path.res) != Some(def.did()) { match path.res { - Res::Def(DefKind::TyAlias { .. }, _) => { + Res::Def(DefKind::TyAlias, _) => { // FIXME: Ideally we should support this. For that // we have to map back from the self type to the // type alias though. That's difficult. diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 4f6b79d9aee62..536f78a73edb6 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1454,13 +1454,13 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return }; - if cx.tcx.features().lazy_type_alias { - // Bounds of lazy type aliases are respected. + // Bounds of lazy type aliases and TAITs are respected. + if cx.tcx.type_alias_is_lazy(item.owner_id) { return; } let ty = cx.tcx.type_of(item.owner_id).skip_binder(); - if ty.has_opaque_types() || ty.has_inherent_projections() { + if ty.has_inherent_projections() { // Bounds of type aliases that contain opaque types or inherent projections are respected. // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X = Type::Inherent;`. return; diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index b56fe375849f1..f27eee0d79a53 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -210,6 +210,7 @@ provide! { tcx, def_id, other, cdata, inferred_outlives_of => { table_defaulted_array } super_predicates_of => { table } type_of => { table } + type_alias_is_lazy => { cdata.root.tables.type_alias_is_lazy.get(cdata, def_id.index) } variances_of => { table } fn_sig => { table } codegen_fn_attrs => { table } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 7506fc1bb05e0..a4ba943275e4c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -31,7 +31,6 @@ use rustc_middle::query::Providers; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; -use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, AssocItemContainer, SymbolName, Ty, TyCtxt}; use rustc_middle::util::common::to_readable_str; use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; @@ -827,7 +826,7 @@ fn should_encode_span(def_kind: DefKind) -> bool { | DefKind::Enum | DefKind::Variant | DefKind::Trait - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy @@ -862,7 +861,7 @@ fn should_encode_attrs(def_kind: DefKind) -> bool { | DefKind::Enum | DefKind::Variant | DefKind::Trait - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy @@ -903,7 +902,7 @@ fn should_encode_expn_that_defined(def_kind: DefKind) -> bool { | DefKind::Variant | DefKind::Trait | DefKind::Impl { .. } => true, - DefKind::TyAlias { .. } + DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy @@ -938,7 +937,7 @@ fn should_encode_visibility(def_kind: DefKind) -> bool { | DefKind::Enum | DefKind::Variant | DefKind::Trait - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy @@ -982,7 +981,7 @@ fn should_encode_stability(def_kind: DefKind) -> bool { | DefKind::Const | DefKind::Fn | DefKind::ForeignMod - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::OpaqueTy | DefKind::Enum | DefKind::Union @@ -1092,9 +1091,7 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def | DefKind::Closure | DefKind::Generator | DefKind::ExternCrate => false, - DefKind::TyAlias { lazy } => { - lazy || tcx.type_of(def_id).instantiate_identity().has_opaque_types() - } + DefKind::TyAlias => tcx.type_alias_is_lazy(def_id), } } @@ -1105,7 +1102,7 @@ fn should_encode_generics(def_kind: DefKind) -> bool { | DefKind::Enum | DefKind::Variant | DefKind::Trait - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy @@ -1145,7 +1142,7 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> | DefKind::Fn | DefKind::Const | DefKind::Static(..) - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::ForeignTy | DefKind::Impl { .. } | DefKind::AssocFn @@ -1205,7 +1202,7 @@ fn should_encode_fn_sig(def_kind: DefKind) -> bool { | DefKind::Const | DefKind::Static(..) | DefKind::Ctor(..) - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::OpaqueTy | DefKind::ForeignTy | DefKind::Impl { .. } @@ -1246,7 +1243,7 @@ fn should_encode_constness(def_kind: DefKind) -> bool { | DefKind::AssocConst | DefKind::AnonConst | DefKind::Static(..) - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::OpaqueTy | DefKind::Impl { of_trait: false } | DefKind::ForeignTy @@ -1279,7 +1276,7 @@ fn should_encode_const(def_kind: DefKind) -> bool { | DefKind::Field | DefKind::Fn | DefKind::Static(..) - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::OpaqueTy | DefKind::ForeignTy | DefKind::Impl { .. } @@ -1451,6 +1448,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let DefKind::Macro(_) = def_kind { self.encode_info_for_macro(local_id); } + if let DefKind::TyAlias = def_kind { + self.tables + .type_alias_is_lazy + .set(def_id.index, self.tcx.type_alias_is_lazy(def_id)); + } if let DefKind::OpaqueTy = def_kind { self.encode_explicit_item_bounds(def_id); self.tables diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 09b2a1a0cb259..42764af52c43f 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -383,6 +383,7 @@ define_tables! { is_intrinsic: Table, is_macro_rules: Table, is_type_alias_impl_trait: Table, + type_alias_is_lazy: Table, attr_flags: Table, def_path_hashes: Table, explicit_item_bounds: Table, Span)>>, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index d105dab48146f..bb1320942b077 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -145,8 +145,7 @@ fixed_size_enum! { ( Enum ) ( Variant ) ( Trait ) - ( TyAlias { lazy: false } ) - ( TyAlias { lazy: true } ) + ( TyAlias ) ( ForeignTy ) ( TraitAlias ) ( AssocTy ) diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 3491c487f3e67..4af2d83e9c721 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -196,9 +196,7 @@ impl<'hir> Map<'hir> { ItemKind::Macro(_, macro_kind) => DefKind::Macro(macro_kind), ItemKind::Mod(..) => DefKind::Mod, ItemKind::OpaqueTy(..) => DefKind::OpaqueTy, - ItemKind::TyAlias(..) => { - DefKind::TyAlias { lazy: self.tcx.features().lazy_type_alias } - } + ItemKind::TyAlias(..) => DefKind::TyAlias, ItemKind::Enum(..) => DefKind::Enum, ItemKind::Struct(..) => DefKind::Struct, ItemKind::Union(..) => DefKind::Union, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 5986a8b1cd8ac..340c5a769dbc4 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -229,7 +229,7 @@ rustc_queries! { action = { use rustc_hir::def::DefKind; match tcx.def_kind(key) { - DefKind::TyAlias { .. } => "expanding type alias", + DefKind::TyAlias => "expanding type alias", DefKind::TraitAlias => "expanding trait alias", _ => "computing type of", } @@ -251,6 +251,14 @@ rustc_queries! { } } + query type_alias_is_lazy(key: DefId) -> bool { + desc { |tcx| + "computing whether `{path}` is a lazy type alias", + path = tcx.def_path_str(key), + } + separate_provide_extern + } + query collect_return_position_impl_trait_in_trait_tys(key: DefId) -> Result<&'tcx FxHashMap>>, ErrorGuaranteed> { diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 219927f5ab427..f50969dd967f8 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -448,7 +448,7 @@ impl<'tcx> AdtDef<'tcx> { Res::Def(DefKind::Ctor(..), cid) => self.variant_with_ctor_id(cid), Res::Def(DefKind::Struct, _) | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::TyAlias { .. }, _) + | Res::Def(DefKind::TyAlias, _) | Res::Def(DefKind::AssocTy, _) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a7dd6dc737da6..c06b8b2dfa081 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1114,7 +1114,7 @@ impl<'tcx> TyCtxt<'tcx> { if let Some(hir::FnDecl { output: hir::FnRetTy::Return(hir_output), .. }) = self.hir().fn_decl_by_hir_id(hir_id) && let hir::TyKind::Path(hir::QPath::Resolved( None, - hir::Path { res: hir::def::Res::Def(DefKind::TyAlias { .. }, def_id), .. }, )) = hir_output.kind + hir::Path { res: hir::def::Res::Def(DefKind::TyAlias, def_id), .. }, )) = hir_output.kind && let Some(local_id) = def_id.as_local() && let Some(alias_ty) = self.hir().get_by_def_id(local_id).alias_ty() // it is type alias && let Some(alias_generics) = self.hir().get_by_def_id(local_id).generics() diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index e7ebb985ca411..f03813a459b6a 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -493,7 +493,7 @@ impl<'tcx> TypeVisitor> for IsSuggestableVisitor<'tcx> { Alias(Opaque, AliasTy { def_id, .. }) => { let parent = self.tcx.parent(def_id); let parent_ty = self.tcx.type_of(parent).instantiate_identity(); - if let DefKind::TyAlias { .. } | DefKind::AssocTy = self.tcx.def_kind(parent) + if let DefKind::TyAlias | DefKind::AssocTy = self.tcx.def_kind(parent) && let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = *parent_ty.kind() && parent_opaque_def_id == def_id { @@ -577,7 +577,7 @@ impl<'tcx> FallibleTypeFolder> for MakeSuggestableFolder<'tcx> { Alias(Opaque, AliasTy { def_id, .. }) => { let parent = self.tcx.parent(def_id); let parent_ty = self.tcx.type_of(parent).instantiate_identity(); - if let hir::def::DefKind::TyAlias { .. } | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent) + if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent) && let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = *parent_ty.kind() && parent_opaque_def_id == def_id { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 99dc0b2607eb5..2d7350387ca78 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -360,7 +360,7 @@ pub trait PrettyPrinter<'tcx>: self.write_str(get_local_name(&self, symbol, parent, parent_key).as_str())?; self.write_str("::")?; } else if let DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::Trait - | DefKind::TyAlias { .. } | DefKind::Fn | DefKind::Const | DefKind::Static(_) = kind + | DefKind::TyAlias | DefKind::Fn | DefKind::Const | DefKind::Static(_) = kind { } else { // If not covered above, like for example items out of `impl` blocks, fallback. @@ -766,7 +766,7 @@ pub trait PrettyPrinter<'tcx>: let parent = self.tcx().parent(def_id); match self.tcx().def_kind(parent) { - DefKind::TyAlias { .. } | DefKind::AssocTy => { + DefKind::TyAlias | DefKind::AssocTy => { // NOTE: I know we should check for NO_QUERIES here, but it's alright. // `type_of` on a type alias or assoc type should never cause a cycle. if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: d, .. }) = @@ -2998,7 +2998,7 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N match child.res { def::Res::Def(DefKind::AssocTy, _) => {} - def::Res::Def(DefKind::TyAlias { .. }, _) => {} + def::Res::Def(DefKind::TyAlias, _) => {} def::Res::Def(defkind, def_id) => { if let Some(ns) = defkind.ns() { collect_fn(&child.ident, ns, def_id); diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c823c4c2b57a0..1e57392e0e7d9 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1223,7 +1223,7 @@ impl<'tcx> AliasTy<'tcx> { DefKind::AssocTy if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(self.def_id)) => ty::Inherent, DefKind::AssocTy => ty::Projection, DefKind::OpaqueTy => ty::Opaque, - DefKind::TyAlias { .. } => ty::Weak, + DefKind::TyAlias => ty::Weak, kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), } } @@ -1959,7 +1959,7 @@ impl<'tcx> Ty<'tcx> { (kind, tcx.def_kind(alias_ty.def_id)), (ty::Opaque, DefKind::OpaqueTy) | (ty::Projection | ty::Inherent, DefKind::AssocTy) - | (ty::Weak, DefKind::TyAlias { .. }) + | (ty::Weak, DefKind::TyAlias) ); Ty::new(tcx, Alias(kind, alias_ty)) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 0dda7cd83be3b..31b52677b2797 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -156,7 +156,7 @@ impl<'tcx> TyCtxt<'tcx> { | DefKind::Enum | DefKind::Trait | DefKind::OpaqueTy - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index dec0636338076..578d8e7a975ea 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -217,7 +217,7 @@ fn find_item_ty_spans( match ty.kind { hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { if let Res::Def(kind, def_id) = path.res - && !matches!(kind, DefKind::TyAlias { .. }) { + && !matches!(kind, DefKind::TyAlias) { let check_params = def_id.as_local().map_or(true, |def_id| { if def_id == needle { spans.push(ty.span); diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index f1f75c267175a..fe47a1cd78c49 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -439,7 +439,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { DefKind::Struct | DefKind::Ctor(CtorOf::Struct, ..) | DefKind::Union - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::AssocTy, _, ) diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index 0e85f35fc1b90..6c206a6bac8bd 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -143,7 +143,7 @@ fn mark_used_by_default_parameters<'tcx>( | DefKind::Enum | DefKind::Variant | DefKind::Trait - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index 9e4d960af1412..153c39977bb44 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -21,7 +21,7 @@ pub fn test_abi(tcx: TyCtxt<'_>) { DefKind::Fn | DefKind::AssocFn => { dump_abi_of_fn_item(tcx, id, attr); } - DefKind::TyAlias { .. } => { + DefKind::TyAlias => { dump_abi_of_fn_type(tcx, id, attr); } _ => { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index d1c3bcf3839d5..493daf314ced2 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -96,7 +96,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn handle_res(&mut self, res: Res) { match res { - Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias { .. }, def_id) => { + Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => { self.check_def_id(def_id); } _ if self.in_pat => {} @@ -923,7 +923,7 @@ impl<'tcx> DeadVisitor<'tcx> { | DefKind::Fn | DefKind::Static(_) | DefKind::Const - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::Enum | DefKind::Union | DefKind::ForeignTy => self.warn_dead_code(def_id, "used"), diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index e3a63f2e00441..e195f9ab6da46 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -23,7 +23,7 @@ pub fn test_layout(tcx: TyCtxt<'_>) { for id in tcx.hir_crate_items(()).definitions() { for attr in tcx.get_attrs(id, sym::rustc_layout) { match tcx.def_kind(id) { - DefKind::TyAlias { .. } | DefKind::Enum | DefKind::Struct | DefKind::Union => { + DefKind::TyAlias | DefKind::Enum | DefKind::Struct | DefKind::Union => { dump_layout_of(tcx, id, attr); } _ => { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 2e71ac8d7dc56..ab85f680fcf9b 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -588,7 +588,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { self.update(def_id, macro_ev, Level::Reachable); match def_kind { // No type privacy, so can be directly marked as reachable. - DefKind::Const | DefKind::Static(_) | DefKind::TraitAlias | DefKind::TyAlias { .. } => { + DefKind::Const | DefKind::Static(_) | DefKind::TraitAlias | DefKind::TyAlias => { if vis.is_accessible_from(module, self.tcx) { self.update(def_id, macro_ev, Level::Reachable); } @@ -1637,8 +1637,8 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx, '_> { let def_kind = tcx.def_kind(def_id); match def_kind { - DefKind::Const | DefKind::Static(_) | DefKind::Fn | DefKind::TyAlias { .. } => { - if let DefKind::TyAlias { .. } = def_kind { + DefKind::Const | DefKind::Static(_) | DefKind::Fn | DefKind::TyAlias => { + if let DefKind::TyAlias = def_kind { self.check_unnameable(def_id, effective_vis); } self.check(def_id, item_visibility, effective_vis).generics().predicates().ty(); diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 2e9ebde296cd2..f2c1f84fccc1d 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -580,9 +580,7 @@ pub(crate) fn report_cycle<'a>( }); } - let alias = if stack - .iter() - .all(|entry| matches!(entry.query.def_kind, Some(DefKind::TyAlias { .. }))) + let alias = if stack.iter().all(|entry| matches!(entry.query.def_kind, Some(DefKind::TyAlias))) { Some(crate::error::Alias::Ty) } else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) { diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 2c36c83ae728b..a18109574feb5 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -698,10 +698,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { // These items live in the type namespace. ItemKind::TyAlias(..) => { - let res = Res::Def( - DefKind::TyAlias { lazy: self.r.tcx.features().lazy_type_alias }, - def_id, - ); + let res = Res::Def(DefKind::TyAlias, def_id); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); } @@ -950,7 +947,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { DefKind::Struct | DefKind::Union | DefKind::Variant - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::ForeignTy | DefKind::OpaqueTy | DefKind::TraitAlias diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 01f9f06059426..c3c43346ed828 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -474,7 +474,7 @@ impl<'a> PathSource<'a> { | DefKind::Enum | DefKind::Trait | DefKind::TraitAlias - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::AssocTy | DefKind::TyParam | DefKind::OpaqueTy @@ -513,7 +513,7 @@ impl<'a> PathSource<'a> { DefKind::Struct | DefKind::Union | DefKind::Variant - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::AssocTy, _, ) | Res::SelfTyParam { .. } @@ -1766,7 +1766,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { Res::Def(DefKind::Struct, def_id) | Res::Def(DefKind::Union, def_id) | Res::Def(DefKind::Enum, def_id) - | Res::Def(DefKind::TyAlias { .. }, def_id) + | Res::Def(DefKind::TyAlias, def_id) | Res::Def(DefKind::Trait, def_id) if i + 1 == proj_start => { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 06a08f29a1ee8..64440a6c04e7a 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1429,7 +1429,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { (Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => { err.span_label(span, fallback_label.to_string()); } - (Res::Def(DefKind::TyAlias { .. }, def_id), PathSource::Trait(_)) => { + (Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => { err.span_label(span, "type aliases cannot be used as traits"); if self.r.tcx.sess.is_nightly_build() { let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \ @@ -1598,7 +1598,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { err.span_label(span, fallback_label.to_string()); err.note("can't use `Self` as a constructor, you must use the implemented struct"); } - (Res::Def(DefKind::TyAlias { .. } | DefKind::AssocTy, _), _) if ns == ValueNS => { + (Res::Def(DefKind::TyAlias | DefKind::AssocTy, _), _) if ns == ValueNS => { err.note("can't use a type alias as a constructor"); } _ => return false, diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index c579da61e382a..0f9d36342ad25 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -59,7 +59,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } DefKind::AnonConst => self.normalize_anon_const(goal), DefKind::OpaqueTy => self.normalize_opaque_type(goal), - DefKind::TyAlias { .. } => self.normalize_weak_type(goal), + DefKind::TyAlias => self.normalize_weak_type(goal), kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), } } diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 436f10a4f7b91..ec2e0daaf88b2 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -119,7 +119,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' }, DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)), DefKind::OpaqueTy => match tcx.def_kind(tcx.local_parent(def_id)) { - DefKind::TyAlias { .. } => ty::List::empty(), + DefKind::TyAlias => ty::List::empty(), DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)), // Nested opaque types only occur in associated types: // ` type Opaque = impl Trait<&'static T, AssocTy = impl Nested>; ` @@ -136,7 +136,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' | DefKind::Enum | DefKind::Variant | DefKind::Trait - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::TyParam diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 38768f0a05b6d..06a30677d2005 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -53,9 +53,7 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { fn parent(&self) -> Option { match self.tcx.def_kind(self.item) { - DefKind::AnonConst | DefKind::InlineConst | DefKind::Fn | DefKind::TyAlias { .. } => { - None - } + DefKind::AnonConst | DefKind::InlineConst | DefKind::Fn | DefKind::TyAlias => None, DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { Some(self.tcx.local_parent(self.item)) } @@ -118,7 +116,7 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { #[instrument(level = "trace", skip(self))] fn visit_nested_item(&mut self, id: rustc_hir::ItemId) { let id = id.owner_id.def_id; - if let DefKind::TyAlias { .. } = self.collector.tcx.def_kind(id) { + if let DefKind::TyAlias = self.collector.tcx.def_kind(id) { let items = self.collector.tcx.opaque_types_defined_by(id); self.collector.opaques.extend(items); } @@ -297,7 +295,7 @@ fn opaque_types_defined_by<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx [ collector.collect_body_and_predicate_taits(); } // We're also doing this for `AssocTy` for the wf checks in `check_opaque_meets_bounds` - DefKind::TyAlias { .. } | DefKind::AssocTy => { + DefKind::TyAlias | DefKind::AssocTy => { tcx.type_of(item).instantiate_identity().visit_with(&mut collector); } DefKind::OpaqueTy => { diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index dd43ee0338322..fcbcfbf5c67cf 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -80,7 +80,7 @@ pub(crate) fn try_inline( build_impls(cx, did, attrs_without_docs, &mut ret); clean::UnionItem(build_union(cx, did)) } - Res::Def(DefKind::TyAlias { .. }, did) => { + Res::Def(DefKind::TyAlias, did) => { record_extern_fqn(cx, did, ItemType::TypeAlias); build_impls(cx, did, attrs_without_docs, &mut ret); clean::TypeAliasItem(build_type_alias(cx, did)) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d9ee622848dd7..34d81f51f76e6 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1758,7 +1758,7 @@ fn maybe_expand_private_type_alias<'tcx>( cx: &mut DocContext<'tcx>, path: &hir::Path<'tcx>, ) -> Option { - let Res::Def(DefKind::TyAlias { .. }, def_id) = path.res else { return None }; + let Res::Def(DefKind::TyAlias, def_id) = path.res else { return None }; // Substitute private type aliases let def_id = def_id.as_local()?; let alias = if !cx.cache.effective_visibilities.is_exported(cx.tcx, def_id.to_def_id()) @@ -2027,7 +2027,7 @@ impl<'tcx> ContainerTy<'tcx> { let (DefKind::Struct | DefKind::Union | DefKind::Enum - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::Trait) = tcx.def_kind(container) else { return ObjectLifetimeDefault::Empty; @@ -2859,7 +2859,7 @@ fn clean_impl<'tcx>( let for_ = clean_ty(impl_.self_ty, cx); let type_alias = for_.def_id(&cx.cache).and_then(|alias_def_id: DefId| match tcx.def_kind(alias_def_id) { - DefKind::TyAlias { .. } => Some(clean_middle_ty( + DefKind::TyAlias => Some(clean_middle_ty( ty::Binder::dummy(tcx.type_of(def_id).instantiate_identity()), cx, Some(def_id.to_def_id()), diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index fd79160ff4a81..be2ee7915889a 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -115,7 +115,7 @@ impl From for ItemType { DefKind::Struct => Self::Struct, DefKind::Union => Self::Union, DefKind::Trait => Self::Trait, - DefKind::TyAlias { .. } => Self::TypeAlias, + DefKind::TyAlias => Self::TypeAlias, DefKind::TraitAlias => Self::TraitAlias, DefKind::Macro(kind) => match kind { MacroKind::Bang => ItemType::Macro, diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 8ea9b7a418b46..d216305e6a97d 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -593,7 +593,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .unwrap_or(Vec::new()) } } - Res::Def(DefKind::TyAlias { .. }, did) => { + Res::Def(DefKind::TyAlias, did) => { // Resolve the link on the type the alias points to. // FIXME: if the associated item is defined directly on the type alias, // it will show up on its documentation page, we should link there instead. diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs index b00fa104f9825..f95d2c2edb1ef 100644 --- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs +++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs @@ -50,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { && fields .iter() .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) - && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias { .. }, ..)) + && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..)) { let expr_spans = fields .iter() diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index be1c46319c2b6..47cce4603a68c 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -288,7 +288,7 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { /// Checks if the given `QPath` belongs to a type alias. pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { match *qpath { - QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias { .. } | DefKind::AssocTy, ..)), + QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)), QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => { is_ty_alias(&qpath) }, _ => false, } diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index 4b06b12fb94d0..686a3f41499b6 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -220,7 +220,7 @@ fn path_segment_certainty( // See the comment preceding `qpath_certainty`. `def_id` could refer to a type or a value. let certainty = lhs.join_clearing_def_ids(rhs); if resolves_to_type { - if let DefKind::TyAlias { .. } = cx.tcx.def_kind(def_id) { + if let DefKind::TyAlias = cx.tcx.def_kind(def_id) { adt_def_id(cx.tcx.type_of(def_id).instantiate_identity()) .map_or(certainty, |def_id| certainty.with_def_id(def_id)) } else { diff --git a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs index 5c59f217be6d2..9976969234234 100644 --- a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs +++ b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs @@ -1,6 +1,5 @@ -// FIXME(inherent_associated_types): This should be `check-pass` -// known-bug: #108491 // compile-flags: --crate-type=lib +// check-pass #![feature(inherent_associated_types)] #![allow(incomplete_features)] @@ -8,11 +7,6 @@ // Bounds on the self type play a major role in the resolution of inherent associated types (*). // As a result of that, if a type alias contains any then its bounds have to be respected and the // lint `type_alias_bounds` should not fire. -// -// FIXME(inherent_associated_types): In the current implementation that is. We might move the -// selection phase of IATs from hir_typeck to trait_selection resulting in us not requiring the -// ParamEnv that early allowing us to ignore bounds on type aliases again. -// Triage this before stabilization. #![deny(type_alias_bounds)] diff --git a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.stderr b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.stderr deleted file mode 100644 index 5e18543fc9001..0000000000000 --- a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.stderr +++ /dev/null @@ -1,55 +0,0 @@ -error[E0391]: cycle detected when expanding type alias `Alias` - --> $DIR/type-alias-bounds-are-enforced.rs:19:1 - | -LL | pub type Alias = (Source::Assoc,); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires computing the variances of `Source`... - --> $DIR/type-alias-bounds-are-enforced.rs:21:1 - | -LL | pub struct Source(T); - | ^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing the variances for items in this crate... - = note: ...which again requires expanding type alias `Alias`, completing the cycle -note: cycle used when collecting item types in top-level module - --> $DIR/type-alias-bounds-are-enforced.rs:5:1 - | -LL | / #![feature(inherent_associated_types)] -LL | | #![allow(incomplete_features)] -LL | | -LL | | // Bounds on the self type play a major role in the resolution of inherent associated types (*). -... | -LL | | pub type Assoc = (); -LL | | } - | |_^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error[E0391]: cycle detected when expanding type alias `Alias` - --> $DIR/type-alias-bounds-are-enforced.rs:19:1 - | -LL | pub type Alias = (Source::Assoc,); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires computing the variances of `Source`... - --> $DIR/type-alias-bounds-are-enforced.rs:21:1 - | -LL | pub struct Source(T); - | ^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing the variances for items in this crate... - = note: ...which again requires expanding type alias `Alias`, completing the cycle -note: cycle used when collecting item types in top-level module - --> $DIR/type-alias-bounds-are-enforced.rs:5:1 - | -LL | / #![feature(inherent_associated_types)] -LL | | #![allow(incomplete_features)] -LL | | -LL | | // Bounds on the self type play a major role in the resolution of inherent associated types (*). -... | -LL | | pub type Assoc = (); -LL | | } - | |_^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0391`. From 3c52a3e280a5fd1e8e4b48a5ec349aa00dcd859c Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 25 Sep 2023 15:46:38 +0200 Subject: [PATCH 142/149] subst -> instantiate --- compiler/rustc_ast_lowering/src/lib.rs | 2 +- .../src/diagnostics/region_name.rs | 4 ++-- .../rustc_borrowck/src/region_infer/mod.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- .../rustc_borrowck/src/universal_regions.rs | 5 ++-- .../scripts/filter_profile.rs | 6 ++--- .../rustc_codegen_cranelift/src/common.rs | 2 +- .../src/debuginfo/create_scope_map.rs | 2 +- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/mod.rs | 2 +- .../src/interpret/eval_context.rs | 2 +- .../rustc_const_eval/src/interpret/util.rs | 5 ++-- .../src/transform/validate.rs | 4 ++-- .../src/error_codes/E0038.md | 7 +++--- .../rustc_hir_analysis/src/astconv/bounds.rs | 2 +- .../src/astconv/generics.rs | 17 ++++++------- .../rustc_hir_analysis/src/astconv/mod.rs | 4 ++-- compiler/rustc_middle/src/ty/instance.rs | 12 +++++----- .../src/ty/normalize_erasing_regions.rs | 24 +++++-------------- compiler/rustc_mir_transform/src/inline.rs | 21 +++++++++------- .../rustc_mir_transform/src/inline/cycle.rs | 2 +- compiler/rustc_monomorphize/src/collector.rs | 2 +- .../rustc_monomorphize/src/partitioning.rs | 2 +- compiler/rustc_monomorphize/src/util.rs | 4 ++-- .../src/methods/unnecessary_to_owned.rs | 4 ++-- 25 files changed, 66 insertions(+), 75 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5b172b863ab61..85ab5e7223bf1 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1664,7 +1664,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lifetime.ident, )); - // Now make an arg that we can use for the substs of the opaque tykind. + // Now make an arg that we can use for the generic params of the opaque tykind. let id = self.next_node_id(); let lifetime_arg = self.new_named_lifetime_with_res(id, lifetime.ident, res); let duplicated_lifetime_def_id = self.local_def_id(duplicated_lifetime_node_id); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 30b47d076e27e..dc304cd7605f5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -27,7 +27,7 @@ pub(crate) struct RegionName { /// This helps to print the right kinds of diagnostics. #[derive(Debug, Clone)] pub(crate) enum RegionNameSource { - /// A bound (not free) region that was substituted at the def site (not an HRTB). + /// A bound (not free) region that was instantiated at the def site (not an HRTB). NamedEarlyBoundRegion(Span), /// A free region that the user has a name (`'a`) for. NamedFreeRegion(Span), @@ -619,7 +619,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { // programs, so we need to use delay_span_bug here. See #82126. self.infcx.tcx.sess.delay_span_bug( hir_arg.span(), - format!("unmatched subst and hir arg: found {kind:?} vs {hir_arg:?}"), + format!("unmatched arg and hir arg: found {kind:?} vs {hir_arg:?}"), ); } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 1049e7a8bbe35..852935676b6f3 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -2250,7 +2250,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { // Query canonicalization can create local superuniverses (for example in - // `InferCtx::query_response_substitution_guess`), but they don't have an associated + // `InferCtx::query_response_instantiation_guess`), but they don't have an associated // `UniverseInfo` explaining why they were created. // This can cause ICEs if these causes are accessed in diagnostics, for example in issue // #114907 where this happens via liveness and dropck outlives results. diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index aa750dd0169eb..9b952f3fe3625 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1007,7 +1007,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } pub(super) fn register_predefined_opaques_in_new_solver(&mut self) { - // OK to use the identity substitutions for each opaque type key, since + // OK to use the identity arguments for each opaque type key, since // we remap opaques from HIR typeck back to their definition params. let opaques: Vec<_> = self .infcx diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index e00299bad66d1..af437f36b9f84 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -639,10 +639,9 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { }; let global_mapping = iter::once((tcx.lifetimes.re_static, fr_static)); - let subst_mapping = - iter::zip(identity_args.regions(), fr_args.regions().map(|r| r.as_var())); + let arg_mapping = iter::zip(identity_args.regions(), fr_args.regions().map(|r| r.as_var())); - UniversalRegionIndices { indices: global_mapping.chain(subst_mapping).collect(), fr_static } + UniversalRegionIndices { indices: global_mapping.chain(arg_mapping).collect(), fr_static } } fn compute_inputs_and_output( diff --git a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs index f782671fe36f9..03912b18ea5f2 100755 --- a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs +++ b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs @@ -100,9 +100,9 @@ fn main() -> Result<(), Box> { stack = &stack[..index + ENCODE_METADATA.len()]; } - const SUBST_AND_NORMALIZE_ERASING_REGIONS: &str = "rustc_middle::ty::normalize_erasing_regions::::subst_and_normalize_erasing_regions"; - if let Some(index) = stack.find(SUBST_AND_NORMALIZE_ERASING_REGIONS) { - stack = &stack[..index + SUBST_AND_NORMALIZE_ERASING_REGIONS.len()]; + const INSTANTIATE_AND_NORMALIZE_ERASING_REGIONS: &str = "rustc_middle::ty::normalize_erasing_regions::::instantiate_and_normalize_erasing_regions"; + if let Some(index) = stack.find(INSTANTIATE_AND_NORMALIZE_ERASING_REGIONS) { + stack = &stack[..index + INSTANTIATE_AND_NORMALIZE_ERASING_REGIONS.len()]; } const NORMALIZE_ERASING_LATE_BOUND_REGIONS: &str = "rustc_middle::ty::normalize_erasing_regions::::normalize_erasing_late_bound_regions"; diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index ec2da39398b2d..359b430b4e57a 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -359,7 +359,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { where T: TypeFoldable> + Copy, { - self.instance.subst_mir_and_normalize_erasing_regions( + self.instance.instantiate_mir_and_normalize_erasing_regions( self.tcx, ty::ParamEnv::reveal_all(), ty::EarlyBinder::bind(value), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index 5ca2942ac4eda..aff764f0224c9 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -90,7 +90,7 @@ fn make_mir_scope<'ll, 'tcx>( Some((callee, _)) => { // FIXME(eddyb) this would be `self.monomorphize(&callee)` // if this is moved to `rustc_codegen_ssa::mir::debuginfo`. - let callee = cx.tcx.subst_and_normalize_erasing_regions( + let callee = cx.tcx.instantiate_and_normalize_erasing_regions( instance.args, ty::ParamEnv::reveal_all(), ty::EarlyBinder::bind(callee), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index c862acdc7def4..30cc9ea9b8258 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -529,7 +529,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { if let Some(impl_def_id) = cx.tcx.impl_of_method(instance.def_id()) { // If the method does *not* belong to a trait, proceed if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { - let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions( + let impl_self_ty = cx.tcx.instantiate_and_normalize_erasing_regions( instance.args, ty::ParamEnv::reveal_all(), cx.tcx.type_of(impl_def_id), diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 1e905a7c78e47..a61018f987031 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -118,7 +118,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { T: Copy + TypeFoldable>, { debug!("monomorphize: self.instance={:?}", self.instance); - self.instance.subst_mir_and_normalize_erasing_regions( + self.instance.instantiate_mir_and_normalize_erasing_regions( self.cx.tcx(), ty::ParamEnv::reveal_all(), ty::EarlyBinder::bind(value), diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 7c743a21d9350..94a5cc67d31c8 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -569,7 +569,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> Result { frame .instance - .try_subst_mir_and_normalize_erasing_regions( + .try_instantiate_mir_and_normalize_erasing_regions( *self.tcx, self.param_env, ty::EarlyBinder::bind(value), diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index b33194423887c..eb639ded70ffd 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -4,7 +4,7 @@ use rustc_middle::ty::{ }; use std::ops::ControlFlow; -/// Checks whether a type contains generic parameters which require substitution. +/// Checks whether a type contains generic parameters which must be instantiated. /// /// In case it does, returns a `TooGeneric` const eval error. Note that due to polymorphization /// types may be "concrete enough" even though they still contain generic parameters in @@ -43,7 +43,8 @@ where .try_into() .expect("more generic parameters than can fit into a `u32`"); // Only recurse when generic parameters in fns, closures and generators - // are used and require substitution. + // are used and have to be instantiated. + // // Just in case there are closures or generators within this subst, // recurse. if unused_params.is_used(index) && subst.has_param() { diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 2f5f2ad6534ad..0d8733070a446 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -558,8 +558,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } } -/// A faster version of the validation pass that only checks those things which may break when apply -/// generic substitutions. +/// A faster version of the validation pass that only checks those things which may break when +/// instantiating any generic parameters. pub fn validate_types<'tcx>( tcx: TyCtxt<'tcx>, mir_phase: MirPhase, diff --git a/compiler/rustc_error_codes/src/error_codes/E0038.md b/compiler/rustc_error_codes/src/error_codes/E0038.md index 584b78554ef88..8f8eabb151907 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0038.md +++ b/compiler/rustc_error_codes/src/error_codes/E0038.md @@ -162,14 +162,13 @@ fn foo(x: T) { ``` The machine code for `foo::()`, `foo::()`, `foo::()`, or any -other type substitution is different. Hence the compiler generates the +other instantiation is different. Hence the compiler generates the implementation on-demand. If you call `foo()` with a `bool` parameter, the compiler will only generate code for `foo::()`. When we have additional type parameters, the number of monomorphized implementations the compiler generates does not grow drastically, since the compiler will only generate an -implementation if the function is called with unparameterized substitutions -(i.e., substitutions where none of the substituted types are themselves -parameterized). +implementation if the function is called with fully concrete arguments +(i.e., arguments which do not contain any generic parameters). However, with trait objects we have to make a table containing _every_ object that implements the trait. Now, if it has type parameters, we need to add diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index ba152cd48dea4..21611e9c58600 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -427,7 +427,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let bound_vars = tcx.late_bound_vars(binding.hir_id); ty::Binder::bind_with_vars(subst_output, bound_vars) } else { - // Include substitutions for generic parameters of associated types + // Append the generic arguments of the associated type to the `trait_ref`. candidate.map_bound(|trait_ref| { let ident = Ident::new(assoc_item.name, binding.item_name.span); let item_segment = hir::PathSegment { diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index 1372cc896be3f..e3621ef933a52 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -139,22 +139,22 @@ fn generic_arg_mismatch_err( err.emit() } -/// Creates the relevant generic argument substitutions +/// Creates the relevant generic arguments /// corresponding to a set of generic parameters. This is a /// rather complex function. Let us try to explain the role /// of each of its parameters: /// -/// To start, we are given the `def_id` of the thing we are -/// creating the substitutions for, and a partial set of -/// substitutions `parent_args`. In general, the substitutions -/// for an item begin with substitutions for all the "parents" of +/// To start, we are given the `def_id` of the thing whose generic +/// parameters we are instantiating, and a partial set of +/// arguments `parent_args`. In general, the generic arguments +/// for an item begin with arguments for all the "parents" of /// that item -- e.g., for a method it might include the /// parameters from the impl. /// /// Therefore, the method begins by walking down these parents, /// starting with the outermost parent and proceed inwards until /// it reaches `def_id`. For each parent `P`, it will check `parent_args` -/// first to see if the parent's substitutions are listed in there. If so, +/// first to see if the parent's arguments are listed in there. If so, /// we can append those and move on. Otherwise, it invokes the /// three callback functions: /// @@ -188,7 +188,7 @@ pub fn create_args_for_parent_generic_args<'tcx, 'a>( stack.push((def_id, parent_defs)); } - // We manually build up the substitution, rather than using convenience + // We manually build up the generic arguments, rather than using convenience // methods in `subst.rs`, so that we can iterate over the arguments and // parameters in lock-step linearly, instead of trying to match each pair. let mut args: SmallVec<[ty::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count); @@ -196,7 +196,8 @@ pub fn create_args_for_parent_generic_args<'tcx, 'a>( while let Some((def_id, defs)) = stack.pop() { let mut params = defs.params.iter().peekable(); - // If we have already computed substitutions for parents, we can use those directly. + // If we have already computed the generic arguments for parents, + // we can use those directly. while let Some(¶m) = params.peek() { if let Some(&kind) = parent_args.get(param.index as usize) { args.push(kind); diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 106cd223d302e..bb2e4447d4db5 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -289,7 +289,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, - /// returns an appropriate set of substitutions for this particular reference to `I`. + /// returns an appropriate set of generic arguments for this particular reference to `I`. pub fn ast_path_args_for_ty( &self, span: Span, @@ -315,7 +315,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Given the type/lifetime/const arguments provided to some path (along with /// an implicit `Self`, if this is a trait reference), returns the complete - /// set of substitutions. This may involve applying defaulted type parameters. + /// set of generic arguments. This may involve applying defaulted type parameters. /// Constraints on associated types are created from `create_assoc_bindings_for_generic_args`. /// /// Example: diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 2b75f6c4e8f23..0b0a708e42bb9 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -118,7 +118,7 @@ impl<'tcx> Instance<'tcx> { /// lifetimes erased, allowing a `ParamEnv` to be specified for use during normalization. pub fn ty(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> { let ty = tcx.type_of(self.def.def_id()); - tcx.subst_and_normalize_erasing_regions(self.args, param_env, ty) + tcx.instantiate_and_normalize_erasing_regions(self.args, param_env, ty) } /// Finds a crate that contains a monomorphization of this instance that @@ -580,7 +580,7 @@ impl<'tcx> Instance<'tcx> { self.def.has_polymorphic_mir_body().then_some(self.args) } - pub fn subst_mir(&self, tcx: TyCtxt<'tcx>, v: EarlyBinder<&T>) -> T + pub fn instantiate_mir(&self, tcx: TyCtxt<'tcx>, v: EarlyBinder<&T>) -> T where T: TypeFoldable> + Copy, { @@ -593,7 +593,7 @@ impl<'tcx> Instance<'tcx> { } #[inline(always)] - pub fn subst_mir_and_normalize_erasing_regions( + pub fn instantiate_mir_and_normalize_erasing_regions( &self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -603,14 +603,14 @@ impl<'tcx> Instance<'tcx> { T: TypeFoldable> + Clone, { if let Some(args) = self.args_for_mir_body() { - tcx.subst_and_normalize_erasing_regions(args, param_env, v) + tcx.instantiate_and_normalize_erasing_regions(args, param_env, v) } else { tcx.normalize_erasing_regions(param_env, v.skip_binder()) } } #[inline(always)] - pub fn try_subst_mir_and_normalize_erasing_regions( + pub fn try_instantiate_mir_and_normalize_erasing_regions( &self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -620,7 +620,7 @@ impl<'tcx> Instance<'tcx> { T: TypeFoldable> + Clone, { if let Some(args) = self.args_for_mir_body() { - tcx.try_subst_and_normalize_erasing_regions(args, param_env, v) + tcx.try_instantiate_and_normalize_erasing_regions(args, param_env, v) } else { tcx.try_normalize_erasing_regions(param_env, v.skip_binder()) } diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 2415d50b2784a..fd125af2074e4 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -134,8 +134,9 @@ impl<'tcx> TyCtxt<'tcx> { /// in-scope substitutions and then normalizing any associated /// types. /// Panics if normalization fails. In case normalization might fail - /// use `try_subst_and_normalize_erasing_regions` instead. - pub fn subst_and_normalize_erasing_regions( + /// use `try_instantiate_and_normalize_erasing_regions` instead. + #[instrument(level = "debug", skip(self))] + pub fn instantiate_and_normalize_erasing_regions( self, param_args: GenericArgsRef<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -144,22 +145,16 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable>, { - debug!( - "subst_and_normalize_erasing_regions(\ - param_args={:?}, \ - value={:?}, \ - param_env={:?})", - param_args, value, param_env, - ); let substituted = value.instantiate(self, param_args); self.normalize_erasing_regions(param_env, substituted) } /// Monomorphizes a type from the AST by first applying the /// in-scope substitutions and then trying to normalize any associated - /// types. Contrary to `subst_and_normalize_erasing_regions` this does + /// types. Contrary to `instantiate_and_normalize_erasing_regions` this does /// not assume that normalization succeeds. - pub fn try_subst_and_normalize_erasing_regions( + #[instrument(level = "debug", skip(self))] + pub fn try_instantiate_and_normalize_erasing_regions( self, param_args: GenericArgsRef<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -168,13 +163,6 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable>, { - debug!( - "subst_and_normalize_erasing_regions(\ - param_args={:?}, \ - value={:?}, \ - param_env={:?})", - param_args, value, param_env, - ); let substituted = value.instantiate(self, param_args); self.try_normalize_erasing_regions(param_env, substituted) } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 1d9c89477cbab..ebd61f8ad9532 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -193,7 +193,7 @@ impl<'tcx> Inliner<'tcx> { return Err("optimization fuel exhausted"); } - let Ok(callee_body) = callsite.callee.try_subst_mir_and_normalize_erasing_regions( + let Ok(callee_body) = callsite.callee.try_instantiate_mir_and_normalize_erasing_regions( self.tcx, self.param_env, ty::EarlyBinder::bind(callee_body.clone()), @@ -481,9 +481,10 @@ impl<'tcx> Inliner<'tcx> { work_list.push(target); // If the place doesn't actually need dropping, treat it like a regular goto. - let ty = callsite - .callee - .subst_mir(self.tcx, ty::EarlyBinder::bind(&place.ty(callee_body, tcx).ty)); + let ty = callsite.callee.instantiate_mir( + self.tcx, + ty::EarlyBinder::bind(&place.ty(callee_body, tcx).ty), + ); if ty.needs_drop(tcx, self.param_env) && let UnwindAction::Cleanup(unwind) = unwind { work_list.push(unwind); } @@ -650,7 +651,7 @@ impl<'tcx> Inliner<'tcx> { // Copy only unevaluated constants from the callee_body into the caller_body. // Although we are only pushing `ConstKind::Unevaluated` consts to // `required_consts`, here we may not only have `ConstKind::Unevaluated` - // because we are calling `subst_and_normalize_erasing_regions`. + // because we are calling `instantiate_and_normalize_erasing_regions`. caller_body.required_consts.extend( callee_body.required_consts.iter().copied().filter(|&ct| match ct.const_ { Const::Ty(_) => { @@ -811,9 +812,10 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { match terminator.kind { TerminatorKind::Drop { ref place, unwind, .. } => { // If the place doesn't actually need dropping, treat it like a regular goto. - let ty = self - .instance - .subst_mir(tcx, ty::EarlyBinder::bind(&place.ty(self.callee_body, tcx).ty)); + let ty = self.instance.instantiate_mir( + tcx, + ty::EarlyBinder::bind(&place.ty(self.callee_body, tcx).ty), + ); if ty.needs_drop(tcx, self.param_env) { self.cost += CALL_PENALTY; if let UnwindAction::Cleanup(_) = unwind { @@ -824,7 +826,8 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { } } TerminatorKind::Call { func: Operand::Constant(ref f), unwind, .. } => { - let fn_ty = self.instance.subst_mir(tcx, ty::EarlyBinder::bind(&f.const_.ty())); + let fn_ty = + self.instance.instantiate_mir(tcx, ty::EarlyBinder::bind(&f.const_.ty())); self.cost += if let ty::FnDef(def_id, _) = *fn_ty.kind() && tcx.is_intrinsic(def_id) { // Don't give intrinsics the extra penalty for calls INSTR_COST diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index 822634129fc2b..d30e0bad81301 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -44,7 +44,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( ) -> bool { trace!(%caller); for &(callee, args) in tcx.mir_inliner_callees(caller.def) { - let Ok(args) = caller.try_subst_mir_and_normalize_erasing_regions( + let Ok(args) = caller.try_instantiate_mir_and_normalize_erasing_regions( tcx, param_env, ty::EarlyBinder::bind(args), diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 4afa4e597f7ce..1a9f0e8352e73 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -606,7 +606,7 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { T: TypeFoldable>, { debug!("monomorphize: self.instance={:?}", self.instance); - self.instance.subst_mir_and_normalize_erasing_regions( + self.instance.instantiate_mir_and_normalize_erasing_regions( self.tcx, ty::ParamEnv::reveal_all(), ty::EarlyBinder::bind(value), diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index c993e64477b07..1d8cbe0e21b7d 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -647,7 +647,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( // parameters, but the self-type of their impl block do will fail to normalize. if !tcx.sess.opts.unstable_opts.polymorphize || !instance.has_param() { // This is a method within an impl, find out what the self-type is: - let impl_self_ty = tcx.subst_and_normalize_erasing_regions( + let impl_self_ty = tcx.instantiate_and_normalize_erasing_regions( instance.args, ty::ParamEnv::reveal_all(), tcx.type_of(impl_def_id), diff --git a/compiler/rustc_monomorphize/src/util.rs b/compiler/rustc_monomorphize/src/util.rs index a3433d3d13dba..e25c5c9f27c75 100644 --- a/compiler/rustc_monomorphize/src/util.rs +++ b/compiler/rustc_monomorphize/src/util.rs @@ -26,12 +26,12 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In let ClosureSizeProfileData { before_feature_tys, after_feature_tys } = typeck_results.closure_size_eval[&closure_def_id]; - let before_feature_tys = tcx.subst_and_normalize_erasing_regions( + let before_feature_tys = tcx.instantiate_and_normalize_erasing_regions( closure_instance.args, param_env, ty::EarlyBinder::bind(before_feature_tys), ); - let after_feature_tys = tcx.subst_and_normalize_erasing_regions( + let after_feature_tys = tcx.instantiate_and_normalize_erasing_regions( closure_instance.args, param_env, ty::EarlyBinder::bind(after_feature_tys), diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 5c5ee26205287..50d6f3b7e55f5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -401,7 +401,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< = get_callee_generic_args_and_args(cx, parent_expr) { // FIXME: the `instantiate_identity()` below seems incorrect, since we eventually - // call `tcx.try_subst_and_normalize_erasing_regions` further down + // call `tcx.try_instantiate_and_normalize_erasing_regions` further down // (i.e., we are explicitly not in the identity context). let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder(); if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id) @@ -452,7 +452,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< let output_ty = fn_sig.output(); if output_ty.contains(*param_ty) { - if let Ok(new_ty) = cx.tcx.try_subst_and_normalize_erasing_regions( + if let Ok(new_ty) = cx.tcx.try_instantiate_and_normalize_erasing_regions( new_subst, cx.param_env, EarlyBinder::bind(output_ty)) { expr = parent_expr; ty = new_ty; From 64df5a851e1cb8c2ae539aa5331d48b8f3646bf4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 25 Sep 2023 23:09:27 +1000 Subject: [PATCH 143/149] Skip MIR pass `UnreachablePropagation` when coverage is enabled When coverage instrumentation and MIR opts are both enabled, coverage relies on two assumptions: - MIR opts that would delete `StatementKind::Coverage` statements instead move them into bb0 and change them to `CoverageKind::Unreachable`. - MIR opts won't delete all `CoverageKind::Counter` statements from an instrumented function. Most MIR opts naturally satisfy the second assumption, because they won't remove coverage statements from bb0, but `UnreachablePropagation` can do so if it finds that bb0 is unreachable. If this happens, LLVM thinks the function isn't instrumented, and it vanishes from coverage reports. A proper solution won't be possible until after per-function coverage info lands in #116046, but for now we can avoid the problem by turning off this particular pass when coverage instrumentation is enabled. --- compiler/rustc_mir_transform/src/unreachable_prop.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index bd1724bf84241..0b9311a20efa3 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -13,7 +13,11 @@ pub struct UnreachablePropagation; impl MirPass<'_> for UnreachablePropagation { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { // Enable only under -Zmir-opt-level=2 as this can make programs less debuggable. - sess.mir_opt_level() >= 2 + + // FIXME(#116171) Coverage gets confused by MIR passes that can remove all + // coverage statements from an instrumented function. This pass can be + // re-enabled when coverage codegen is robust against that happening. + sess.mir_opt_level() >= 2 && !sess.instrument_coverage() } fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { From 305524d1d655f744e739f45850bc6f74918ac0c9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 25 Sep 2023 15:00:38 +0000 Subject: [PATCH 144/149] Anonymize binders for refining_impl_trait check --- .../src/check/compare_impl_item/refine.rs | 29 ++++++++++++++++--- .../in-trait/anonymize-binders-for-refine.rs | 13 +++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 tests/ui/impl-trait/in-trait/anonymize-binders-for-refine.rs diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index a8149b634ef7c..d9e0e87eb471e 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -5,7 +5,7 @@ use rustc_infer::infer::{outlives::env::OutlivesEnvironment, TyCtxtInferExt}; use rustc_lint_defs::builtin::REFINING_IMPL_TRAIT; use rustc_middle::traits::{ObligationCause, Reveal}; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, + self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::traits::{ @@ -176,9 +176,13 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( return; }; - // For quicker lookup, use an `IndexSet` - // (we don't use one earlier because it's not foldable..) - let trait_bounds = FxIndexSet::from_iter(trait_bounds); + // For quicker lookup, use an `IndexSet` (we don't use one earlier because + // it's not foldable..). + // Also, We have to anonymize binders in these types because they may contain + // `BrNamed` bound vars, which contain unique `DefId`s which correspond to syntax + // locations that we don't care about when checking bound equality. + let trait_bounds = FxIndexSet::from_iter(trait_bounds.fold_with(&mut Anonymize { tcx })); + let impl_bounds = impl_bounds.fold_with(&mut Anonymize { tcx }); // Find any clauses that are present in the impl's RPITITs that are not // present in the trait's RPITITs. This will trigger on trivial predicates, @@ -309,3 +313,20 @@ fn type_visibility<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option None, } } + +struct Anonymize<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> TypeFolder> for Anonymize<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> + where + T: TypeFoldable>, + { + self.tcx.anonymize_bound_vars(t) + } +} diff --git a/tests/ui/impl-trait/in-trait/anonymize-binders-for-refine.rs b/tests/ui/impl-trait/in-trait/anonymize-binders-for-refine.rs new file mode 100644 index 0000000000000..e62662f2f077d --- /dev/null +++ b/tests/ui/impl-trait/in-trait/anonymize-binders-for-refine.rs @@ -0,0 +1,13 @@ +// compile-flags: --crate-type=lib +// check-pass + +#![feature(return_position_impl_trait_in_trait)] +#![deny(refining_impl_trait)] + +pub trait Tr { + fn foo() -> impl for<'a> Tr<&'a Self>; +} + +impl Tr for () { + fn foo() -> impl for<'a> Tr<&'a Self> {} +} From 35f9345076b80a26c7c96f18fb95f69adbee2375 Mon Sep 17 00:00:00 2001 From: Milo <50248166+Milo123459@users.noreply.github.com> Date: Tue, 26 Sep 2023 16:02:05 +0000 Subject: [PATCH 145/149] add test fix tidy remove dir --- tests/ui/async-await/const-async-fn-in-main.rs | 7 +++++++ tests/ui/async-await/const-async-fn-in-main.stderr | 11 +++++++++++ 2 files changed, 18 insertions(+) create mode 100644 tests/ui/async-await/const-async-fn-in-main.rs create mode 100644 tests/ui/async-await/const-async-fn-in-main.stderr diff --git a/tests/ui/async-await/const-async-fn-in-main.rs b/tests/ui/async-await/const-async-fn-in-main.rs new file mode 100644 index 0000000000000..5d1aa4d83f38e --- /dev/null +++ b/tests/ui/async-await/const-async-fn-in-main.rs @@ -0,0 +1,7 @@ +// edition:2021 +// Check what happens when a const async fn is in the main function (#102796) + +fn main() { + const async fn a() {} +//~^ ERROR functions cannot be both `const` and `async` +} diff --git a/tests/ui/async-await/const-async-fn-in-main.stderr b/tests/ui/async-await/const-async-fn-in-main.stderr new file mode 100644 index 0000000000000..10b15170922f7 --- /dev/null +++ b/tests/ui/async-await/const-async-fn-in-main.stderr @@ -0,0 +1,11 @@ +error: functions cannot be both `const` and `async` + --> $DIR/const-async-fn-in-main.rs:5:5 + | +LL | const async fn a() {} + | ^^^^^-^^^^^---------- + | | | + | | `async` because of this + | `const` because of this + +error: aborting due to previous error + From 4b15959218a4e856d95329d7752b1c55d5922de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 26 Sep 2023 23:46:10 +0000 Subject: [PATCH 146/149] Add context to `let: Ty = loop { break };` We weren't accounting for the case where `break` was immediately within the `loop` block. --- compiler/rustc_hir_typeck/src/demand.rs | 1 + tests/ui/issues/issue-27042.stderr | 7 ++++++- tests/ui/loops/loop-labeled-break-value.stderr | 15 ++++++++++++--- tests/ui/loops/loop-properly-diverging-2.stderr | 5 ++++- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 31e773585d196..256a4bf944910 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -545,6 +545,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Climb the HIR tree to see if the current `Expr` is part of a `break;` statement. let Some( hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Semi(&ref p), .. }) + | hir::Node::Block(hir::Block { expr: Some(&ref p), .. }) | hir::Node::Expr(&ref p), ) = self.tcx.hir().find(parent_id) else { diff --git a/tests/ui/issues/issue-27042.stderr b/tests/ui/issues/issue-27042.stderr index 69eedeb8025a6..01532de999e3b 100644 --- a/tests/ui/issues/issue-27042.stderr +++ b/tests/ui/issues/issue-27042.stderr @@ -11,8 +11,13 @@ LL | | while true { break }; // but here we cite the whole loop error[E0308]: mismatched types --> $DIR/issue-27042.rs:6:16 | +LL | let _: i32 = + | - expected because of this assignment +LL | 'a: // in this case, the citation is just the `break`: LL | loop { break }; - | ^^^^^ expected `i32`, found `()` + | ---- ^^^^^ expected `i32`, found `()` + | | + | this loop is expected to be of type `i32` | help: give it a value of the expected type | diff --git a/tests/ui/loops/loop-labeled-break-value.stderr b/tests/ui/loops/loop-labeled-break-value.stderr index f233670e8c4c1..694d6c306f645 100644 --- a/tests/ui/loops/loop-labeled-break-value.stderr +++ b/tests/ui/loops/loop-labeled-break-value.stderr @@ -2,7 +2,10 @@ error[E0308]: mismatched types --> $DIR/loop-labeled-break-value.rs:3:29 | LL | let _: i32 = loop { break }; - | ^^^^^ expected `i32`, found `()` + | - ---- ^^^^^ expected `i32`, found `()` + | | | + | | this loop is expected to be of type `i32` + | expected because of this assignment | help: give it a value of the expected type | @@ -13,7 +16,10 @@ error[E0308]: mismatched types --> $DIR/loop-labeled-break-value.rs:6:37 | LL | let _: i32 = 'inner: loop { break 'inner }; - | ^^^^^^^^^^^^ expected `i32`, found `()` + | - ---- ^^^^^^^^^^^^ expected `i32`, found `()` + | | | + | | this loop is expected to be of type `i32` + | expected because of this assignment | help: give it a value of the expected type | @@ -24,7 +30,10 @@ error[E0308]: mismatched types --> $DIR/loop-labeled-break-value.rs:9:45 | LL | let _: i32 = 'inner2: loop { loop { break 'inner2 } }; - | ^^^^^^^^^^^^^ expected `i32`, found `()` + | - ---- ^^^^^^^^^^^^^ expected `i32`, found `()` + | | | + | | this loop is expected to be of type `i32` + | expected because of this assignment | help: give it a value of the expected type | diff --git a/tests/ui/loops/loop-properly-diverging-2.stderr b/tests/ui/loops/loop-properly-diverging-2.stderr index dc60657ee142b..1d1ae60cda174 100644 --- a/tests/ui/loops/loop-properly-diverging-2.stderr +++ b/tests/ui/loops/loop-properly-diverging-2.stderr @@ -2,7 +2,10 @@ error[E0308]: mismatched types --> $DIR/loop-properly-diverging-2.rs:2:23 | LL | let x: i32 = loop { break }; - | ^^^^^ expected `i32`, found `()` + | - ---- ^^^^^ expected `i32`, found `()` + | | | + | | this loop is expected to be of type `i32` + | expected because of this assignment | help: give it a value of the expected type | From 0ff1254d91d5b51a5079bd0729b41beb7a2f4adc Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 27 Sep 2023 09:21:34 +0800 Subject: [PATCH 147/149] Update cargo New license exceptions from gix: * (byteyarn", "Apache-2.0") * ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause") --- src/tools/cargo | 2 +- src/tools/tidy/src/deps.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/cargo b/src/tools/cargo index 414d9e3a6d809..e6aabe8b3fcf6 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 414d9e3a6d8096f3e276234ce220c868767a8792 +Subproject commit e6aabe8b3fcf639be3a5bf68e77853bd7b3fa27d diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index c05349c8e8da7..7d3ef4197a73e 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -58,14 +58,14 @@ const EXCEPTIONS_CARGO: &[(&str, &str)] = &[ // tidy-alphabetical-start ("bitmaps", "MPL-2.0+"), ("bytesize", "Apache-2.0"), + ("byteyarn", "Apache-2.0"), ("ciborium", "Apache-2.0"), ("ciborium-io", "Apache-2.0"), ("ciborium-ll", "Apache-2.0"), ("dunce", "CC0-1.0 OR MIT-0 OR Apache-2.0"), + ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"), ("fiat-crypto", "MIT OR Apache-2.0 OR BSD-1-Clause"), ("im-rc", "MPL-2.0+"), - ("imara-diff", "Apache-2.0"), - ("instant", "BSD-3-Clause"), ("normalize-line-endings", "Apache-2.0"), ("openssl", "Apache-2.0"), ("ryu", "Apache-2.0 OR BSL-1.0"), From fc0d833f6fb9876c367bb738927525102e5761a7 Mon Sep 17 00:00:00 2001 From: The Miri Conjob Bot Date: Wed, 27 Sep 2023 05:38:37 +0000 Subject: [PATCH 148/149] Preparing for merge from rustc --- 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 458bba9a61668..3f1babf53234c 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -42ca6e4e5760a548a6fa858482de6d237f6fb3b8 +d206f2c0857eb879877f27508139dd62a40294c3 From c8a44b1eaf5aed5cf8e57340f1ac56c9bad08acf Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 27 Sep 2023 08:50:16 +0200 Subject: [PATCH 149/149] clippy --- src/tools/miri/cargo-miri/src/phases.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 80ce63255826e..d655df6d994d4 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -10,7 +10,7 @@ use rustc_version::VersionMeta; use crate::{setup::*, util::*}; -const CARGO_MIRI_HELP: &str = r#"Runs binary crates and tests in Miri +const CARGO_MIRI_HELP: &str = r"Runs binary crates and tests in Miri Usage: cargo miri [subcommand] [...] [--] [...] @@ -31,7 +31,7 @@ Examples: This will print the path to the generated sysroot (and nothing else) on stdout. stderr will still contain progress information about how the build is doing. -"#; +"; fn show_help() { println!("{CARGO_MIRI_HELP}");