From 864368a1d1bce411400d454155c870152ee033fa Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 22 Jul 2023 10:13:49 +0800 Subject: [PATCH 01/21] Fix wrong span for trait selection failure error reporting --- .../src/traits/error_reporting/mod.rs | 8 ++++++ tests/ui/dst/issue-113447.fixed | 25 +++++++++++++++++++ tests/ui/dst/issue-113447.rs | 25 +++++++++++++++++++ tests/ui/dst/issue-113447.stderr | 25 +++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 tests/ui/dst/issue-113447.fixed create mode 100644 tests/ui/dst/issue-113447.rs create mode 100644 tests/ui/dst/issue-113447.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index c14839fe9be08..d1439ff2f2515 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2994,6 +2994,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { unsatisfied_const: bool, ) { let body_def_id = obligation.cause.body_id; + let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } = + obligation.cause.code() + { + *rhs_span + } else { + span + }; + // Try to report a help message if is_fn_trait && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( diff --git a/tests/ui/dst/issue-113447.fixed b/tests/ui/dst/issue-113447.fixed new file mode 100644 index 0000000000000..536f680f697c7 --- /dev/null +++ b/tests/ui/dst/issue-113447.fixed @@ -0,0 +1,25 @@ +// run-rustfix + +pub struct Bytes; + +impl Bytes { + pub fn as_slice(&self) -> &[u8] { + todo!() + } +} + +impl PartialEq<[u8]> for Bytes { + fn eq(&self, other: &[u8]) -> bool { + self.as_slice() == other + } +} + +impl PartialEq for &[u8] { + fn eq(&self, other: &Bytes) -> bool { + *other == **self + } +} + +fn main() { + let _ = &[0u8] == &[0xAA][..]; //~ ERROR can't compare `&[u8; 1]` with `[{integer}; 1]` +} diff --git a/tests/ui/dst/issue-113447.rs b/tests/ui/dst/issue-113447.rs new file mode 100644 index 0000000000000..c10a4f2ff8ec4 --- /dev/null +++ b/tests/ui/dst/issue-113447.rs @@ -0,0 +1,25 @@ +// run-rustfix + +pub struct Bytes; + +impl Bytes { + pub fn as_slice(&self) -> &[u8] { + todo!() + } +} + +impl PartialEq<[u8]> for Bytes { + fn eq(&self, other: &[u8]) -> bool { + self.as_slice() == other + } +} + +impl PartialEq for &[u8] { + fn eq(&self, other: &Bytes) -> bool { + *other == **self + } +} + +fn main() { + let _ = &[0u8] == [0xAA]; //~ ERROR can't compare `&[u8; 1]` with `[{integer}; 1]` +} diff --git a/tests/ui/dst/issue-113447.stderr b/tests/ui/dst/issue-113447.stderr new file mode 100644 index 0000000000000..240553a675bfe --- /dev/null +++ b/tests/ui/dst/issue-113447.stderr @@ -0,0 +1,25 @@ +error[E0277]: can't compare `&[u8; 1]` with `[{integer}; 1]` + --> $DIR/issue-113447.rs:24:20 + | +LL | let _ = &[0u8] == [0xAA]; + | ^^ no implementation for `&[u8; 1] == [{integer}; 1]` + | + = help: the trait `PartialEq<[{integer}; 1]>` is not implemented for `&[u8; 1]` + = help: the following other types implement trait `PartialEq`: + <[A; N] as PartialEq<[B; N]>> + <[A; N] as PartialEq<[B]>> + <[A; N] as PartialEq<&[B]>> + <[A; N] as PartialEq<&mut [B]>> + <[T] as PartialEq>> + <[A] as PartialEq<[B]>> + <[B] as PartialEq<[A; N]>> + <&[u8] as PartialEq> + and 4 others +help: convert the array to a `&[u8]` slice instead + | +LL | let _ = &[0u8] == &[0xAA][..]; + | + ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 1d456583293f351507bc5b8c292d408300e3ad7c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 24 Jul 2023 14:07:23 +0000 Subject: [PATCH 02/21] Some tracing changes --- compiler/rustc_borrowck/src/type_check/mod.rs | 6 +- .../src/mem_categorization.rs | 75 ++++++++----------- compiler/rustc_hir_typeck/src/upvar.rs | 10 +-- 3 files changed, 35 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 78aa513033c92..0b2f39c1eb3fd 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -504,14 +504,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { /// Checks that the types internal to the `place` match up with /// what would be expected. + #[instrument(level = "debug", skip(self, location), ret)] fn sanitize_place( &mut self, place: &Place<'tcx>, location: Location, context: PlaceContext, ) -> PlaceTy<'tcx> { - debug!("sanitize_place: {:?}", place); - let mut place_ty = PlaceTy::from_ty(self.body().local_decls[place.local].ty); for elem in place.projection.iter() { @@ -614,7 +613,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } } - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self, location), ret, level = "debug")] fn sanitize_projection( &mut self, base: PlaceTy<'tcx>, @@ -623,7 +622,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { location: Location, context: PlaceContext, ) -> PlaceTy<'tcx> { - debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place); let tcx = self.tcx(); let base_ty = base.ty; match pi { diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 0700e2e05546c..9de4f82a06bdb 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -198,13 +198,14 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } /// Like `pat_ty`, but ignores implicit `&` patterns. + #[instrument(level = "debug", skip(self), ret)] fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> McResult> { let base_ty = self.node_ty(pat.hir_id)?; - debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty); + trace!(?base_ty); // This code detects whether we are looking at a `ref x`, // and if so, figures out what the type *being borrowed* is. - let ret_ty = match pat.kind { + match pat.kind { PatKind::Binding(..) => { let bm = *self .typeck_results @@ -217,21 +218,18 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. match base_ty.builtin_deref(false) { - Some(t) => t.ty, + Some(t) => Ok(t.ty), None => { - debug!("By-ref binding of non-derefable type {:?}", base_ty); - return Err(()); + debug!("By-ref binding of non-derefable type"); + Err(()) } } } else { - base_ty + Ok(base_ty) } } - _ => base_ty, - }; - debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty); - - Ok(ret_ty) + _ => Ok(base_ty), + } } pub(crate) fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult> { @@ -299,13 +297,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub(crate) fn cat_expr_unadjusted( &self, expr: &hir::Expr<'_>, ) -> McResult> { - debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr); - let expr_ty = self.expr_ty(expr)?; match expr.kind { hir::ExprKind::Unary(hir::UnOp::Deref, ref e_base) => { @@ -319,7 +315,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { hir::ExprKind::Field(ref base, _) => { let base = self.cat_expr(base)?; - debug!("cat_expr(cat_field): id={} expr={:?} base={:?}", expr.hir_id, expr, base); + debug!(?base); let field_idx = self .typeck_results @@ -389,7 +385,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - #[instrument(level = "debug", skip(self, span))] + #[instrument(level = "debug", skip(self, span), ret)] pub(crate) fn cat_res( &self, hir_id: hir::HirId, @@ -430,6 +426,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// Note: the actual upvar access contains invisible derefs of closure /// environment and upvar reference as appropriate. Only regionck cares /// about these dereferences, so we let it compute them as needed. + #[instrument(level = "debug", skip(self), ret)] fn cat_upvar(&self, hir_id: hir::HirId, var_id: hir::HirId) -> McResult> { let closure_expr_def_id = self.body_owner; @@ -439,24 +436,20 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { }; let var_ty = self.node_ty(var_id)?; - let ret = PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()); - - debug!("cat_upvar ret={:?}", ret); - Ok(ret) + Ok(PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new())) } + #[instrument(level = "debug", skip(self), ret)] pub(crate) fn cat_rvalue( &self, hir_id: hir::HirId, span: Span, expr_ty: Ty<'tcx>, ) -> PlaceWithHirId<'tcx> { - debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span); - let ret = PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()); - debug!("cat_rvalue ret={:?}", ret); - ret + PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()) } + #[instrument(level = "debug", skip(self, node), ret)] pub(crate) fn cat_projection( &self, node: &N, @@ -466,14 +459,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { ) -> PlaceWithHirId<'tcx> { let mut projections = base_place.place.projections; projections.push(Projection { kind, ty }); - let ret = PlaceWithHirId::new( + PlaceWithHirId::new( node.hir_id(), base_place.place.base_ty, base_place.place.base, projections, - ); - debug!("cat_field ret {:?}", ret); - ret + ) } #[instrument(level = "debug", skip(self))] @@ -497,7 +488,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_deref(expr, base) } - #[instrument(level = "debug", skip(self, node))] + #[instrument(level = "debug", skip(self, node), ret)] fn cat_deref( &self, node: &impl HirNode, @@ -514,14 +505,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { let mut projections = base_place.place.projections; projections.push(Projection { kind: ProjectionKind::Deref, ty: deref_ty }); - let ret = PlaceWithHirId::new( + Ok(PlaceWithHirId::new( node.hir_id(), base_place.place.base_ty, base_place.place.base, projections, - ); - debug!("cat_deref ret {:?}", ret); - Ok(ret) + )) } pub(crate) fn cat_pattern( @@ -603,6 +592,13 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } + /// Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it + /// is being matched against. + /// + /// In general, the way that this works is that we walk down the pattern, + /// constructing a `PlaceWithHirId` that represents the path that will be taken + /// to reach the value being matched. + #[instrument(skip(self, op), ret, level = "debug")] fn cat_pattern_( &self, mut place_with_id: PlaceWithHirId<'tcx>, @@ -612,15 +608,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { where F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>), { - // Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it - // is being matched against. - // - // In general, the way that this works is that we walk down the pattern, - // constructing a `PlaceWithHirId` that represents the path that will be taken - // to reach the value being matched. - - debug!("cat_pattern(pat={:?}, place_with_id={:?})", pat, place_with_id); - // If (pattern) adjustments are active for this pattern, adjust the `PlaceWithHirId` correspondingly. // `PlaceWithHirId`s are constructed differently from patterns. For example, in // @@ -654,11 +641,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. for _ in 0..self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len()) { - debug!("cat_pattern: applying adjustment to place_with_id={:?}", place_with_id); + debug!("applying adjustment to place_with_id={:?}", place_with_id); place_with_id = self.cat_deref(pat, place_with_id)?; } let place_with_id = place_with_id; // lose mutability - debug!("cat_pattern: applied adjustment derefs to get place_with_id={:?}", place_with_id); + debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id); // Invoke the callback, but only now, after the `place_with_id` has adjusted. // diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index fb81a8395d79b..08c05a67b6bb6 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -294,10 +294,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Equate the type variables for the upvars with the actual types. let final_upvar_tys = self.final_upvar_tys(closure_def_id); - debug!( - "analyze_closure: id={:?} args={:?} final_upvar_tys={:?}", - closure_hir_id, args, final_upvar_tys - ); + debug!(?closure_hir_id, ?args, ?final_upvar_tys); // Build a tuple (U0..Un) of the final upvar types U0..Un // and unify the upvar tuple type in the closure with it: @@ -338,10 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let upvar_ty = captured_place.place.ty(); let capture = captured_place.info.capture_kind; - debug!( - "final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}", - captured_place.place, upvar_ty, capture, captured_place.mutability, - ); + debug!(?captured_place.place, ?upvar_ty, ?capture, ?captured_place.mutability); apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture, captured_place.region) }) From d14569bd64e468bb08472f4885b4796ba8354474 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 24 Jul 2023 14:46:04 +0000 Subject: [PATCH 03/21] Simplify a shadowing replacement (that sometimes identity replaces) with mutation --- compiler/rustc_hir_typeck/src/upvar.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 08c05a67b6bb6..62eff4bab86c9 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -264,12 +264,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype(span, closure_kind.to_ty(self.tcx), closure_kind_ty); // If we have an origin, store it. - if let Some(origin) = origin { - let origin = if enable_precise_capture(span) { - (origin.0, origin.1) - } else { - (origin.0, Place { projections: vec![], ..origin.1 }) - }; + if let Some(mut origin) = origin { + if !enable_precise_capture(span) { + // Without precise captures, we just capture the base and ignore + // the projections. + origin.1.projections.clear() + } self.typeck_results .borrow_mut() From e390dc9c36448ca033aad48a6e7fd5ecc3f038d2 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 24 Jul 2023 14:47:32 +0000 Subject: [PATCH 04/21] Perform OpaqueCast field projection on HIR, too. This is necessary for closure captures in 2021 edition, as they capture individual fields, not the full mentioned variables. So it may try to capture a field of an opaque (because the hidden type is known to be something with a field). --- compiler/rustc_hir_typeck/src/mem_categorization.rs | 9 +++++++++ compiler/rustc_hir_typeck/src/upvar.rs | 5 +++++ compiler/rustc_middle/src/hir/place.rs | 4 ++++ compiler/rustc_middle/src/ty/closure.rs | 2 ++ compiler/rustc_mir_build/src/build/expr/as_place.rs | 3 +++ compiler/rustc_mir_build/src/thir/cx/expr.rs | 3 +++ src/tools/clippy/clippy_utils/src/sugg.rs | 2 ++ .../type-alias-impl-trait/issue-96572-unconstrained.rs | 2 ++ 8 files changed, 30 insertions(+) diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 9de4f82a06bdb..e91103f213015 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -457,7 +457,16 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { ty: Ty<'tcx>, kind: ProjectionKind, ) -> PlaceWithHirId<'tcx> { + let place_ty = base_place.place.ty(); let mut projections = base_place.place.projections; + + let node_ty = self.typeck_results.node_type(node.hir_id()); + // 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() { + projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty }); + } projections.push(Projection { kind, ty }); PlaceWithHirId::new( node.hir_id(), diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 62eff4bab86c9..facbb4b3cf885 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -673,6 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match (p1.kind, p2.kind) { // Paths are the same, continue to next loop. (ProjectionKind::Deref, ProjectionKind::Deref) => {} + (ProjectionKind::OpaqueCast, ProjectionKind::OpaqueCast) => {} (ProjectionKind::Field(i1, _), ProjectionKind::Field(i2, _)) if i1 == i2 => {} @@ -695,10 +696,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { l @ (ProjectionKind::Index | ProjectionKind::Subslice | ProjectionKind::Deref + | ProjectionKind::OpaqueCast | ProjectionKind::Field(..)), r @ (ProjectionKind::Index | ProjectionKind::Subslice | ProjectionKind::Deref + | ProjectionKind::OpaqueCast | ProjectionKind::Field(..)), ) => bug!( "ProjectionKinds Index or Subslice were unexpected: ({:?}, {:?})", @@ -1885,6 +1888,7 @@ fn restrict_capture_precision( return (place, curr_mode); } ProjectionKind::Deref => {} + ProjectionKind::OpaqueCast => {} ProjectionKind::Field(..) => {} // ignore } } @@ -1941,6 +1945,7 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String ProjectionKind::Deref => String::from("Deref"), ProjectionKind::Index => String::from("Index"), ProjectionKind::Subslice => String::from("Subslice"), + ProjectionKind::OpaqueCast => String::from("OpaqueCast"), }; if i != 0 { projections_str.push(','); diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index 8a22de931c35b..32f3a177508f7 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -36,6 +36,10 @@ pub enum ProjectionKind { /// A subslice covering a range of values like `B[x..y]`. Subslice, + + /// A conversion from an opaque type to its hidden type so we can + /// do further projections on it. + OpaqueCast, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 91eefa2c1251f..6cf4fef1d04e0 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -174,6 +174,8 @@ impl<'tcx> CapturedPlace<'tcx> { // Ignore derefs for now, as they are likely caused by // autoderefs that don't appear in the original code. HirProjectionKind::Deref => {} + // Just change the type to the hidden type, so we can actually project. + HirProjectionKind::OpaqueCast => {} proj => bug!("Unexpected projection {:?} in captured place", proj), } ty = proj.ty; diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 7756d5d48795f..2e7ef265a93c2 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -236,6 +236,9 @@ fn strip_prefix<'a, 'tcx>( } assert_matches!(iter.next(), Some(ProjectionElem::Field(..))); } + HirProjectionKind::OpaqueCast => { + assert_matches!(iter.next(), Some(ProjectionElem::OpaqueCast(..))); + } HirProjectionKind::Index | HirProjectionKind::Subslice => { bug!("unexpected projection kind: {:?}", projection); } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 711a9126c04a3..2f0f71a8dc6cf 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1074,6 +1074,9 @@ impl<'tcx> Cx<'tcx> { variant_index, name: field, }, + HirProjectionKind::OpaqueCast => { + ExprKind::Use { source: self.thir.exprs.push(captured_place_expr) } + } HirProjectionKind::Index | HirProjectionKind::Subslice => { // We don't capture these projections, so we can ignore them here continue; diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 5d7e1494fcfde..a43a81bc63a13 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -1011,6 +1011,8 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { }, // note: unable to trigger `Subslice` kind in tests ProjectionKind::Subslice => (), + // Doesn't have surface syntax. Only occurs in patterns. + ProjectionKind::OpaqueCast => (), ProjectionKind::Deref => { // Explicit derefs are typically handled later on, but // some items do not need explicit deref, such as array accesses, diff --git a/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs b/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs index 2c740ccc1aed4..fdd8fa65bd05f 100644 --- a/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs +++ b/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs @@ -1,5 +1,7 @@ #![feature(type_alias_impl_trait)] // check-pass +// revisions: default edition2021 +//[edition2021] compile-flags: --edition 2021 fn main() { type T = impl Copy; From 8e32dade71ad57978786537ec41c13222fd14611 Mon Sep 17 00:00:00 2001 From: bohan Date: Sun, 30 Jul 2023 15:49:33 +0800 Subject: [PATCH 05/21] parser: more friendly hints for handling `async move` in the 2015 edition --- compiler/rustc_parse/messages.ftl | 2 ++ compiler/rustc_parse/src/errors.rs | 7 +++++++ compiler/rustc_parse/src/parser/diagnostics.rs | 15 +++++++++++---- tests/ui/parser/issues/issue-114219.rs | 4 ++++ tests/ui/parser/issues/issue-114219.stderr | 8 ++++++++ 5 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/ui/parser/issues/issue-114219.rs create mode 100644 tests/ui/parser/issues/issue-114219.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 83d96ad8e7665..aec27060feda2 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -23,6 +23,8 @@ parse_async_block_in_2015 = `async` blocks are only allowed in Rust 2018 or late parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015 .label = to use `async fn`, switch to Rust 2018 or later +parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 2018 or later + parse_async_move_order_incorrect = the order of `move` and `async` is incorrect .suggestion = try switching the order diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 06c0996072730..8ac30a6a5fef2 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1434,6 +1434,13 @@ pub(crate) struct AsyncBlockIn2015 { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_async_move_block_in_2015)] +pub(crate) struct AsyncMoveBlockIn2015 { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_self_argument_pointer)] pub(crate) struct SelfArgumentPointer { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index e6de51a673c85..6156ebec2306c 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -4,10 +4,11 @@ use super::{ TokenExpectType, TokenType, }; use crate::errors::{ - AmbiguousPlus, AttributeOnParamType, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi, - ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg, - ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything, - DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, + AmbiguousPlus, AsyncMoveBlockIn2015, AttributeOnParamType, BadQPathStage2, BadTypePlus, + BadTypePlusSub, ColonAsSemi, ComparisonOperatorsCannotBeChained, + ComparisonOperatorsCannotBeChainedSugg, ConstGenericWithoutBraces, + ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything, DocCommentOnParamType, + DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg, @@ -573,6 +574,12 @@ impl<'a> Parser<'a> { return Err(self.sess.create_err(UseEqInstead { span: self.token.span })); } + if self.token.is_keyword(kw::Move) && self.prev_token.is_keyword(kw::Async) { + // The 2015 edition is in use because parsing of `async move` has failed. + let span = self.prev_token.span.to(self.token.span); + return Err(self.sess.create_err(AsyncMoveBlockIn2015 { span })); + } + let expect = tokens_to_string(&expected); let actual = super::token_descr(&self.token); let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 { diff --git a/tests/ui/parser/issues/issue-114219.rs b/tests/ui/parser/issues/issue-114219.rs new file mode 100644 index 0000000000000..332258b628c37 --- /dev/null +++ b/tests/ui/parser/issues/issue-114219.rs @@ -0,0 +1,4 @@ +fn main() { + async move {}; + //~^ ERROR `async move` blocks are only allowed in Rust 2018 or later +} diff --git a/tests/ui/parser/issues/issue-114219.stderr b/tests/ui/parser/issues/issue-114219.stderr new file mode 100644 index 0000000000000..90dcdc427757b --- /dev/null +++ b/tests/ui/parser/issues/issue-114219.stderr @@ -0,0 +1,8 @@ +error: `async move` blocks are only allowed in Rust 2018 or later + --> $DIR/issue-114219.rs:2:5 + | +LL | async move {}; + | ^^^^^^^^^^ + +error: aborting due to previous error + From 049c728c60f7c950b7185f0f277609694a8e2a16 Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Tue, 1 Aug 2023 23:30:40 +0800 Subject: [PATCH 06/21] Suggests turbofish in patterns --- compiler/rustc_parse/messages.ftl | 2 ++ compiler/rustc_parse/src/errors.rs | 14 ++++++++ compiler/rustc_parse/src/parser/pat.rs | 1 + compiler/rustc_parse/src/parser/path.rs | 10 +++++- tests/ui/did_you_mean/issue-114112.rs | 11 +++++++ tests/ui/did_you_mean/issue-114112.stderr | 13 ++++++++ tests/ui/parser/issues/issue-22647.rs | 2 +- tests/ui/parser/issues/issue-22647.stderr | 9 ++++-- tests/ui/parser/issues/issue-22712.rs | 2 +- tests/ui/parser/issues/issue-22712.stderr | 9 ++++-- tests/ui/parser/pat-lt-bracket-3.rs | 3 +- tests/ui/parser/pat-lt-bracket-3.stderr | 9 ++++-- tests/ui/parser/pat-lt-bracket-4.rs | 2 +- tests/ui/parser/pat-lt-bracket-4.stderr | 9 ++++-- tests/ui/span/issue-34264.rs | 2 +- tests/ui/span/issue-34264.stderr | 15 +++------ tests/ui/suggestions/issue-64252-self-type.rs | 6 ++-- .../suggestions/issue-64252-self-type.stderr | 32 +++++++------------ 18 files changed, 101 insertions(+), 50 deletions(-) create mode 100644 tests/ui/did_you_mean/issue-114112.rs create mode 100644 tests/ui/did_you_mean/issue-114112.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 83d96ad8e7665..6a9a5a239e48a 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -270,6 +270,8 @@ parse_found_expr_would_be_stmt = expected expression, found `{$token}` parse_function_body_equals_expr = function body cannot be `= expression;` .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` +parse_generic_args_in_pat_require_turbofish_syntax = generic args in patterns require the turbofish syntax + parse_generic_parameters_without_angle_brackets = generic parameters without surrounding angle brackets .suggestion = surround the type parameters with angle brackets diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 06c0996072730..3184ca777cef2 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2731,3 +2731,17 @@ pub(crate) struct WhereClauseBeforeConstBodySugg { #[suggestion_part(code = "")] pub right: Span, } + +#[derive(Diagnostic)] +#[diag(parse_generic_args_in_pat_require_turbofish_syntax)] +pub(crate) struct GenericArgsInPatRequireTurbofishSyntax { + #[primary_span] + pub span: Span, + #[suggestion( + parse_sugg_turbofish_syntax, + style = "verbose", + code = "::", + applicability = "maybe-incorrect" + )] + pub suggest_turbofish: Span, +} diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 14891c45d81a2..e0539c4ac04dc 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -805,6 +805,7 @@ impl<'a> Parser<'a> { | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern. | token::ModSep // A tuple / struct variant pattern. | token::Not)) // A macro expanding to a pattern. + && !(self.look_ahead(1, |t| t.kind == token::Lt) && self.look_ahead(2, |t| t.can_begin_type())) // May suggest the turbofish syntax for generics, only valid for recoveries. } /// Parses `ident` or `ident @ pat`. diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index feb7e829caf68..4fe8a5aa62609 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -1,6 +1,6 @@ use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{Parser, Restrictions, TokenType}; -use crate::errors::PathSingleColon; +use crate::errors::{GenericArgsInPatRequireTurbofishSyntax, PathSingleColon}; use crate::{errors, maybe_whole}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; @@ -382,6 +382,14 @@ impl<'a> Parser<'a> { }; PathSegment { ident, args: Some(args), id: ast::DUMMY_NODE_ID } + } else if style == PathStyle::Pat + && self.check_noexpect(&token::Lt) + && self.look_ahead(1, |t| t.can_begin_type()) + { + return Err(self.sess.create_err(GenericArgsInPatRequireTurbofishSyntax { + span: self.token.span, + suggest_turbofish: self.token.span.shrink_to_lo(), + })); } else { // Generic arguments are not found. PathSegment::from_ident(ident) diff --git a/tests/ui/did_you_mean/issue-114112.rs b/tests/ui/did_you_mean/issue-114112.rs new file mode 100644 index 0000000000000..0fde12ecd788f --- /dev/null +++ b/tests/ui/did_you_mean/issue-114112.rs @@ -0,0 +1,11 @@ +enum E { + A(T) +} + +fn main() { + match E::::A(1) { + E::A(v) => { //~ ERROR generic args in patterns require the turbofish syntax + println!("{v:?}"); + }, + } +} diff --git a/tests/ui/did_you_mean/issue-114112.stderr b/tests/ui/did_you_mean/issue-114112.stderr new file mode 100644 index 0000000000000..d76b5f72e30cf --- /dev/null +++ b/tests/ui/did_you_mean/issue-114112.stderr @@ -0,0 +1,13 @@ +error: generic args in patterns require the turbofish syntax + --> $DIR/issue-114112.rs:7:10 + | +LL | E::A(v) => { + | ^ + | +help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + | +LL | E::::A(v) => { + | ++ + +error: aborting due to previous error + diff --git a/tests/ui/parser/issues/issue-22647.rs b/tests/ui/parser/issues/issue-22647.rs index a6861410682cb..163cbc69ddd62 100644 --- a/tests/ui/parser/issues/issue-22647.rs +++ b/tests/ui/parser/issues/issue-22647.rs @@ -1,5 +1,5 @@ fn main() { - let caller = |f: F| //~ ERROR expected one of `:`, `;`, `=`, `@`, or `|`, found `<` + let caller = |f: F| //~ ERROR generic args in patterns require the turbofish syntax where F: Fn() -> i32 { let x = f(); diff --git a/tests/ui/parser/issues/issue-22647.stderr b/tests/ui/parser/issues/issue-22647.stderr index 89b454d1973d5..585e70266619a 100644 --- a/tests/ui/parser/issues/issue-22647.stderr +++ b/tests/ui/parser/issues/issue-22647.stderr @@ -1,8 +1,13 @@ -error: expected one of `:`, `;`, `=`, `@`, or `|`, found `<` +error: generic args in patterns require the turbofish syntax --> $DIR/issue-22647.rs:2:15 | LL | let caller = |f: F| - | ^ expected one of `:`, `;`, `=`, `@`, or `|` + | ^ + | +help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + | +LL | let caller:: = |f: F| + | ++ error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-22712.rs b/tests/ui/parser/issues/issue-22712.rs index 774de9c7e6448..92b12b8e1934c 100644 --- a/tests/ui/parser/issues/issue-22712.rs +++ b/tests/ui/parser/issues/issue-22712.rs @@ -3,7 +3,7 @@ struct Foo { } fn bar() { - let Foo> //~ ERROR expected one of `:`, `;`, `=`, `@`, or `|`, found `<` + let Foo> //~ ERROR generic args in patterns require the turbofish syntax } fn main() {} diff --git a/tests/ui/parser/issues/issue-22712.stderr b/tests/ui/parser/issues/issue-22712.stderr index 30fabac65640c..7f9d99d8edfa1 100644 --- a/tests/ui/parser/issues/issue-22712.stderr +++ b/tests/ui/parser/issues/issue-22712.stderr @@ -1,8 +1,13 @@ -error: expected one of `:`, `;`, `=`, `@`, or `|`, found `<` +error: generic args in patterns require the turbofish syntax --> $DIR/issue-22712.rs:6:12 | LL | let Foo> - | ^ expected one of `:`, `;`, `=`, `@`, or `|` + | ^ + | +help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + | +LL | let Foo::> + | ++ error: aborting due to previous error diff --git a/tests/ui/parser/pat-lt-bracket-3.rs b/tests/ui/parser/pat-lt-bracket-3.rs index a8bdfd3fa181b..bd83fe8db4b56 100644 --- a/tests/ui/parser/pat-lt-bracket-3.rs +++ b/tests/ui/parser/pat-lt-bracket-3.rs @@ -3,8 +3,7 @@ struct Foo(T, T); impl Foo { fn foo(&self) { match *self { - Foo(x, y) => { - //~^ error: expected one of `=>`, `@`, `if`, or `|`, found `<` + Foo(x, y) => { //~ ERROR generic args in patterns require the turbofish syntax println!("Goodbye, World!") } } diff --git a/tests/ui/parser/pat-lt-bracket-3.stderr b/tests/ui/parser/pat-lt-bracket-3.stderr index bacf868e3c4e7..afdf1e9a55762 100644 --- a/tests/ui/parser/pat-lt-bracket-3.stderr +++ b/tests/ui/parser/pat-lt-bracket-3.stderr @@ -1,8 +1,13 @@ -error: expected one of `=>`, `@`, `if`, or `|`, found `<` +error: generic args in patterns require the turbofish syntax --> $DIR/pat-lt-bracket-3.rs:6:16 | LL | Foo(x, y) => { - | ^ expected one of `=>`, `@`, `if`, or `|` + | ^ + | +help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + | +LL | Foo::(x, y) => { + | ++ error: aborting due to previous error diff --git a/tests/ui/parser/pat-lt-bracket-4.rs b/tests/ui/parser/pat-lt-bracket-4.rs index de314f6c6412d..6d348b68cd66b 100644 --- a/tests/ui/parser/pat-lt-bracket-4.rs +++ b/tests/ui/parser/pat-lt-bracket-4.rs @@ -5,7 +5,7 @@ enum BtNode { fn main() { let y = match 10 { - Foo::A(value) => value, //~ error: expected one of `=>`, `@`, `if`, or `|`, found `<` + Foo::A(value) => value, //~ ERROR generic args in patterns require the turbofish syntax Foo::B => 7, }; } diff --git a/tests/ui/parser/pat-lt-bracket-4.stderr b/tests/ui/parser/pat-lt-bracket-4.stderr index 911c276b9319a..b71a5ad939e59 100644 --- a/tests/ui/parser/pat-lt-bracket-4.stderr +++ b/tests/ui/parser/pat-lt-bracket-4.stderr @@ -1,8 +1,13 @@ -error: expected one of `=>`, `@`, `if`, or `|`, found `<` +error: generic args in patterns require the turbofish syntax --> $DIR/pat-lt-bracket-4.rs:8:12 | LL | Foo::A(value) => value, - | ^ expected one of `=>`, `@`, `if`, or `|` + | ^ + | +help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + | +LL | Foo::::A(value) => value, + | ++ error: aborting due to previous error diff --git a/tests/ui/span/issue-34264.rs b/tests/ui/span/issue-34264.rs index 9227ee482dfa5..c7a8440f28401 100644 --- a/tests/ui/span/issue-34264.rs +++ b/tests/ui/span/issue-34264.rs @@ -1,5 +1,5 @@ fn foo(Option, String) {} //~ ERROR expected one of -//~^ ERROR expected one of +//~^ ERROR generic args in patterns require the turbofish syntax fn bar(x, y: usize) {} //~ ERROR expected one of fn main() { diff --git a/tests/ui/span/issue-34264.stderr b/tests/ui/span/issue-34264.stderr index f0dea66f6128d..1874d7f853379 100644 --- a/tests/ui/span/issue-34264.stderr +++ b/tests/ui/span/issue-34264.stderr @@ -1,18 +1,13 @@ -error: expected one of `:`, `@`, or `|`, found `<` +error: generic args in patterns require the turbofish syntax --> $DIR/issue-34264.rs:1:14 | LL | fn foo(Option, String) {} - | ^ expected one of `:`, `@`, or `|` + | ^ | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | fn foo(self: Option, String) {} - | +++++ -help: if this is a type, explicitly ignore the parameter name +help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments | -LL | fn foo(_: Option, String) {} - | ++ +LL | fn foo(Option::, String) {} + | ++ error: expected one of `:`, `@`, or `|`, found `)` --> $DIR/issue-34264.rs:1:27 diff --git a/tests/ui/suggestions/issue-64252-self-type.rs b/tests/ui/suggestions/issue-64252-self-type.rs index 128d5e85c22c8..ad25d334507fc 100644 --- a/tests/ui/suggestions/issue-64252-self-type.rs +++ b/tests/ui/suggestions/issue-64252-self-type.rs @@ -1,14 +1,12 @@ // This test checks that a suggestion to add a `self: ` parameter name is provided // to functions where this is applicable. -pub fn foo(Box) { } -//~^ ERROR expected one of `:`, `@`, or `|`, found `<` +pub fn foo(Box) { } //~ ERROR generic args in patterns require the turbofish syntax struct Bar; impl Bar { - fn bar(Box) { } - //~^ ERROR expected one of `:`, `@`, or `|`, found `<` + fn bar(Box) { } //~ ERROR generic args in patterns require the turbofish syntax } fn main() { } diff --git a/tests/ui/suggestions/issue-64252-self-type.stderr b/tests/ui/suggestions/issue-64252-self-type.stderr index c3418dab0e8af..ba5c2da241500 100644 --- a/tests/ui/suggestions/issue-64252-self-type.stderr +++ b/tests/ui/suggestions/issue-64252-self-type.stderr @@ -1,34 +1,24 @@ -error: expected one of `:`, `@`, or `|`, found `<` +error: generic args in patterns require the turbofish syntax --> $DIR/issue-64252-self-type.rs:4:15 | LL | pub fn foo(Box) { } - | ^ expected one of `:`, `@`, or `|` + | ^ | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name +help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments | -LL | pub fn foo(self: Box) { } - | +++++ -help: if this is a type, explicitly ignore the parameter name - | -LL | pub fn foo(_: Box) { } - | ++ +LL | pub fn foo(Box::) { } + | ++ -error: expected one of `:`, `@`, or `|`, found `<` - --> $DIR/issue-64252-self-type.rs:10:15 +error: generic args in patterns require the turbofish syntax + --> $DIR/issue-64252-self-type.rs:9:15 | LL | fn bar(Box) { } - | ^ expected one of `:`, `@`, or `|` - | - = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name + | ^ | -LL | fn bar(self: Box) { } - | +++++ -help: if this is a type, explicitly ignore the parameter name +help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments | -LL | fn bar(_: Box) { } - | ++ +LL | fn bar(Box::) { } + | ++ error: aborting due to 2 previous errors From 743ae5a2eb5be808704dccb97a013add2edcca20 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 12 Jul 2023 16:17:58 +0200 Subject: [PATCH 07/21] Expand incorrect_fn_null_check lint with reference null checking --- compiler/rustc_lint/messages.ftl | 5 +- compiler/rustc_lint/src/fn_null_check.rs | 34 +++++++--- compiler/rustc_lint/src/lints.rs | 14 +++- tests/ui/lint/fn_null_check.rs | 21 ++++++ tests/ui/lint/fn_null_check.stderr | 82 +++++++++++++++++++++--- 5 files changed, 133 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 16e17fc9d6a5c..42748512f663e 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -213,9 +213,12 @@ lint_expectation = this lint expectation is unfulfilled .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message .rationale = {$rationale} -lint_fn_null_check = function pointers are not nullable, so checking them for null will always return false +lint_fn_null_check_fn_ptr = function pointers are not nullable, so checking them for null will always return false .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value +lint_fn_null_check_ref = references are not nullable, so checking them for null will always return false + .label = expression has type `{$orig_ty}` + lint_for_loops_over_fallibles = for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement .suggestion = consider using `if let` to clear intent diff --git a/compiler/rustc_lint/src/fn_null_check.rs b/compiler/rustc_lint/src/fn_null_check.rs index e3b33463ccfbc..b036c943f5a10 100644 --- a/compiler/rustc_lint/src/fn_null_check.rs +++ b/compiler/rustc_lint/src/fn_null_check.rs @@ -31,7 +31,7 @@ declare_lint! { declare_lint_pass!(IncorrectFnNullChecks => [INCORRECT_FN_NULL_CHECKS]); -fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { +fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option> { let mut expr = expr.peel_blocks(); let mut had_at_least_one_cast = false; while let ExprKind::Cast(cast_expr, cast_ty) = expr.kind @@ -39,7 +39,18 @@ fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { expr = cast_expr.peel_blocks(); had_at_least_one_cast = true; } - had_at_least_one_cast && cx.typeck_results().expr_ty_adjusted(expr).is_fn() + if !had_at_least_one_cast { + None + } else { + let orig_ty = cx.typeck_results().expr_ty(expr); + if orig_ty.is_fn() { + Some(FnNullCheckDiag::FnPtr) + } else if orig_ty.is_ref() { + Some(FnNullCheckDiag::Ref { orig_ty, label: expr.span }) + } else { + None + } + } } impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { @@ -54,9 +65,9 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_const_is_null | sym::ptr_is_null) ) - && is_fn_ptr_cast(cx, arg) => + && let Some(diag) = incorrect_check(cx, arg) => { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) + cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag) } // Catching: @@ -67,17 +78,20 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_const_is_null | sym::ptr_is_null) ) - && is_fn_ptr_cast(cx, receiver) => + && let Some(diag) = incorrect_check(cx, receiver) => { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) + cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag) } ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => { let to_check: &Expr<'_>; - if is_fn_ptr_cast(cx, left) { + let diag: FnNullCheckDiag<'_>; + if let Some(ddiag) = incorrect_check(cx, left) { to_check = right; - } else if is_fn_ptr_cast(cx, right) { + diag = ddiag; + } else if let Some(ddiag) = incorrect_check(cx, right) { to_check = left; + diag = ddiag; } else { return; } @@ -89,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { if let ExprKind::Lit(spanned) = cast_expr.kind && let LitKind::Int(v, _) = spanned.node && v == 0 => { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) + cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag) }, // Catching: @@ -100,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) && (diag_item == sym::ptr_null || diag_item == sym::ptr_null_mut) => { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) + cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag) }, _ => {}, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 968172693a93b..5f3b5c702d7ed 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -615,9 +615,17 @@ pub struct ExpectationNote { // fn_null_check.rs #[derive(LintDiagnostic)] -#[diag(lint_fn_null_check)] -#[help] -pub struct FnNullCheckDiag; +pub enum FnNullCheckDiag<'a> { + #[diag(lint_fn_null_check_fn_ptr)] + #[help(lint_help)] + FnPtr, + #[diag(lint_fn_null_check_ref)] + Ref { + orig_ty: Ty<'a>, + #[label] + label: Span, + }, +} // for_loops_over_fallibles.rs #[derive(LintDiagnostic)] diff --git a/tests/ui/lint/fn_null_check.rs b/tests/ui/lint/fn_null_check.rs index 7f01f2c428395..87b120fc131b1 100644 --- a/tests/ui/lint/fn_null_check.rs +++ b/tests/ui/lint/fn_null_check.rs @@ -3,6 +3,7 @@ fn main() { let fn_ptr = main; + // ------------- Function pointers --------------- if (fn_ptr as *mut ()).is_null() {} //~^ WARN function pointers are not nullable if (fn_ptr as *const u8).is_null() {} @@ -20,6 +21,26 @@ fn main() { if (fn_ptr as fn() as *const ()).is_null() {} //~^ WARN function pointers are not nullable + // ---------------- References ------------------ + if (&mut 8 as *mut i32).is_null() {} + //~^ WARN references are not nullable + if (&8 as *const i32).is_null() {} + //~^ WARN references are not nullable + if (&8 as *const i32) == std::ptr::null() {} + //~^ WARN references are not nullable + let ref_num = &8; + if (ref_num as *const i32) == std::ptr::null() {} + //~^ WARN references are not nullable + if (b"\0" as *const u8).is_null() {} + //~^ WARN references are not nullable + if ("aa" as *const str).is_null() {} + //~^ WARN references are not nullable + if (&[1, 2] as *const i32).is_null() {} + //~^ WARN references are not nullable + if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {} + //~^ WARN references are not nullable + + // ---------------------------------------------- const ZPTR: *const () = 0 as *const _; const NOT_ZPTR: *const () = 1 as *const _; diff --git a/tests/ui/lint/fn_null_check.stderr b/tests/ui/lint/fn_null_check.stderr index 0398c0da50feb..7a08c5bda2b67 100644 --- a/tests/ui/lint/fn_null_check.stderr +++ b/tests/ui/lint/fn_null_check.stderr @@ -1,5 +1,5 @@ warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:6:8 + --> $DIR/fn_null_check.rs:7:8 | LL | if (fn_ptr as *mut ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | if (fn_ptr as *mut ()).is_null() {} = note: `#[warn(incorrect_fn_null_checks)]` on by default warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:8:8 + --> $DIR/fn_null_check.rs:9:8 | LL | if (fn_ptr as *const u8).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | if (fn_ptr as *const u8).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:10:8 + --> $DIR/fn_null_check.rs:11:8 | LL | if (fn_ptr as *const ()) == std::ptr::null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | if (fn_ptr as *const ()) == std::ptr::null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:12:8 + --> $DIR/fn_null_check.rs:13:8 | LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:14:8 + --> $DIR/fn_null_check.rs:15:8 | LL | if (fn_ptr as *const ()) == (0 as *const ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | if (fn_ptr as *const ()) == (0 as *const ()) {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:16:8 + --> $DIR/fn_null_check.rs:17:8 | LL | if <*const _>::is_null(fn_ptr as *const ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | if <*const _>::is_null(fn_ptr as *const ()) {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:18:8 + --> $DIR/fn_null_check.rs:19:8 | LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -56,12 +56,76 @@ LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:20:8 + --> $DIR/fn_null_check.rs:21:8 | LL | if (fn_ptr as fn() as *const ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value -warning: 8 warnings emitted +warning: references are not nullable, so checking them for null will always return false + --> $DIR/fn_null_check.rs:25:8 + | +LL | if (&mut 8 as *mut i32).is_null() {} + | ^------^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&mut i32` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/fn_null_check.rs:27:8 + | +LL | if (&8 as *const i32).is_null() {} + | ^--^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&i32` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/fn_null_check.rs:29:8 + | +LL | if (&8 as *const i32) == std::ptr::null() {} + | ^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&i32` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/fn_null_check.rs:32:8 + | +LL | if (ref_num as *const i32) == std::ptr::null() {} + | ^-------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&i32` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/fn_null_check.rs:34:8 + | +LL | if (b"\0" as *const u8).is_null() {} + | ^-----^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&[u8; 1]` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/fn_null_check.rs:36:8 + | +LL | if ("aa" as *const str).is_null() {} + | ^----^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&str` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/fn_null_check.rs:38:8 + | +LL | if (&[1, 2] as *const i32).is_null() {} + | ^-------^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&[i32; 2]` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/fn_null_check.rs:40:8 + | +LL | if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {} + | ^-----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&mut [i32; 2]` + +warning: 16 warnings emitted From d2b7c8028f92640421762f0941c42a21d280002e Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 13 Jul 2023 12:01:13 +0200 Subject: [PATCH 08/21] Rename incorrect_fn_null_checks to useless_ptr_null_checks --- compiler/rustc_lint/messages.ftl | 12 +++---- compiler/rustc_lint/src/lib.rs | 6 ++-- compiler/rustc_lint/src/lints.rs | 8 ++--- .../src/{fn_null_check.rs => ptr_nulls.rs} | 34 +++++++++---------- .../{fn_null_check.rs => ptr_null_checks.rs} | 0 ...ll_check.stderr => ptr_null_checks.stderr} | 34 +++++++++---------- 6 files changed, 47 insertions(+), 47 deletions(-) rename compiler/rustc_lint/src/{fn_null_check.rs => ptr_nulls.rs} (78%) rename tests/ui/lint/{fn_null_check.rs => ptr_null_checks.rs} (100%) rename tests/ui/lint/{fn_null_check.stderr => ptr_null_checks.stderr} (88%) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 42748512f663e..4897ffd0cece5 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -213,12 +213,6 @@ lint_expectation = this lint expectation is unfulfilled .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message .rationale = {$rationale} -lint_fn_null_check_fn_ptr = function pointers are not nullable, so checking them for null will always return false - .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value - -lint_fn_null_check_ref = references are not nullable, so checking them for null will always return false - .label = expression has type `{$orig_ty}` - lint_for_loops_over_fallibles = for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement .suggestion = consider using `if let` to clear intent @@ -453,6 +447,12 @@ lint_path_statement_drop = path statement drops value lint_path_statement_no_effect = path statement with no effect +lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false + .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value + +lint_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false + .label = expression has type `{$orig_ty}` + lint_query_instability = using `{$query}` can result in unstable query results .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 53089294fe2e6..0298c24fbbacf 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -57,7 +57,6 @@ mod early; mod enum_intrinsics_non_enums; mod errors; mod expect; -mod fn_null_check; mod for_loops_over_fallibles; pub mod hidden_unicode_codepoints; mod internal; @@ -76,6 +75,7 @@ mod noop_method_call; mod opaque_hidden_inferred_bound; mod pass_by_value; mod passes; +mod ptr_nulls; mod redundant_semicolon; mod reference_casting; mod traits; @@ -102,7 +102,6 @@ use builtin::*; use deref_into_dyn_supertrait::*; use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; -use fn_null_check::*; use for_loops_over_fallibles::*; use hidden_unicode_codepoints::*; use internal::*; @@ -117,6 +116,7 @@ use nonstandard_style::*; use noop_method_call::*; use opaque_hidden_inferred_bound::*; use pass_by_value::*; +use ptr_nulls::*; use redundant_semicolon::*; use reference_casting::*; use traits::*; @@ -227,7 +227,7 @@ late_lint_methods!( // Depends on types used in type definitions MissingCopyImplementations: MissingCopyImplementations, // Depends on referenced function signatures in expressions - IncorrectFnNullChecks: IncorrectFnNullChecks, + PtrNullChecks: PtrNullChecks, MutableTransmutes: MutableTransmutes, TypeAliasBounds: TypeAliasBounds, TrivialConstraints: TrivialConstraints, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 5f3b5c702d7ed..9e1d5605260c3 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -613,13 +613,13 @@ pub struct ExpectationNote { pub rationale: Symbol, } -// fn_null_check.rs +// ptr_nulls.rs #[derive(LintDiagnostic)] -pub enum FnNullCheckDiag<'a> { - #[diag(lint_fn_null_check_fn_ptr)] +pub enum PtrNullChecksDiag<'a> { + #[diag(lint_ptr_null_checks_fn_ptr)] #[help(lint_help)] FnPtr, - #[diag(lint_fn_null_check_ref)] + #[diag(lint_ptr_null_checks_ref)] Ref { orig_ty: Ty<'a>, #[label] diff --git a/compiler/rustc_lint/src/fn_null_check.rs b/compiler/rustc_lint/src/ptr_nulls.rs similarity index 78% rename from compiler/rustc_lint/src/fn_null_check.rs rename to compiler/rustc_lint/src/ptr_nulls.rs index b036c943f5a10..b2138c42224b4 100644 --- a/compiler/rustc_lint/src/fn_null_check.rs +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -1,12 +1,12 @@ -use crate::{lints::FnNullCheckDiag, LateContext, LateLintPass, LintContext}; +use crate::{lints::PtrNullChecksDiag, LateContext, LateLintPass, LintContext}; use rustc_ast::LitKind; use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; declare_lint! { - /// The `incorrect_fn_null_checks` lint checks for expression that checks if a - /// function pointer is null. + /// The `useless_ptr_null_checks` lint checks for useless null checks against pointers + /// obtained from non-null types. /// /// ### Example /// @@ -22,16 +22,16 @@ declare_lint! { /// /// ### Explanation /// - /// Function pointers are assumed to be non-null, checking them for null will always - /// return false. - INCORRECT_FN_NULL_CHECKS, + /// Function pointers and references are assumed to be non-null, checking them for null + /// will always return false. + USELESS_PTR_NULL_CHECKS, Warn, - "incorrect checking of null function pointer" + "useless checking of non-null-typed pointer" } -declare_lint_pass!(IncorrectFnNullChecks => [INCORRECT_FN_NULL_CHECKS]); +declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]); -fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option> { +fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option> { let mut expr = expr.peel_blocks(); let mut had_at_least_one_cast = false; while let ExprKind::Cast(cast_expr, cast_ty) = expr.kind @@ -44,16 +44,16 @@ fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option LateLintPass<'tcx> for IncorrectFnNullChecks { +impl<'tcx> LateLintPass<'tcx> for PtrNullChecks { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match expr.kind { // Catching: @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { ) && let Some(diag) = incorrect_check(cx, arg) => { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag) + cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) } // Catching: @@ -80,12 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { ) && let Some(diag) = incorrect_check(cx, receiver) => { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag) + cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) } ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => { let to_check: &Expr<'_>; - let diag: FnNullCheckDiag<'_>; + let diag: PtrNullChecksDiag<'_>; if let Some(ddiag) = incorrect_check(cx, left) { to_check = right; diag = ddiag; @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { if let ExprKind::Lit(spanned) = cast_expr.kind && let LitKind::Int(v, _) = spanned.node && v == 0 => { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag) + cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) }, // Catching: @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) && (diag_item == sym::ptr_null || diag_item == sym::ptr_null_mut) => { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, diag) + cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) }, _ => {}, diff --git a/tests/ui/lint/fn_null_check.rs b/tests/ui/lint/ptr_null_checks.rs similarity index 100% rename from tests/ui/lint/fn_null_check.rs rename to tests/ui/lint/ptr_null_checks.rs diff --git a/tests/ui/lint/fn_null_check.stderr b/tests/ui/lint/ptr_null_checks.stderr similarity index 88% rename from tests/ui/lint/fn_null_check.stderr rename to tests/ui/lint/ptr_null_checks.stderr index 7a08c5bda2b67..62ce910c7169b 100644 --- a/tests/ui/lint/fn_null_check.stderr +++ b/tests/ui/lint/ptr_null_checks.stderr @@ -1,14 +1,14 @@ warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:7:8 + --> $DIR/ptr_null_checks.rs:7:8 | LL | if (fn_ptr as *mut ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value - = note: `#[warn(incorrect_fn_null_checks)]` on by default + = note: `#[warn(useless_ptr_null_checks)]` on by default warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:9:8 + --> $DIR/ptr_null_checks.rs:9:8 | LL | if (fn_ptr as *const u8).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | if (fn_ptr as *const u8).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:11:8 + --> $DIR/ptr_null_checks.rs:11:8 | LL | if (fn_ptr as *const ()) == std::ptr::null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | if (fn_ptr as *const ()) == std::ptr::null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:13:8 + --> $DIR/ptr_null_checks.rs:13:8 | LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:15:8 + --> $DIR/ptr_null_checks.rs:15:8 | LL | if (fn_ptr as *const ()) == (0 as *const ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | if (fn_ptr as *const ()) == (0 as *const ()) {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:17:8 + --> $DIR/ptr_null_checks.rs:17:8 | LL | if <*const _>::is_null(fn_ptr as *const ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | if <*const _>::is_null(fn_ptr as *const ()) {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:19:8 + --> $DIR/ptr_null_checks.rs:19:8 | LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:21:8 + --> $DIR/ptr_null_checks.rs:21:8 | LL | if (fn_ptr as fn() as *const ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | if (fn_ptr as fn() as *const ()).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: references are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:25:8 + --> $DIR/ptr_null_checks.rs:25:8 | LL | if (&mut 8 as *mut i32).is_null() {} | ^------^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | if (&mut 8 as *mut i32).is_null() {} | expression has type `&mut i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:27:8 + --> $DIR/ptr_null_checks.rs:27:8 | LL | if (&8 as *const i32).is_null() {} | ^--^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | if (&8 as *const i32).is_null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:29:8 + --> $DIR/ptr_null_checks.rs:29:8 | LL | if (&8 as *const i32) == std::ptr::null() {} | ^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | if (&8 as *const i32) == std::ptr::null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:32:8 + --> $DIR/ptr_null_checks.rs:32:8 | LL | if (ref_num as *const i32) == std::ptr::null() {} | ^-------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | if (ref_num as *const i32) == std::ptr::null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:34:8 + --> $DIR/ptr_null_checks.rs:34:8 | LL | if (b"\0" as *const u8).is_null() {} | ^-----^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +104,7 @@ LL | if (b"\0" as *const u8).is_null() {} | expression has type `&[u8; 1]` warning: references are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:36:8 + --> $DIR/ptr_null_checks.rs:36:8 | LL | if ("aa" as *const str).is_null() {} | ^----^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +112,7 @@ LL | if ("aa" as *const str).is_null() {} | expression has type `&str` warning: references are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:38:8 + --> $DIR/ptr_null_checks.rs:38:8 | LL | if (&[1, 2] as *const i32).is_null() {} | ^-------^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | if (&[1, 2] as *const i32).is_null() {} | expression has type `&[i32; 2]` warning: references are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:40:8 + --> $DIR/ptr_null_checks.rs:40:8 | LL | if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {} | ^-----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 84c5372a45f0eeffa3cf8b6bdc0e981c6eea299e Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 13 Jul 2023 12:23:06 +0200 Subject: [PATCH 09/21] Rename incorrect_fn_null_checks to useless_ptr_null_checks (clippy side) --- src/tools/clippy/clippy_lints/src/renamed_lints.rs | 2 +- src/tools/clippy/tests/ui/rename.fixed | 4 ++-- src/tools/clippy/tests/ui/rename.rs | 2 +- src/tools/clippy/tests/ui/rename.stderr | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs index 49bdc6796049a..fc1fabcc0ae59 100644 --- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs +++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs @@ -43,7 +43,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), ("clippy::forget_copy", "forgetting_copy_types"), ("clippy::forget_ref", "forgetting_references"), - ("clippy::fn_null_check", "incorrect_fn_null_checks"), + ("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/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 8ec19b120d127..8257bf2947ab0 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -38,7 +38,7 @@ #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] #![allow(forgetting_references)] -#![allow(incorrect_fn_null_checks)] +#![allow(useless_ptr_null_checks)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] @@ -92,7 +92,7 @@ #![warn(for_loops_over_fallibles)] #![warn(forgetting_copy_types)] #![warn(forgetting_references)] -#![warn(incorrect_fn_null_checks)] +#![warn(useless_ptr_null_checks)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 51a5976eb6149..6569dad18d406 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -38,7 +38,7 @@ #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] #![allow(forgetting_references)] -#![allow(incorrect_fn_null_checks)] +#![allow(useless_ptr_null_checks)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index e420ea1d2adb5..57e991e5695a1 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -246,11 +246,11 @@ error: lint `clippy::forget_ref` has been renamed to `forgetting_references` LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` -error: lint `clippy::fn_null_check` has been renamed to `incorrect_fn_null_checks` +error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::fn_null_check)] - | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `incorrect_fn_null_checks` + | ^^^^^^^^^^^^^^^^^^^^^ 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:96:9 From ef3413d423299039cdf807b2e9484e7825cc21cd Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 13 Jul 2023 13:38:31 +0200 Subject: [PATCH 10/21] Add more tests for useless_ptr_null_checks lint --- tests/ui/lint/ptr_null_checks.rs | 9 +++++ tests/ui/lint/ptr_null_checks.stderr | 58 ++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/tests/ui/lint/ptr_null_checks.rs b/tests/ui/lint/ptr_null_checks.rs index 87b120fc131b1..600ca32ca8937 100644 --- a/tests/ui/lint/ptr_null_checks.rs +++ b/tests/ui/lint/ptr_null_checks.rs @@ -1,5 +1,8 @@ // check-pass +extern "C" fn c_fn() {} +fn static_i32() -> &'static i32 { &1 } + fn main() { let fn_ptr = main; @@ -20,6 +23,8 @@ fn main() { //~^ WARN function pointers are not nullable if (fn_ptr as fn() as *const ()).is_null() {} //~^ WARN function pointers are not nullable + if (c_fn as *const fn()).is_null() {} + //~^ WARN function pointers are not nullable // ---------------- References ------------------ if (&mut 8 as *mut i32).is_null() {} @@ -39,6 +44,10 @@ fn main() { //~^ WARN references are not nullable if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {} //~^ WARN references are not nullable + if (static_i32() as *const i32).is_null() {} + //~^ WARN references are not nullable + if (&*{ static_i32() } as *const i32).is_null() {} + //~^ WARN references are not nullable // ---------------------------------------------- const ZPTR: *const () = 0 as *const _; diff --git a/tests/ui/lint/ptr_null_checks.stderr b/tests/ui/lint/ptr_null_checks.stderr index 62ce910c7169b..10efa8685befd 100644 --- a/tests/ui/lint/ptr_null_checks.stderr +++ b/tests/ui/lint/ptr_null_checks.stderr @@ -1,5 +1,5 @@ warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:7:8 + --> $DIR/ptr_null_checks.rs:10:8 | LL | if (fn_ptr as *mut ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | if (fn_ptr as *mut ()).is_null() {} = note: `#[warn(useless_ptr_null_checks)]` on by default warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:9:8 + --> $DIR/ptr_null_checks.rs:12:8 | LL | if (fn_ptr as *const u8).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | if (fn_ptr as *const u8).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:11:8 + --> $DIR/ptr_null_checks.rs:14:8 | LL | if (fn_ptr as *const ()) == std::ptr::null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | if (fn_ptr as *const ()) == std::ptr::null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:13:8 + --> $DIR/ptr_null_checks.rs:16:8 | LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:15:8 + --> $DIR/ptr_null_checks.rs:18:8 | LL | if (fn_ptr as *const ()) == (0 as *const ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | if (fn_ptr as *const ()) == (0 as *const ()) {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:17:8 + --> $DIR/ptr_null_checks.rs:20:8 | LL | if <*const _>::is_null(fn_ptr as *const ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | if <*const _>::is_null(fn_ptr as *const ()) {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:19:8 + --> $DIR/ptr_null_checks.rs:22:8 | LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -56,15 +56,23 @@ LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:21:8 + --> $DIR/ptr_null_checks.rs:24:8 | LL | if (fn_ptr as fn() as *const ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value +warning: function pointers are not nullable, so checking them for null will always return false + --> $DIR/ptr_null_checks.rs:26:8 + | +LL | if (c_fn as *const fn()).is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value + warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:25:8 + --> $DIR/ptr_null_checks.rs:30:8 | LL | if (&mut 8 as *mut i32).is_null() {} | ^------^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +80,7 @@ LL | if (&mut 8 as *mut i32).is_null() {} | expression has type `&mut i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:27:8 + --> $DIR/ptr_null_checks.rs:32:8 | LL | if (&8 as *const i32).is_null() {} | ^--^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +88,7 @@ LL | if (&8 as *const i32).is_null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:29:8 + --> $DIR/ptr_null_checks.rs:34:8 | LL | if (&8 as *const i32) == std::ptr::null() {} | ^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +96,7 @@ LL | if (&8 as *const i32) == std::ptr::null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:32:8 + --> $DIR/ptr_null_checks.rs:37:8 | LL | if (ref_num as *const i32) == std::ptr::null() {} | ^-------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +104,7 @@ LL | if (ref_num as *const i32) == std::ptr::null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:34:8 + --> $DIR/ptr_null_checks.rs:39:8 | LL | if (b"\0" as *const u8).is_null() {} | ^-----^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +112,7 @@ LL | if (b"\0" as *const u8).is_null() {} | expression has type `&[u8; 1]` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:36:8 + --> $DIR/ptr_null_checks.rs:41:8 | LL | if ("aa" as *const str).is_null() {} | ^----^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +120,7 @@ LL | if ("aa" as *const str).is_null() {} | expression has type `&str` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:38:8 + --> $DIR/ptr_null_checks.rs:43:8 | LL | if (&[1, 2] as *const i32).is_null() {} | ^-------^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +128,28 @@ LL | if (&[1, 2] as *const i32).is_null() {} | expression has type `&[i32; 2]` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:40:8 + --> $DIR/ptr_null_checks.rs:45:8 | LL | if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {} | ^-----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | expression has type `&mut [i32; 2]` -warning: 16 warnings emitted +warning: references are not nullable, so checking them for null will always return false + --> $DIR/ptr_null_checks.rs:47:8 + | +LL | if (static_i32() as *const i32).is_null() {} + | ^------------^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&i32` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/ptr_null_checks.rs:49:8 + | +LL | if (&*{ static_i32() } as *const i32).is_null() {} + | ^------------------^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&i32` + +warning: 19 warnings emitted From 0b9529cca39d86138066012e85cae96ba4a7c253 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 13 Jul 2023 14:11:29 +0200 Subject: [PATCH 11/21] Add diagnostic items for `<*const _>::cast` and `ptr::from_mut` --- compiler/rustc_span/src/symbol.rs | 2 ++ library/core/src/ptr/mod.rs | 1 + library/core/src/ptr/mut_ptr.rs | 1 + 3 files changed, 4 insertions(+) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d3739733c1d10..9cff8a688ff84 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1155,8 +1155,10 @@ symbols! { profiler_builtins, profiler_runtime, ptr, + ptr_cast, ptr_cast_mut, ptr_const_is_null, + ptr_from_mut, ptr_from_ref, ptr_guaranteed_cmp, ptr_is_null, diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index acc9ca29d41a1..5f094ac4e7e64 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -710,6 +710,7 @@ pub const fn from_ref(r: &T) -> *const T { #[inline(always)] #[must_use] #[unstable(feature = "ptr_from_ref", issue = "106116")] +#[rustc_diagnostic_item = "ptr_from_mut"] pub const fn from_mut(r: &mut T) -> *mut T { r } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index e7f27439540b9..e3a3f69afd9a2 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -54,6 +54,7 @@ impl *mut T { /// Casts to a pointer of another type. #[stable(feature = "ptr_cast", since = "1.38.0")] #[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")] + #[rustc_diagnostic_item = "ptr_cast"] #[inline(always)] pub const fn cast(self) -> *mut U { self as _ From 89b2fe7750eb251c2b621bad1f8d86477d7a1f91 Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Thu, 3 Aug 2023 00:00:56 +0800 Subject: [PATCH 12/21] Keep the suggestion for wrong arbitrary self types --- compiler/rustc_parse/src/parser/attr.rs | 4 +- .../rustc_parse/src/parser/diagnostics.rs | 26 +++++++++---- compiler/rustc_parse/src/parser/expr.rs | 10 ++--- compiler/rustc_parse/src/parser/item.rs | 6 +-- compiler/rustc_parse/src/parser/mod.rs | 6 +-- .../rustc_parse/src/parser/nonterminal.rs | 4 +- compiler/rustc_parse/src/parser/pat.rs | 36 ++++++++++------- compiler/rustc_parse/src/parser/path.rs | 39 ++++++++++++------- compiler/rustc_parse/src/parser/stmt.rs | 2 +- compiler/rustc_parse/src/parser/ty.rs | 6 +-- .../anon-params-denied-2018.stderr | 20 +++++++--- tests/ui/span/issue-34264.rs | 2 +- tests/ui/span/issue-34264.stderr | 15 ++++--- tests/ui/suggestions/issue-64252-self-type.rs | 5 +-- .../suggestions/issue-64252-self-type.stderr | 32 +++++++++------ 15 files changed, 133 insertions(+), 80 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index ee0abba1c1753..1271bce799d5c 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -260,7 +260,7 @@ impl<'a> Parser<'a> { item } else { let do_parse = |this: &mut Self| { - let path = this.parse_path(PathStyle::Mod)?; + let path = this.parse_path(PathStyle::Mod, None)?; let args = this.parse_attr_args()?; Ok(ast::AttrItem { path, args, tokens: None }) }; @@ -387,7 +387,7 @@ impl<'a> Parser<'a> { } let lo = self.token.span; - let path = self.parse_path(PathStyle::Mod)?; + let path = self.parse_path(PathStyle::Mod, None)?; let kind = self.parse_meta_item_kind()?; let span = lo.to(self.prev_token.span); Ok(ast::MetaItem { path, kind, span }) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 7d04a335c9e0b..8bc4433a9653a 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1579,7 +1579,7 @@ impl<'a> Parser<'a> { self.expect(&token::ModSep)?; let mut path = ast::Path { segments: ThinVec::new(), span: DUMMY_SP, tokens: None }; - self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?; + self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None, None)?; path.span = ty_span.to(self.prev_token.span); let ty_str = self.span_to_snippet(ty_span).unwrap_or_else(|_| pprust::ty_to_string(&ty)); @@ -2019,7 +2019,7 @@ impl<'a> Parser<'a> { { let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)"; - let (ident, self_sugg, param_sugg, type_sugg, self_span, param_span, type_span) = + let (ident, self_sugg, param_sugg, type_sugg, self_span, param_span, type_span, maybe_name) = match pat.kind { PatKind::Ident(_, ident, _) => ( ident, @@ -2029,6 +2029,7 @@ impl<'a> Parser<'a> { pat.span.shrink_to_lo(), pat.span.shrink_to_hi(), pat.span.shrink_to_lo(), + true, ), // Also catches `fn foo(&a)`. PatKind::Ref(ref inner_pat, mutab) @@ -2045,11 +2046,22 @@ impl<'a> Parser<'a> { pat.span.shrink_to_lo(), pat.span, pat.span.shrink_to_lo(), + true, ) } _ => unreachable!(), } - } + }, + PatKind::Path(_, ref path) if let Some(segment) = path.segments.last() => ( + segment.ident, + "self: ", + ": TypeName".to_string(), + "_: ", + pat.span.shrink_to_lo(), + pat.span.shrink_to_hi(), + pat.span.shrink_to_lo(), + path.segments.len() == 1, // Avoid suggesting that `fn foo(a::b)` is fixed with a change to `fn foo(a::b: TypeName)`. + ), _ => { // Otherwise, try to get a type and emit a suggestion. if let Some(ty) = pat.to_ty() { @@ -2077,7 +2089,7 @@ impl<'a> Parser<'a> { } // Avoid suggesting that `fn foo(HashMap)` is fixed with a change to // `fn foo(HashMap: TypeName)`. - if self.token != token::Lt { + if self.token != token::Lt && maybe_name { err.span_suggestion( param_span, "if this is a parameter name, give it a type", @@ -2100,7 +2112,7 @@ impl<'a> Parser<'a> { } pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P, P)> { - let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName))?; + let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?; self.expect(&token::Colon)?; let ty = self.parse_ty()?; @@ -2508,7 +2520,7 @@ impl<'a> Parser<'a> { // Skip the `:`. snapshot_pat.bump(); snapshot_type.bump(); - match snapshot_pat.parse_pat_no_top_alt(expected) { + match snapshot_pat.parse_pat_no_top_alt(expected, None) { Err(inner_err) => { inner_err.cancel(); } @@ -2772,7 +2784,7 @@ impl<'a> Parser<'a> { /// sequence of patterns until `)` is reached. fn skip_pat_list(&mut self) -> PResult<'a, ()> { while !self.check(&token::CloseDelim(Delimiter::Parenthesis)) { - self.parse_pat_no_top_alt(None)?; + self.parse_pat_no_top_alt(None, None)?; if !self.eat(&token::Comma) { return Ok(()); } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index b54cb8c5a0c10..6dafb8b999b2f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -775,7 +775,7 @@ impl<'a> Parser<'a> { _ => {} } - match self.parse_path(PathStyle::Expr) { + match self.parse_path(PathStyle::Expr, None) { Ok(path) => { let span_after_type = parser_snapshot_after_type.token.span; let expr = mk_expr( @@ -1314,7 +1314,7 @@ impl<'a> Parser<'a> { } let fn_span_lo = self.token.span; - let mut seg = self.parse_path_segment(PathStyle::Expr, None)?; + let mut seg = self.parse_path_segment(PathStyle::Expr, None, None)?; self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]); self.check_turbofish_missing_angle_brackets(&mut seg); @@ -1544,7 +1544,7 @@ impl<'a> Parser<'a> { })?; (Some(qself), path) } else { - (None, self.parse_path(PathStyle::Expr)?) + (None, self.parse_path(PathStyle::Expr, None)?) }; // `!`, as an operator, is prefix, so we know this isn't that. @@ -2338,7 +2338,7 @@ impl<'a> Parser<'a> { let lo = self.token.span; let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { - let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName))?; + let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?; let ty = if this.eat(&token::Colon) { this.parse_ty()? } else { @@ -2781,7 +2781,7 @@ impl<'a> Parser<'a> { return None; } let pre_pat_snapshot = self.create_snapshot_for_diagnostic(); - match self.parse_pat_no_top_alt(None) { + match self.parse_pat_no_top_alt(None, None) { Ok(_pat) => { if self.token.kind == token::FatArrow { // Reached arm end. diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 1301ed3e3882e..d3f756139def3 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -452,7 +452,7 @@ impl<'a> Parser<'a> { /// Parses an item macro, e.g., `item!();`. fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, MacCall> { - let path = self.parse_path(PathStyle::Mod)?; // `foo::bar` + let path = self.parse_path(PathStyle::Mod, None)?; // `foo::bar` self.expect(&token::Not)?; // `!` match self.parse_delim_args() { // `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`. @@ -976,7 +976,7 @@ impl<'a> Parser<'a> { self.parse_use_tree_glob_or_nested()? } else { // `use path::*;` or `use path::{...};` or `use path;` or `use path as bar;` - prefix = self.parse_path(PathStyle::Mod)?; + prefix = self.parse_path(PathStyle::Mod, None)?; if self.eat(&token::ModSep) { self.parse_use_tree_glob_or_nested()? @@ -987,7 +987,7 @@ impl<'a> Parser<'a> { .emit_err(errors::SingleColonImportPath { span: self.prev_token.span }); // We parse the rest of the path and append it to the original prefix. - self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None)?; + self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None, None)?; prefix.span = lo.to(self.prev_token.span); } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 37b4c371c94b4..2af0caa1bdaf7 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1413,7 +1413,7 @@ impl<'a> Parser<'a> { // Parse `pub(in path)`. self.bump(); // `(` self.bump(); // `in` - let path = self.parse_path(PathStyle::Mod)?; // `path` + let path = self.parse_path(PathStyle::Mod, None)?; // `path` self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` let vis = VisibilityKind::Restricted { path: P(path), @@ -1430,7 +1430,7 @@ impl<'a> Parser<'a> { { // Parse `pub(crate)`, `pub(self)`, or `pub(super)`. self.bump(); // `(` - let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self` + let path = self.parse_path(PathStyle::Mod, None)?; // `crate`/`super`/`self` self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` let vis = VisibilityKind::Restricted { path: P(path), @@ -1456,7 +1456,7 @@ impl<'a> Parser<'a> { /// Recovery for e.g. `pub(something) fn ...` or `struct X { pub(something) y: Z }` fn recover_incorrect_vis_restriction(&mut self) -> PResult<'a, ()> { self.bump(); // `(` - let path = self.parse_path(PathStyle::Mod)?; + let path = self.parse_path(PathStyle::Mod, None)?; self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` let path_str = pprust::path_to_string(&path); diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index adb0d372a40df..a09c1e4d7fde2 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -131,7 +131,7 @@ impl<'a> Parser<'a> { }, NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => { token::NtPat(self.collect_tokens_no_attrs(|this| match kind { - NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None), + NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None, None), NonterminalKind::PatWithOr { .. } => this.parse_pat_allow_top_alt( None, RecoverComma::No, @@ -168,7 +168,7 @@ impl<'a> Parser<'a> { }.into_diagnostic(&self.sess.span_diagnostic)); } NonterminalKind::Path => token::NtPath( - P(self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?), + P(self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type, None))?), ), NonterminalKind::Meta => token::NtMeta(P(self.parse_attr_item(true)?)), NonterminalKind::Vis => token::NtVis( diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index e0539c4ac04dc..615ef28db085d 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -80,7 +80,8 @@ enum EatOrResult { } /// The syntax location of a given pattern. Used for diagnostics. -pub(super) enum PatternLocation { +#[derive(Clone, Copy)] +pub enum PatternLocation { LetBinding, FunctionParameter, } @@ -91,8 +92,12 @@ impl<'a> Parser<'a> { /// Corresponds to `pat` in RFC 2535 and does not admit or-patterns /// at the top level. Used when parsing the parameters of lambda expressions, /// functions, function pointers, and `pat` macro fragments. - pub fn parse_pat_no_top_alt(&mut self, expected: Option) -> PResult<'a, P> { - self.parse_pat_with_range_pat(true, expected) + pub fn parse_pat_no_top_alt( + &mut self, + expected: Option, + syntax_loc: Option, + ) -> PResult<'a, P> { + self.parse_pat_with_range_pat(true, expected, syntax_loc) } /// Parses a pattern. @@ -110,7 +115,7 @@ impl<'a> Parser<'a> { ra: RecoverColon, rt: CommaRecoveryMode, ) -> PResult<'a, P> { - self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt).map(|(pat, _)| pat) + self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) } /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = @@ -121,6 +126,7 @@ impl<'a> Parser<'a> { rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, + syntax_loc: Option, ) -> PResult<'a, (P, bool)> { // Keep track of whether we recovered from a trailing vert so that we can avoid duplicated // suggestions (which bothers rustfix). @@ -133,7 +139,7 @@ impl<'a> Parser<'a> { }; // Parse the first pattern (`p_0`). - let mut first_pat = self.parse_pat_no_top_alt(expected)?; + let mut first_pat = self.parse_pat_no_top_alt(expected, syntax_loc.clone())?; if rc == RecoverComma::Yes { self.maybe_recover_unexpected_comma(first_pat.span, rt)?; } @@ -172,7 +178,7 @@ impl<'a> Parser<'a> { break; } } - let pat = self.parse_pat_no_top_alt(expected).map_err(|mut err| { + let pat = self.parse_pat_no_top_alt(expected, syntax_loc).map_err(|mut err| { err.span_label(lo, WHILE_PARSING_OR_MSG); err })?; @@ -208,6 +214,7 @@ impl<'a> Parser<'a> { rc, RecoverColon::No, CommaRecoveryMode::LikelyTuple, + Some(syntax_loc), )?; let colon = self.eat(&token::Colon); @@ -319,6 +326,7 @@ impl<'a> Parser<'a> { &mut self, allow_range_pat: bool, expected: Option, + syntax_loc: Option, ) -> PResult<'a, P> { maybe_recover_from_interpolated_ty_qpath!(self, true); maybe_whole!(self, NtPat, |x| x); @@ -393,7 +401,7 @@ impl<'a> Parser<'a> { (Some(qself), path) } else { // Parse an unqualified path - (None, self.parse_path(PathStyle::Pat)?) + (None, self.parse_path(PathStyle::Pat, syntax_loc)?) }; let span = lo.to(self.prev_token.span); @@ -485,7 +493,7 @@ impl<'a> Parser<'a> { // At this point we attempt to parse `@ $pat_rhs` and emit an error. self.bump(); // `@` - let mut rhs = self.parse_pat_no_top_alt(None)?; + let mut rhs = self.parse_pat_no_top_alt(None, None)?; let whole_span = lhs.span.to(rhs.span); if let PatKind::Ident(_, _, sub @ None) = &mut rhs.kind { @@ -541,7 +549,7 @@ impl<'a> Parser<'a> { } let mutbl = self.parse_mutability(); - let subpat = self.parse_pat_with_range_pat(false, expected)?; + let subpat = self.parse_pat_with_range_pat(false, expected, None)?; Ok(PatKind::Ref(subpat, mutbl)) } @@ -584,7 +592,7 @@ impl<'a> Parser<'a> { } // Parse the pattern we hope to be an identifier. - let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier))?; + let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?; // If we don't have `mut $ident (@ pat)?`, error. if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind @@ -776,7 +784,7 @@ impl<'a> Parser<'a> { (Some(qself), path) } else { // Parse an unqualified path - (None, self.parse_path(PathStyle::Pat)?) + (None, self.parse_path(PathStyle::Pat, None)?) }; let hi = self.prev_token.span; Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path))) @@ -814,7 +822,7 @@ impl<'a> Parser<'a> { fn parse_pat_ident(&mut self, binding_annotation: BindingAnnotation) -> PResult<'a, PatKind> { let ident = self.parse_ident()?; let sub = if self.eat(&token::At) { - Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?) + Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) } else { None }; @@ -903,14 +911,14 @@ impl<'a> Parser<'a> { // We cannot use `parse_pat_ident()` since it will complain `box` // is not an identifier. let sub = if self.eat(&token::At) { - Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?) + Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) } else { None }; Ok(PatKind::Ident(BindingAnnotation::NONE, Ident::new(kw::Box, box_span), sub)) } else { - let pat = self.parse_pat_with_range_pat(false, None)?; + let pat = self.parse_pat_with_range_pat(false, None, None)?; self.sess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span)); Ok(PatKind::Box(pat)) } diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 4fe8a5aa62609..0d5f48e424e2c 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -1,3 +1,4 @@ +use super::pat::PatternLocation; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{Parser, Restrictions, TokenType}; use crate::errors::{GenericArgsInPatRequireTurbofishSyntax, PathSingleColon}; @@ -79,7 +80,7 @@ impl<'a> Parser<'a> { let (mut path, path_span); if self.eat_keyword(kw::As) { let path_lo = self.token.span; - path = self.parse_path(PathStyle::Type)?; + path = self.parse_path(PathStyle::Type, None)?; path_span = path_lo.to(self.prev_token.span); } else { path_span = self.token.span.to(self.token.span); @@ -98,7 +99,7 @@ impl<'a> Parser<'a> { } let qself = P(QSelf { ty, path_span, position: path.segments.len() }); - self.parse_path_segments(&mut path.segments, style, None)?; + self.parse_path_segments(&mut path.segments, style, None, None)?; Ok(( qself, @@ -139,8 +140,12 @@ impl<'a> Parser<'a> { true } - pub(super) fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> { - self.parse_path_inner(style, None) + pub(super) fn parse_path( + &mut self, + style: PathStyle, + syntax_loc: Option, + ) -> PResult<'a, Path> { + self.parse_path_inner(style, None, syntax_loc) } /// Parses simple paths. @@ -157,6 +162,7 @@ impl<'a> Parser<'a> { &mut self, style: PathStyle, ty_generics: Option<&Generics>, + syntax_loc: Option, ) -> PResult<'a, Path> { let reject_generics_if_mod_style = |parser: &Parser<'_>, path: &Path| { // Ensure generic arguments don't end up in attribute paths, such as: @@ -201,7 +207,7 @@ impl<'a> Parser<'a> { if self.eat(&token::ModSep) { segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); } - self.parse_path_segments(&mut segments, style, ty_generics)?; + self.parse_path_segments(&mut segments, style, ty_generics, syntax_loc)?; Ok(Path { segments, span: lo.to(self.prev_token.span), tokens: None }) } @@ -210,9 +216,10 @@ impl<'a> Parser<'a> { segments: &mut ThinVec, style: PathStyle, ty_generics: Option<&Generics>, + syntax_loc: Option, ) -> PResult<'a, ()> { loop { - let segment = self.parse_path_segment(style, ty_generics)?; + let segment = self.parse_path_segment(style, ty_generics, syntax_loc)?; if style.has_generic_ambiguity() { // In order to check for trailing angle brackets, we must have finished // recursing (`parse_path_segment` can indirectly call this function), @@ -267,6 +274,7 @@ impl<'a> Parser<'a> { &mut self, style: PathStyle, ty_generics: Option<&Generics>, + syntax_loc: Option, ) -> PResult<'a, PathSegment> { let ident = self.parse_path_segment_ident()?; let is_args_start = |token: &Token| { @@ -286,6 +294,17 @@ impl<'a> Parser<'a> { is_args_start(&this.token) }; + if let Some(PatternLocation::FunctionParameter) = syntax_loc { + } else if style == PathStyle::Pat + && self.check_noexpect(&token::Lt) + && self.look_ahead(1, |t| t.can_begin_type()) + { + return Err(self.sess.create_err(GenericArgsInPatRequireTurbofishSyntax { + span: self.token.span, + suggest_turbofish: self.token.span.shrink_to_lo(), + })); + } + Ok( if style == PathStyle::Type && check_args_start(self) || style != PathStyle::Mod @@ -382,14 +401,6 @@ impl<'a> Parser<'a> { }; PathSegment { ident, args: Some(args), id: ast::DUMMY_NODE_ID } - } else if style == PathStyle::Pat - && self.check_noexpect(&token::Lt) - && self.look_ahead(1, |t| t.can_begin_type()) - { - return Err(self.sess.create_err(GenericArgsInPatRequireTurbofishSyntax { - span: self.token.span, - suggest_turbofish: self.token.span.shrink_to_lo(), - })); } else { // Generic arguments are not found. PathSegment::from_ident(ident) diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 9fcf51a04ec05..2c08e984be578 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -149,7 +149,7 @@ impl<'a> Parser<'a> { fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> { let stmt = self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { - let path = this.parse_path(PathStyle::Expr)?; + let path = this.parse_path(PathStyle::Expr, None)?; if this.eat(&token::Not) { let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 3bb50b05aa346..8be84c7d46224 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -289,7 +289,7 @@ impl<'a> Parser<'a> { recover_return_sign, )? } else { - let path = self.parse_path(PathStyle::Type)?; + let path = self.parse_path(PathStyle::Type, None)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)? } @@ -649,7 +649,7 @@ impl<'a> Parser<'a> { ty_generics: Option<&Generics>, ) -> PResult<'a, TyKind> { // Simple path - let path = self.parse_path_inner(PathStyle::Type, ty_generics)?; + let path = self.parse_path_inner(PathStyle::Type, ty_generics, None)?; if self.eat(&token::Not) { // Macro invocation in type position Ok(TyKind::MacCall(P(MacCall { path, args: self.parse_delim_args()? }))) @@ -865,7 +865,7 @@ impl<'a> Parser<'a> { path } else { - self.parse_path(PathStyle::Type)? + self.parse_path(PathStyle::Type, None)? }; if self.may_recover() && self.token == TokenKind::OpenDelim(Delimiter::Parenthesis) { diff --git a/tests/ui/anon-params/anon-params-denied-2018.stderr b/tests/ui/anon-params/anon-params-denied-2018.stderr index bb60c898e8122..ede0e70cd7100 100644 --- a/tests/ui/anon-params/anon-params-denied-2018.stderr +++ b/tests/ui/anon-params/anon-params-denied-2018.stderr @@ -45,10 +45,14 @@ LL | fn foo_with_qualified_path(::Baz); | ^ expected one of 8 possible tokens | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: explicitly ignore the parameter name +help: if this is a `self` type, give it a parameter name + | +LL | fn foo_with_qualified_path(self: ::Baz); + | +++++ +help: if this is a type, explicitly ignore the parameter name | LL | fn foo_with_qualified_path(_: ::Baz); - | ~~~~~~~~~~~~~~~~~~ + | ++ error: expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` --> $DIR/anon-params-denied-2018.rs:15:56 @@ -69,10 +73,14 @@ LL | fn foo_with_multiple_qualified_paths(::Baz, ::Baz); | ^ expected one of 8 possible tokens | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: explicitly ignore the parameter name +help: if this is a `self` type, give it a parameter name + | +LL | fn foo_with_multiple_qualified_paths(self: ::Baz, ::Baz); + | +++++ +help: if this is a type, explicitly ignore the parameter name | LL | fn foo_with_multiple_qualified_paths(_: ::Baz, ::Baz); - | ~~~~~~~~~~~~~~~~~~ + | ++ error: expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` --> $DIR/anon-params-denied-2018.rs:18:74 @@ -81,10 +89,10 @@ LL | fn foo_with_multiple_qualified_paths(::Baz, ::Baz); | ^ expected one of 8 possible tokens | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: explicitly ignore the parameter name +help: if this is a type, explicitly ignore the parameter name | LL | fn foo_with_multiple_qualified_paths(::Baz, _: ::Baz); - | ~~~~~~~~~~~~~~~~~~ + | ++ error: expected one of `:`, `@`, or `|`, found `,` --> $DIR/anon-params-denied-2018.rs:22:36 diff --git a/tests/ui/span/issue-34264.rs b/tests/ui/span/issue-34264.rs index c7a8440f28401..9227ee482dfa5 100644 --- a/tests/ui/span/issue-34264.rs +++ b/tests/ui/span/issue-34264.rs @@ -1,5 +1,5 @@ fn foo(Option, String) {} //~ ERROR expected one of -//~^ ERROR generic args in patterns require the turbofish syntax +//~^ ERROR expected one of fn bar(x, y: usize) {} //~ ERROR expected one of fn main() { diff --git a/tests/ui/span/issue-34264.stderr b/tests/ui/span/issue-34264.stderr index 1874d7f853379..8c639d5513b53 100644 --- a/tests/ui/span/issue-34264.stderr +++ b/tests/ui/span/issue-34264.stderr @@ -1,13 +1,18 @@ -error: generic args in patterns require the turbofish syntax +error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `<` --> $DIR/issue-34264.rs:1:14 | LL | fn foo(Option, String) {} - | ^ + | ^ expected one of 9 possible tokens | -help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this is a `self` type, give it a parameter name + | +LL | fn foo(self: Option, String) {} + | +++++ +help: if this is a type, explicitly ignore the parameter name | -LL | fn foo(Option::, String) {} - | ++ +LL | fn foo(_: Option, String) {} + | ++ error: expected one of `:`, `@`, or `|`, found `)` --> $DIR/issue-34264.rs:1:27 diff --git a/tests/ui/suggestions/issue-64252-self-type.rs b/tests/ui/suggestions/issue-64252-self-type.rs index ad25d334507fc..ea76dc8742b9b 100644 --- a/tests/ui/suggestions/issue-64252-self-type.rs +++ b/tests/ui/suggestions/issue-64252-self-type.rs @@ -1,12 +1,11 @@ // This test checks that a suggestion to add a `self: ` parameter name is provided // to functions where this is applicable. -pub fn foo(Box) { } //~ ERROR generic args in patterns require the turbofish syntax - +pub fn foo(Box) { } //~ ERROR expected one of struct Bar; impl Bar { - fn bar(Box) { } //~ ERROR generic args in patterns require the turbofish syntax + fn bar(Box) { } //~ ERROR expected one of } fn main() { } diff --git a/tests/ui/suggestions/issue-64252-self-type.stderr b/tests/ui/suggestions/issue-64252-self-type.stderr index ba5c2da241500..dd83d6a1cb291 100644 --- a/tests/ui/suggestions/issue-64252-self-type.stderr +++ b/tests/ui/suggestions/issue-64252-self-type.stderr @@ -1,24 +1,34 @@ -error: generic args in patterns require the turbofish syntax +error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `<` --> $DIR/issue-64252-self-type.rs:4:15 | LL | pub fn foo(Box) { } - | ^ + | ^ expected one of 9 possible tokens | -help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this is a `self` type, give it a parameter name | -LL | pub fn foo(Box::) { } - | ++ +LL | pub fn foo(self: Box) { } + | +++++ +help: if this is a type, explicitly ignore the parameter name + | +LL | pub fn foo(_: Box) { } + | ++ -error: generic args in patterns require the turbofish syntax - --> $DIR/issue-64252-self-type.rs:9:15 +error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `<` + --> $DIR/issue-64252-self-type.rs:8:15 | LL | fn bar(Box) { } - | ^ + | ^ expected one of 9 possible tokens + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this is a `self` type, give it a parameter name | -help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments +LL | fn bar(self: Box) { } + | +++++ +help: if this is a type, explicitly ignore the parameter name | -LL | fn bar(Box::) { } - | ++ +LL | fn bar(_: Box) { } + | ++ error: aborting due to 2 previous errors From f5243d2bfc9c8982523d443e960f8b8c54f6cd73 Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Thu, 3 Aug 2023 00:13:41 +0800 Subject: [PATCH 13/21] Fix rustfmt dep --- src/tools/rustfmt/src/parse/macros/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 67f3985926e2f..7a802f7a88e06 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -56,7 +56,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { ); parse_macro_arg!( Pat, - |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None), + |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None, None), |x: ptr::P| Some(x) ); // `parse_item` returns `Option>`. From 6ae2677d72f1bc2b239ecfbfa1bb8d672fbfd211 Mon Sep 17 00:00:00 2001 From: Taras Tsugrii Date: Wed, 2 Aug 2023 20:51:16 -0700 Subject: [PATCH 14/21] [rustc_span][perf] Hoist lookup sorted by words out of the loop. @lqd commented on https://github.com/rust-lang/rust/pull/114351 asking if `sort_by_words(lookup)` is computed repeatedly. I was assuming that rustc should have no difficulties to hoist it automatically outside of the loop to avoid repeated pure computation, but according to https://godbolt.org/z/frs8Kj1rq it seems like I was wrong: original version seems to have 2 calls per loop iteration ``` .LBB16_3: mov rbx, qword ptr [r13] mov r14, qword ptr [r13 + 8] lea rdi, [rsp + 40] mov rsi, rbx mov rdx, r14 call example::sort_by_words lea rdi, [rsp + 64] mov rsi, qword ptr [rsp + 8] mov rdx, qword ptr [rsp + 16] call example::sort_by_words mov rdi, qword ptr [rsp + 40] mov rdx, qword ptr [rsp + 56] mov rsi, qword ptr [rsp + 64] cmp rdx, qword ptr [rsp + 80] mov qword ptr [rsp + 32], rdi mov qword ptr [rsp + 24], rsi jne .LBB16_5 call qword ptr [rip + bcmp@GOTPCREL] test eax, eax sete al mov dword ptr [rsp + 4], eax mov rsi, qword ptr [rsp + 72] test rsi, rsi jne .LBB16_8 jmp .LBB16_9 ``` but the manually hoisted version just 1: ``` .LBB16_3: mov r13, qword ptr [r15] mov r14, qword ptr [r15 + 8] lea rdi, [rsp + 64] mov rsi, r13 mov rdx, r14 call example::sort_by_words mov rdi, qword ptr [rsp + 64] mov rdx, qword ptr [rsp + 16] cmp qword ptr [rsp + 80], rdx mov qword ptr [rsp + 32], rdi jne .LBB16_5 mov rsi, qword ptr [rsp + 8] call qword ptr [rip + bcmp@GOTPCREL] test eax, eax sete bpl mov rsi, qword ptr [rsp + 72] test rsi, rsi jne .LBB16_8 jmp .LBB16_9 ``` This code is probably not very hot, but there is no reason to leave such a low hanging fruit. --- compiler/rustc_span/src/edit_distance.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_span/src/edit_distance.rs b/compiler/rustc_span/src/edit_distance.rs index 259f423865480..4811bb2c35489 100644 --- a/compiler/rustc_span/src/edit_distance.rs +++ b/compiler/rustc_span/src/edit_distance.rs @@ -238,8 +238,9 @@ fn find_best_match_for_name_impl( } fn find_match_by_sorted_words(iter_names: &[Symbol], lookup: &str) -> Option { + let lookup_sorted_by_words = sort_by_words(lookup); iter_names.iter().fold(None, |result, candidate| { - if sort_by_words(candidate.as_str()) == sort_by_words(lookup) { + if sort_by_words(candidate.as_str()) == lookup_sorted_by_words { Some(*candidate) } else { result From 41e85c3d2369ebc44c19c6009a061b64d43672ee Mon Sep 17 00:00:00 2001 From: r0cky Date: Thu, 3 Aug 2023 05:18:19 +0000 Subject: [PATCH 15/21] Apply suggestions --- compiler/rustc_parse/src/parser/pat.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 615ef28db085d..5803d0c1c0524 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -139,7 +139,7 @@ impl<'a> Parser<'a> { }; // Parse the first pattern (`p_0`). - let mut first_pat = self.parse_pat_no_top_alt(expected, syntax_loc.clone())?; + let mut first_pat = self.parse_pat_no_top_alt(expected, syntax_loc)?; if rc == RecoverComma::Yes { self.maybe_recover_unexpected_comma(first_pat.span, rt)?; } @@ -813,7 +813,9 @@ impl<'a> Parser<'a> { | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern. | token::ModSep // A tuple / struct variant pattern. | token::Not)) // A macro expanding to a pattern. - && !(self.look_ahead(1, |t| t.kind == token::Lt) && self.look_ahead(2, |t| t.can_begin_type())) // May suggest the turbofish syntax for generics, only valid for recoveries. + // May suggest the turbofish syntax for generics, only valid for recoveries. + && !(self.look_ahead(1, |t| t.kind == token::Lt) + && self.look_ahead(2, |t| t.can_begin_type())) } /// Parses `ident` or `ident @ pat`. From 2195fa6a9bad1e95511875c93c34b55fe7ae55fa Mon Sep 17 00:00:00 2001 From: bohan Date: Thu, 3 Aug 2023 16:44:02 +0800 Subject: [PATCH 16/21] fix the span in the suggestion of remove question mark --- .../src/infer/error_reporting/mod.rs | 2 +- .../remove-question-symbol-with-paren.rs | 9 ++++++++ .../remove-question-symbol-with-paren.stderr | 22 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/ui/suggestions/remove-question-symbol-with-paren.rs create mode 100644 tests/ui/suggestions/remove-question-symbol-with-paren.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 25077f9bb97ee..7739cf1ee05b0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -764,7 +764,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { Some(ty) if expected == ty => { let source_map = self.tcx.sess.source_map(); err.span_suggestion( - source_map.end_point(cause.span), + source_map.end_point(cause.span()), "try removing this `?`", "", Applicability::MachineApplicable, diff --git a/tests/ui/suggestions/remove-question-symbol-with-paren.rs b/tests/ui/suggestions/remove-question-symbol-with-paren.rs new file mode 100644 index 0000000000000..c522793dbcb7e --- /dev/null +++ b/tests/ui/suggestions/remove-question-symbol-with-paren.rs @@ -0,0 +1,9 @@ +// https://github.com/rust-lang/rust/issues/114392 + +fn foo() -> Option<()> { + let x = Some(()); + (x?) + //~^ ERROR `?` operator has incompatible types +} + +fn main() {} diff --git a/tests/ui/suggestions/remove-question-symbol-with-paren.stderr b/tests/ui/suggestions/remove-question-symbol-with-paren.stderr new file mode 100644 index 0000000000000..39e35f733a1d2 --- /dev/null +++ b/tests/ui/suggestions/remove-question-symbol-with-paren.stderr @@ -0,0 +1,22 @@ +error[E0308]: `?` operator has incompatible types + --> $DIR/remove-question-symbol-with-paren.rs:5:6 + | +LL | (x?) + | ^^ expected `Option<()>`, found `()` + | + = note: `?` operator cannot convert from `()` to `Option<()>` + = note: expected enum `Option<()>` + found unit type `()` +help: try removing this `?` + | +LL - (x?) +LL + (x) + | +help: try wrapping the expression in `Some` + | +LL | (Some(x?)) + | +++++ + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 4b3dadbe5a9f3dd06932a9099abd37bae751cdd3 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 13 Jul 2023 14:13:08 +0200 Subject: [PATCH 17/21] Also lint on cast/cast_mut and ptr::from_mut/ptr::from_ref --- compiler/rustc_lint/src/ptr_nulls.rs | 52 +++++++++++----- tests/ui/consts/ptr_is_null.rs | 1 + tests/ui/lint/ptr_null_checks.rs | 16 +++++ tests/ui/lint/ptr_null_checks.stderr | 88 +++++++++++++++++++++------- 4 files changed, 121 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs index b2138c42224b4..64b86fec46b42 100644 --- a/compiler/rustc_lint/src/ptr_nulls.rs +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -31,25 +31,45 @@ declare_lint! { declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]); -fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option> { - let mut expr = expr.peel_blocks(); +/// This function detects and returns the original expression from a series of consecutive casts, +/// ie. `(my_fn as *const _ as *mut _).cast_mut()` would return the expression for `my_fn`. +fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'a Expr<'a>> { let mut had_at_least_one_cast = false; - while let ExprKind::Cast(cast_expr, cast_ty) = expr.kind - && let TyKind::Ptr(_) = cast_ty.kind { - expr = cast_expr.peel_blocks(); - had_at_least_one_cast = true; + loop { + e = e.peel_blocks(); + e = if let ExprKind::Cast(expr, t) = e.kind + && let TyKind::Ptr(_) = t.kind { + had_at_least_one_cast = true; + expr + } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_cast | sym::ptr_cast_mut)) { + had_at_least_one_cast = true; + expr + } else if let ExprKind::Call(path, [arg]) = e.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_from_ref | sym::ptr_from_mut)) { + had_at_least_one_cast = true; + arg + } else if had_at_least_one_cast { + return Some(e); + } else { + return None; + }; } - if !had_at_least_one_cast { - None +} + +fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option> { + let expr = ptr_cast_chain(cx, expr)?; + + let orig_ty = cx.typeck_results().expr_ty(expr); + if orig_ty.is_fn() { + Some(PtrNullChecksDiag::FnPtr) + } else if orig_ty.is_ref() { + Some(PtrNullChecksDiag::Ref { orig_ty, label: expr.span }) } else { - let orig_ty = cx.typeck_results().expr_ty(expr); - if orig_ty.is_fn() { - Some(PtrNullChecksDiag::FnPtr) - } else if orig_ty.is_ref() { - Some(PtrNullChecksDiag::Ref { orig_ty, label: expr.span }) - } else { - None - } + None } } diff --git a/tests/ui/consts/ptr_is_null.rs b/tests/ui/consts/ptr_is_null.rs index 8babb68585dea..43b9767db1645 100644 --- a/tests/ui/consts/ptr_is_null.rs +++ b/tests/ui/consts/ptr_is_null.rs @@ -2,6 +2,7 @@ // check-pass #![feature(const_ptr_is_null)] +#![allow(useless_ptr_null_checks)] const FOO: &usize = &42; diff --git a/tests/ui/lint/ptr_null_checks.rs b/tests/ui/lint/ptr_null_checks.rs index 600ca32ca8937..e677ea3094d5e 100644 --- a/tests/ui/lint/ptr_null_checks.rs +++ b/tests/ui/lint/ptr_null_checks.rs @@ -1,5 +1,9 @@ // check-pass +#![feature(ptr_from_ref)] + +use std::ptr; + extern "C" fn c_fn() {} fn static_i32() -> &'static i32 { &1 } @@ -21,6 +25,10 @@ fn main() { //~^ WARN function pointers are not nullable if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} //~^ WARN function pointers are not nullable + if (fn_ptr as *mut fn() as *const fn()).cast_mut().is_null() {} + //~^ WARN function pointers are not nullable + if ((fn_ptr as *mut fn()).cast() as *const fn()).cast_mut().is_null() {} + //~^ WARN function pointers are not nullable if (fn_ptr as fn() as *const ()).is_null() {} //~^ WARN function pointers are not nullable if (c_fn as *const fn()).is_null() {} @@ -29,8 +37,16 @@ fn main() { // ---------------- References ------------------ if (&mut 8 as *mut i32).is_null() {} //~^ WARN references are not nullable + if ptr::from_mut(&mut 8).is_null() {} + //~^ WARN references are not nullable if (&8 as *const i32).is_null() {} //~^ WARN references are not nullable + if ptr::from_ref(&8).is_null() {} + //~^ WARN references are not nullable + if ptr::from_ref(&8).cast_mut().is_null() {} + //~^ WARN references are not nullable + if (ptr::from_ref(&8).cast_mut() as *mut i32).is_null() {} + //~^ WARN references are not nullable if (&8 as *const i32) == std::ptr::null() {} //~^ WARN references are not nullable let ref_num = &8; diff --git a/tests/ui/lint/ptr_null_checks.stderr b/tests/ui/lint/ptr_null_checks.stderr index 10efa8685befd..525d96c491972 100644 --- a/tests/ui/lint/ptr_null_checks.stderr +++ b/tests/ui/lint/ptr_null_checks.stderr @@ -1,5 +1,5 @@ warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:10:8 + --> $DIR/ptr_null_checks.rs:14:8 | LL | if (fn_ptr as *mut ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | if (fn_ptr as *mut ()).is_null() {} = note: `#[warn(useless_ptr_null_checks)]` on by default warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:12:8 + --> $DIR/ptr_null_checks.rs:16:8 | LL | if (fn_ptr as *const u8).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | if (fn_ptr as *const u8).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:14:8 + --> $DIR/ptr_null_checks.rs:18:8 | LL | if (fn_ptr as *const ()) == std::ptr::null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | if (fn_ptr as *const ()) == std::ptr::null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:16:8 + --> $DIR/ptr_null_checks.rs:20:8 | LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:18:8 + --> $DIR/ptr_null_checks.rs:22:8 | LL | if (fn_ptr as *const ()) == (0 as *const ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | if (fn_ptr as *const ()) == (0 as *const ()) {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:20:8 + --> $DIR/ptr_null_checks.rs:24:8 | LL | if <*const _>::is_null(fn_ptr as *const ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | if <*const _>::is_null(fn_ptr as *const ()) {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:22:8 + --> $DIR/ptr_null_checks.rs:26:8 | LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -56,7 +56,23 @@ LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:24:8 + --> $DIR/ptr_null_checks.rs:28:8 + | +LL | if (fn_ptr as *mut fn() as *const fn()).cast_mut().is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value + +warning: function pointers are not nullable, so checking them for null will always return false + --> $DIR/ptr_null_checks.rs:30:8 + | +LL | if ((fn_ptr as *mut fn()).cast() as *const fn()).cast_mut().is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value + +warning: function pointers are not nullable, so checking them for null will always return false + --> $DIR/ptr_null_checks.rs:32:8 | LL | if (fn_ptr as fn() as *const ()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +80,7 @@ LL | if (fn_ptr as fn() as *const ()).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:26:8 + --> $DIR/ptr_null_checks.rs:34:8 | LL | if (c_fn as *const fn()).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +88,7 @@ LL | if (c_fn as *const fn()).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:30:8 + --> $DIR/ptr_null_checks.rs:38:8 | LL | if (&mut 8 as *mut i32).is_null() {} | ^------^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +96,15 @@ LL | if (&mut 8 as *mut i32).is_null() {} | expression has type `&mut i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:32:8 + --> $DIR/ptr_null_checks.rs:40:8 + | +LL | if ptr::from_mut(&mut 8).is_null() {} + | ^^^^^^^^^^^^^^------^^^^^^^^^^^ + | | + | expression has type `&mut i32` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/ptr_null_checks.rs:42:8 | LL | if (&8 as *const i32).is_null() {} | ^--^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +112,31 @@ LL | if (&8 as *const i32).is_null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:34:8 + --> $DIR/ptr_null_checks.rs:44:8 + | +LL | if ptr::from_ref(&8).is_null() {} + | ^^^^^^^^^^^^^^--^^^^^^^^^^^ + | | + | expression has type `&i32` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/ptr_null_checks.rs:46:8 + | +LL | if ptr::from_ref(&8).cast_mut().is_null() {} + | ^^^^^^^^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&i32` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/ptr_null_checks.rs:48:8 + | +LL | if (ptr::from_ref(&8).cast_mut() as *mut i32).is_null() {} + | ^^^^^^^^^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&i32` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/ptr_null_checks.rs:50:8 | LL | if (&8 as *const i32) == std::ptr::null() {} | ^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +144,7 @@ LL | if (&8 as *const i32) == std::ptr::null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:37:8 + --> $DIR/ptr_null_checks.rs:53:8 | LL | if (ref_num as *const i32) == std::ptr::null() {} | ^-------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +152,7 @@ LL | if (ref_num as *const i32) == std::ptr::null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:39:8 + --> $DIR/ptr_null_checks.rs:55:8 | LL | if (b"\0" as *const u8).is_null() {} | ^-----^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +160,7 @@ LL | if (b"\0" as *const u8).is_null() {} | expression has type `&[u8; 1]` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:41:8 + --> $DIR/ptr_null_checks.rs:57:8 | LL | if ("aa" as *const str).is_null() {} | ^----^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +168,7 @@ LL | if ("aa" as *const str).is_null() {} | expression has type `&str` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:43:8 + --> $DIR/ptr_null_checks.rs:59:8 | LL | if (&[1, 2] as *const i32).is_null() {} | ^-------^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,7 +176,7 @@ LL | if (&[1, 2] as *const i32).is_null() {} | expression has type `&[i32; 2]` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:45:8 + --> $DIR/ptr_null_checks.rs:61:8 | LL | if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {} | ^-----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +184,7 @@ LL | if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {} | expression has type `&mut [i32; 2]` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:47:8 + --> $DIR/ptr_null_checks.rs:63:8 | LL | if (static_i32() as *const i32).is_null() {} | ^------------^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,12 +192,12 @@ LL | if (static_i32() as *const i32).is_null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:49:8 + --> $DIR/ptr_null_checks.rs:65:8 | LL | if (&*{ static_i32() } as *const i32).is_null() {} | ^------------------^^^^^^^^^^^^^^^^^^^^^^^^^ | | | expression has type `&i32` -warning: 19 warnings emitted +warning: 25 warnings emitted From 8c8af6cf99d6a54ece11d21c15e909aef2b60552 Mon Sep 17 00:00:00 2001 From: r0cky Date: Thu, 3 Aug 2023 08:56:31 +0000 Subject: [PATCH 18/21] Avoid too many expected symbols and reduce `None`s --- compiler/rustc_parse/src/parser/attr.rs | 4 +- .../rustc_parse/src/parser/diagnostics.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 6 +-- compiler/rustc_parse/src/parser/item.rs | 6 +-- compiler/rustc_parse/src/parser/mod.rs | 6 +-- .../rustc_parse/src/parser/nonterminal.rs | 2 +- compiler/rustc_parse/src/parser/pat.rs | 43 ++++++++++++------- compiler/rustc_parse/src/parser/path.rs | 33 +++----------- compiler/rustc_parse/src/parser/stmt.rs | 2 +- compiler/rustc_parse/src/parser/ty.rs | 6 +-- tests/ui/span/issue-34264.stderr | 4 +- .../suggestions/issue-64252-self-type.stderr | 8 ++-- 12 files changed, 58 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 1271bce799d5c..ee0abba1c1753 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -260,7 +260,7 @@ impl<'a> Parser<'a> { item } else { let do_parse = |this: &mut Self| { - let path = this.parse_path(PathStyle::Mod, None)?; + let path = this.parse_path(PathStyle::Mod)?; let args = this.parse_attr_args()?; Ok(ast::AttrItem { path, args, tokens: None }) }; @@ -387,7 +387,7 @@ impl<'a> Parser<'a> { } let lo = self.token.span; - let path = self.parse_path(PathStyle::Mod, None)?; + let path = self.parse_path(PathStyle::Mod)?; let kind = self.parse_meta_item_kind()?; let span = lo.to(self.prev_token.span); Ok(ast::MetaItem { path, kind, span }) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 8bc4433a9653a..5fa4f7902d624 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1579,7 +1579,7 @@ impl<'a> Parser<'a> { self.expect(&token::ModSep)?; let mut path = ast::Path { segments: ThinVec::new(), span: DUMMY_SP, tokens: None }; - self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None, None)?; + self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?; path.span = ty_span.to(self.prev_token.span); let ty_str = self.span_to_snippet(ty_span).unwrap_or_else(|_| pprust::ty_to_string(&ty)); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 6dafb8b999b2f..0e19a67a8413a 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -775,7 +775,7 @@ impl<'a> Parser<'a> { _ => {} } - match self.parse_path(PathStyle::Expr, None) { + match self.parse_path(PathStyle::Expr) { Ok(path) => { let span_after_type = parser_snapshot_after_type.token.span; let expr = mk_expr( @@ -1314,7 +1314,7 @@ impl<'a> Parser<'a> { } let fn_span_lo = self.token.span; - let mut seg = self.parse_path_segment(PathStyle::Expr, None, None)?; + let mut seg = self.parse_path_segment(PathStyle::Expr, None)?; self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]); self.check_turbofish_missing_angle_brackets(&mut seg); @@ -1544,7 +1544,7 @@ impl<'a> Parser<'a> { })?; (Some(qself), path) } else { - (None, self.parse_path(PathStyle::Expr, None)?) + (None, self.parse_path(PathStyle::Expr)?) }; // `!`, as an operator, is prefix, so we know this isn't that. diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index d3f756139def3..1301ed3e3882e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -452,7 +452,7 @@ impl<'a> Parser<'a> { /// Parses an item macro, e.g., `item!();`. fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, MacCall> { - let path = self.parse_path(PathStyle::Mod, None)?; // `foo::bar` + let path = self.parse_path(PathStyle::Mod)?; // `foo::bar` self.expect(&token::Not)?; // `!` match self.parse_delim_args() { // `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`. @@ -976,7 +976,7 @@ impl<'a> Parser<'a> { self.parse_use_tree_glob_or_nested()? } else { // `use path::*;` or `use path::{...};` or `use path;` or `use path as bar;` - prefix = self.parse_path(PathStyle::Mod, None)?; + prefix = self.parse_path(PathStyle::Mod)?; if self.eat(&token::ModSep) { self.parse_use_tree_glob_or_nested()? @@ -987,7 +987,7 @@ impl<'a> Parser<'a> { .emit_err(errors::SingleColonImportPath { span: self.prev_token.span }); // We parse the rest of the path and append it to the original prefix. - self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None, None)?; + self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None)?; prefix.span = lo.to(self.prev_token.span); } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 2af0caa1bdaf7..37b4c371c94b4 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1413,7 +1413,7 @@ impl<'a> Parser<'a> { // Parse `pub(in path)`. self.bump(); // `(` self.bump(); // `in` - let path = self.parse_path(PathStyle::Mod, None)?; // `path` + let path = self.parse_path(PathStyle::Mod)?; // `path` self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` let vis = VisibilityKind::Restricted { path: P(path), @@ -1430,7 +1430,7 @@ impl<'a> Parser<'a> { { // Parse `pub(crate)`, `pub(self)`, or `pub(super)`. self.bump(); // `(` - let path = self.parse_path(PathStyle::Mod, None)?; // `crate`/`super`/`self` + let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self` self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` let vis = VisibilityKind::Restricted { path: P(path), @@ -1456,7 +1456,7 @@ impl<'a> Parser<'a> { /// Recovery for e.g. `pub(something) fn ...` or `struct X { pub(something) y: Z }` fn recover_incorrect_vis_restriction(&mut self) -> PResult<'a, ()> { self.bump(); // `(` - let path = self.parse_path(PathStyle::Mod, None)?; + let path = self.parse_path(PathStyle::Mod)?; self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` let path_str = pprust::path_to_string(&path); diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index a09c1e4d7fde2..f5681532b3af7 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -168,7 +168,7 @@ impl<'a> Parser<'a> { }.into_diagnostic(&self.sess.span_diagnostic)); } NonterminalKind::Path => token::NtPath( - P(self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type, None))?), + P(self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?), ), NonterminalKind::Meta => token::NtMeta(P(self.parse_attr_item(true)?)), NonterminalKind::Vis => token::NtVis( diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 5803d0c1c0524..8d68a3a50acae 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -2,10 +2,11 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken}; use crate::errors::{ self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, - ExpectedCommaAfterPatternField, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, - InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect, - RemoveLet, RepeatedMutInPattern, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, - TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam, + ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax, + InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, + PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern, + TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, + UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, }; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; @@ -366,11 +367,11 @@ impl<'a> Parser<'a> { // Parse _ PatKind::Wild } else if self.eat_keyword(kw::Mut) { - self.parse_pat_ident_mut()? + self.parse_pat_ident_mut(syntax_loc)? } else if self.eat_keyword(kw::Ref) { // Parse ref ident @ pat / ref mut ident @ pat let mutbl = self.parse_mutability(); - self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl))? + self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl), syntax_loc)? } else if self.eat_keyword(kw::Box) { self.parse_pat_box()? } else if self.check_inline_const(0) { @@ -392,7 +393,7 @@ impl<'a> Parser<'a> { // Parse `ident @ pat` // This can give false positives and parse nullary enums, // they are dealt with later in resolve. - self.parse_pat_ident(BindingAnnotation::NONE)? + self.parse_pat_ident(BindingAnnotation::NONE, syntax_loc)? } else if self.is_start_of_pat_with_path() { // Parse pattern starting with a path let (qself, path) = if self.eat_lt() { @@ -401,7 +402,7 @@ impl<'a> Parser<'a> { (Some(qself), path) } else { // Parse an unqualified path - (None, self.parse_path(PathStyle::Pat, syntax_loc)?) + (None, self.parse_path(PathStyle::Pat)?) }; let span = lo.to(self.prev_token.span); @@ -574,12 +575,12 @@ impl<'a> Parser<'a> { } /// Parse a mutable binding with the `mut` token already eaten. - fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> { + fn parse_pat_ident_mut(&mut self, syntax_loc: Option) -> PResult<'a, PatKind> { let mut_span = self.prev_token.span; if self.eat_keyword(kw::Ref) { self.sess.emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) }); - return self.parse_pat_ident(BindingAnnotation::REF_MUT); + return self.parse_pat_ident(BindingAnnotation::REF_MUT, syntax_loc); } self.recover_additional_muts(); @@ -784,7 +785,7 @@ impl<'a> Parser<'a> { (Some(qself), path) } else { // Parse an unqualified path - (None, self.parse_path(PathStyle::Pat, None)?) + (None, self.parse_path(PathStyle::Pat)?) }; let hi = self.prev_token.span; Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path))) @@ -813,16 +814,28 @@ impl<'a> Parser<'a> { | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern. | token::ModSep // A tuple / struct variant pattern. | token::Not)) // A macro expanding to a pattern. - // May suggest the turbofish syntax for generics, only valid for recoveries. - && !(self.look_ahead(1, |t| t.kind == token::Lt) - && self.look_ahead(2, |t| t.can_begin_type())) } /// Parses `ident` or `ident @ pat`. /// Used by the copy foo and ref foo patterns to give a good /// error message when parsing mistakes like `ref foo(a, b)`. - fn parse_pat_ident(&mut self, binding_annotation: BindingAnnotation) -> PResult<'a, PatKind> { + fn parse_pat_ident( + &mut self, + binding_annotation: BindingAnnotation, + syntax_loc: Option, + ) -> PResult<'a, PatKind> { let ident = self.parse_ident()?; + + if !matches!(syntax_loc, Some(PatternLocation::FunctionParameter)) + && self.check_noexpect(&token::Lt) + && self.look_ahead(1, |t| t.can_begin_type()) + { + return Err(self.sess.create_err(GenericArgsInPatRequireTurbofishSyntax { + span: self.token.span, + suggest_turbofish: self.token.span.shrink_to_lo(), + })); + } + let sub = if self.eat(&token::At) { Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) } else { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 0d5f48e424e2c..feb7e829caf68 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -1,7 +1,6 @@ -use super::pat::PatternLocation; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{Parser, Restrictions, TokenType}; -use crate::errors::{GenericArgsInPatRequireTurbofishSyntax, PathSingleColon}; +use crate::errors::PathSingleColon; use crate::{errors, maybe_whole}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; @@ -80,7 +79,7 @@ impl<'a> Parser<'a> { let (mut path, path_span); if self.eat_keyword(kw::As) { let path_lo = self.token.span; - path = self.parse_path(PathStyle::Type, None)?; + path = self.parse_path(PathStyle::Type)?; path_span = path_lo.to(self.prev_token.span); } else { path_span = self.token.span.to(self.token.span); @@ -99,7 +98,7 @@ impl<'a> Parser<'a> { } let qself = P(QSelf { ty, path_span, position: path.segments.len() }); - self.parse_path_segments(&mut path.segments, style, None, None)?; + self.parse_path_segments(&mut path.segments, style, None)?; Ok(( qself, @@ -140,12 +139,8 @@ impl<'a> Parser<'a> { true } - pub(super) fn parse_path( - &mut self, - style: PathStyle, - syntax_loc: Option, - ) -> PResult<'a, Path> { - self.parse_path_inner(style, None, syntax_loc) + pub(super) fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> { + self.parse_path_inner(style, None) } /// Parses simple paths. @@ -162,7 +157,6 @@ impl<'a> Parser<'a> { &mut self, style: PathStyle, ty_generics: Option<&Generics>, - syntax_loc: Option, ) -> PResult<'a, Path> { let reject_generics_if_mod_style = |parser: &Parser<'_>, path: &Path| { // Ensure generic arguments don't end up in attribute paths, such as: @@ -207,7 +201,7 @@ impl<'a> Parser<'a> { if self.eat(&token::ModSep) { segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); } - self.parse_path_segments(&mut segments, style, ty_generics, syntax_loc)?; + self.parse_path_segments(&mut segments, style, ty_generics)?; Ok(Path { segments, span: lo.to(self.prev_token.span), tokens: None }) } @@ -216,10 +210,9 @@ impl<'a> Parser<'a> { segments: &mut ThinVec, style: PathStyle, ty_generics: Option<&Generics>, - syntax_loc: Option, ) -> PResult<'a, ()> { loop { - let segment = self.parse_path_segment(style, ty_generics, syntax_loc)?; + let segment = self.parse_path_segment(style, ty_generics)?; if style.has_generic_ambiguity() { // In order to check for trailing angle brackets, we must have finished // recursing (`parse_path_segment` can indirectly call this function), @@ -274,7 +267,6 @@ impl<'a> Parser<'a> { &mut self, style: PathStyle, ty_generics: Option<&Generics>, - syntax_loc: Option, ) -> PResult<'a, PathSegment> { let ident = self.parse_path_segment_ident()?; let is_args_start = |token: &Token| { @@ -294,17 +286,6 @@ impl<'a> Parser<'a> { is_args_start(&this.token) }; - if let Some(PatternLocation::FunctionParameter) = syntax_loc { - } else if style == PathStyle::Pat - && self.check_noexpect(&token::Lt) - && self.look_ahead(1, |t| t.can_begin_type()) - { - return Err(self.sess.create_err(GenericArgsInPatRequireTurbofishSyntax { - span: self.token.span, - suggest_turbofish: self.token.span.shrink_to_lo(), - })); - } - Ok( if style == PathStyle::Type && check_args_start(self) || style != PathStyle::Mod diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 2c08e984be578..9fcf51a04ec05 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -149,7 +149,7 @@ impl<'a> Parser<'a> { fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> { let stmt = self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { - let path = this.parse_path(PathStyle::Expr, None)?; + let path = this.parse_path(PathStyle::Expr)?; if this.eat(&token::Not) { let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 8be84c7d46224..3bb50b05aa346 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -289,7 +289,7 @@ impl<'a> Parser<'a> { recover_return_sign, )? } else { - let path = self.parse_path(PathStyle::Type, None)?; + let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)? } @@ -649,7 +649,7 @@ impl<'a> Parser<'a> { ty_generics: Option<&Generics>, ) -> PResult<'a, TyKind> { // Simple path - let path = self.parse_path_inner(PathStyle::Type, ty_generics, None)?; + let path = self.parse_path_inner(PathStyle::Type, ty_generics)?; if self.eat(&token::Not) { // Macro invocation in type position Ok(TyKind::MacCall(P(MacCall { path, args: self.parse_delim_args()? }))) @@ -865,7 +865,7 @@ impl<'a> Parser<'a> { path } else { - self.parse_path(PathStyle::Type, None)? + self.parse_path(PathStyle::Type)? }; if self.may_recover() && self.token == TokenKind::OpenDelim(Delimiter::Parenthesis) { diff --git a/tests/ui/span/issue-34264.stderr b/tests/ui/span/issue-34264.stderr index 8c639d5513b53..f0dea66f6128d 100644 --- a/tests/ui/span/issue-34264.stderr +++ b/tests/ui/span/issue-34264.stderr @@ -1,8 +1,8 @@ -error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `<` +error: expected one of `:`, `@`, or `|`, found `<` --> $DIR/issue-34264.rs:1:14 | LL | fn foo(Option, String) {} - | ^ expected one of 9 possible tokens + | ^ expected one of `:`, `@`, or `|` | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) help: if this is a `self` type, give it a parameter name diff --git a/tests/ui/suggestions/issue-64252-self-type.stderr b/tests/ui/suggestions/issue-64252-self-type.stderr index dd83d6a1cb291..dbef39faeadf6 100644 --- a/tests/ui/suggestions/issue-64252-self-type.stderr +++ b/tests/ui/suggestions/issue-64252-self-type.stderr @@ -1,8 +1,8 @@ -error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `<` +error: expected one of `:`, `@`, or `|`, found `<` --> $DIR/issue-64252-self-type.rs:4:15 | LL | pub fn foo(Box) { } - | ^ expected one of 9 possible tokens + | ^ expected one of `:`, `@`, or `|` | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) help: if this is a `self` type, give it a parameter name @@ -14,11 +14,11 @@ help: if this is a type, explicitly ignore the parameter name LL | pub fn foo(_: Box) { } | ++ -error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `<` +error: expected one of `:`, `@`, or `|`, found `<` --> $DIR/issue-64252-self-type.rs:8:15 | LL | fn bar(Box) { } - | ^ expected one of 9 possible tokens + | ^ expected one of `:`, `@`, or `|` | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) help: if this is a `self` type, give it a parameter name From ee519532f69610e208fc61a68537aa662c3e8d16 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 3 Aug 2023 10:57:11 +0200 Subject: [PATCH 19/21] Also add label with original type for function pointers --- compiler/rustc_lint/messages.ftl | 1 + compiler/rustc_lint/src/lints.rs | 6 +++- compiler/rustc_lint/src/ptr_nulls.rs | 2 +- tests/ui/lint/ptr_null_checks.stderr | 44 +++++++++++++++++++++------- 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 4897ffd0cece5..027a10de9bb9e 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -449,6 +449,7 @@ lint_path_statement_no_effect = path statement with no effect lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value + .label = expression has type `{$orig_ty}` lint_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false .label = expression has type `{$orig_ty}` diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 9e1d5605260c3..c0a8d078dc4a6 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -618,7 +618,11 @@ pub struct ExpectationNote { pub enum PtrNullChecksDiag<'a> { #[diag(lint_ptr_null_checks_fn_ptr)] #[help(lint_help)] - FnPtr, + FnPtr { + orig_ty: Ty<'a>, + #[label] + label: Span, + }, #[diag(lint_ptr_null_checks_ref)] Ref { orig_ty: Ty<'a>, diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs index 64b86fec46b42..02aff91032fd9 100644 --- a/compiler/rustc_lint/src/ptr_nulls.rs +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -65,7 +65,7 @@ fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option $DIR/ptr_null_checks.rs:14:8 | LL | if (fn_ptr as *mut ()).is_null() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^------^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `fn() {main}` | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value = note: `#[warn(useless_ptr_null_checks)]` on by default @@ -11,7 +13,9 @@ warning: function pointers are not nullable, so checking them for null will alwa --> $DIR/ptr_null_checks.rs:16:8 | LL | if (fn_ptr as *const u8).is_null() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^------^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `fn() {main}` | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value @@ -19,7 +23,9 @@ warning: function pointers are not nullable, so checking them for null will alwa --> $DIR/ptr_null_checks.rs:18:8 | LL | if (fn_ptr as *const ()) == std::ptr::null() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `fn() {main}` | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value @@ -27,7 +33,9 @@ warning: function pointers are not nullable, so checking them for null will alwa --> $DIR/ptr_null_checks.rs:20:8 | LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `fn() {main}` | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value @@ -35,7 +43,9 @@ warning: function pointers are not nullable, so checking them for null will alwa --> $DIR/ptr_null_checks.rs:22:8 | LL | if (fn_ptr as *const ()) == (0 as *const ()) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `fn() {main}` | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value @@ -43,7 +53,9 @@ warning: function pointers are not nullable, so checking them for null will alwa --> $DIR/ptr_null_checks.rs:24:8 | LL | if <*const _>::is_null(fn_ptr as *const ()) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^------^^^^^^^^^^^^^^ + | | + | expression has type `fn() {main}` | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value @@ -51,7 +63,9 @@ warning: function pointers are not nullable, so checking them for null will alwa --> $DIR/ptr_null_checks.rs:26:8 | LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `fn() {main}` | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value @@ -59,7 +73,9 @@ warning: function pointers are not nullable, so checking them for null will alwa --> $DIR/ptr_null_checks.rs:28:8 | LL | if (fn_ptr as *mut fn() as *const fn()).cast_mut().is_null() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `fn() {main}` | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value @@ -67,7 +83,9 @@ warning: function pointers are not nullable, so checking them for null will alwa --> $DIR/ptr_null_checks.rs:30:8 | LL | if ((fn_ptr as *mut fn()).cast() as *const fn()).cast_mut().is_null() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `fn() {main}` | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value @@ -75,7 +93,9 @@ warning: function pointers are not nullable, so checking them for null will alwa --> $DIR/ptr_null_checks.rs:32:8 | LL | if (fn_ptr as fn() as *const ()).is_null() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^--------------^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `fn()` | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value @@ -83,7 +103,9 @@ warning: function pointers are not nullable, so checking them for null will alwa --> $DIR/ptr_null_checks.rs:34:8 | LL | if (c_fn as *const fn()).is_null() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^----^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `extern "C" fn() {c_fn}` | = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value From dce7e87b1646fd5f4f7908fcd0aa1060c5189d44 Mon Sep 17 00:00:00 2001 From: r0cky Date: Thu, 3 Aug 2023 10:34:57 +0000 Subject: [PATCH 20/21] Reduce arbitrary self type suggestions --- .../rustc_parse/src/parser/diagnostics.rs | 18 +++-------------- .../anon-params-denied-2018.stderr | 20 ++++++------------- tests/ui/suggestions/issue-64252-self-type.rs | 7 +++++-- .../suggestions/issue-64252-self-type.stderr | 2 +- 4 files changed, 15 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 5fa4f7902d624..00ffa7de2ff62 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2019,7 +2019,7 @@ impl<'a> Parser<'a> { { let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)"; - let (ident, self_sugg, param_sugg, type_sugg, self_span, param_span, type_span, maybe_name) = + let (ident, self_sugg, param_sugg, type_sugg, self_span, param_span, type_span) = match pat.kind { PatKind::Ident(_, ident, _) => ( ident, @@ -2029,7 +2029,6 @@ impl<'a> Parser<'a> { pat.span.shrink_to_lo(), pat.span.shrink_to_hi(), pat.span.shrink_to_lo(), - true, ), // Also catches `fn foo(&a)`. PatKind::Ref(ref inner_pat, mutab) @@ -2046,22 +2045,11 @@ impl<'a> Parser<'a> { pat.span.shrink_to_lo(), pat.span, pat.span.shrink_to_lo(), - true, ) } _ => unreachable!(), } - }, - PatKind::Path(_, ref path) if let Some(segment) = path.segments.last() => ( - segment.ident, - "self: ", - ": TypeName".to_string(), - "_: ", - pat.span.shrink_to_lo(), - pat.span.shrink_to_hi(), - pat.span.shrink_to_lo(), - path.segments.len() == 1, // Avoid suggesting that `fn foo(a::b)` is fixed with a change to `fn foo(a::b: TypeName)`. - ), + } _ => { // Otherwise, try to get a type and emit a suggestion. if let Some(ty) = pat.to_ty() { @@ -2089,7 +2077,7 @@ impl<'a> Parser<'a> { } // Avoid suggesting that `fn foo(HashMap)` is fixed with a change to // `fn foo(HashMap: TypeName)`. - if self.token != token::Lt && maybe_name { + if self.token != token::Lt { err.span_suggestion( param_span, "if this is a parameter name, give it a type", diff --git a/tests/ui/anon-params/anon-params-denied-2018.stderr b/tests/ui/anon-params/anon-params-denied-2018.stderr index ede0e70cd7100..bb60c898e8122 100644 --- a/tests/ui/anon-params/anon-params-denied-2018.stderr +++ b/tests/ui/anon-params/anon-params-denied-2018.stderr @@ -45,14 +45,10 @@ LL | fn foo_with_qualified_path(::Baz); | ^ expected one of 8 possible tokens | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | fn foo_with_qualified_path(self: ::Baz); - | +++++ -help: if this is a type, explicitly ignore the parameter name +help: explicitly ignore the parameter name | LL | fn foo_with_qualified_path(_: ::Baz); - | ++ + | ~~~~~~~~~~~~~~~~~~ error: expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` --> $DIR/anon-params-denied-2018.rs:15:56 @@ -73,14 +69,10 @@ LL | fn foo_with_multiple_qualified_paths(::Baz, ::Baz); | ^ expected one of 8 possible tokens | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a `self` type, give it a parameter name - | -LL | fn foo_with_multiple_qualified_paths(self: ::Baz, ::Baz); - | +++++ -help: if this is a type, explicitly ignore the parameter name +help: explicitly ignore the parameter name | LL | fn foo_with_multiple_qualified_paths(_: ::Baz, ::Baz); - | ++ + | ~~~~~~~~~~~~~~~~~~ error: expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` --> $DIR/anon-params-denied-2018.rs:18:74 @@ -89,10 +81,10 @@ LL | fn foo_with_multiple_qualified_paths(::Baz, ::Baz); | ^ expected one of 8 possible tokens | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) -help: if this is a type, explicitly ignore the parameter name +help: explicitly ignore the parameter name | LL | fn foo_with_multiple_qualified_paths(::Baz, _: ::Baz); - | ++ + | ~~~~~~~~~~~~~~~~~~ error: expected one of `:`, `@`, or `|`, found `,` --> $DIR/anon-params-denied-2018.rs:22:36 diff --git a/tests/ui/suggestions/issue-64252-self-type.rs b/tests/ui/suggestions/issue-64252-self-type.rs index ea76dc8742b9b..128d5e85c22c8 100644 --- a/tests/ui/suggestions/issue-64252-self-type.rs +++ b/tests/ui/suggestions/issue-64252-self-type.rs @@ -1,11 +1,14 @@ // This test checks that a suggestion to add a `self: ` parameter name is provided // to functions where this is applicable. -pub fn foo(Box) { } //~ ERROR expected one of +pub fn foo(Box) { } +//~^ ERROR expected one of `:`, `@`, or `|`, found `<` + struct Bar; impl Bar { - fn bar(Box) { } //~ ERROR expected one of + fn bar(Box) { } + //~^ ERROR expected one of `:`, `@`, or `|`, found `<` } fn main() { } diff --git a/tests/ui/suggestions/issue-64252-self-type.stderr b/tests/ui/suggestions/issue-64252-self-type.stderr index dbef39faeadf6..c3418dab0e8af 100644 --- a/tests/ui/suggestions/issue-64252-self-type.stderr +++ b/tests/ui/suggestions/issue-64252-self-type.stderr @@ -15,7 +15,7 @@ LL | pub fn foo(_: Box) { } | ++ error: expected one of `:`, `@`, or `|`, found `<` - --> $DIR/issue-64252-self-type.rs:8:15 + --> $DIR/issue-64252-self-type.rs:10:15 | LL | fn bar(Box) { } | ^ expected one of `:`, `@`, or `|` From fa9d5e79990fdd28ab37820105e42192b72cffe1 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 14 Jul 2023 08:20:29 +0800 Subject: [PATCH 21/21] match scrutinee need necessary parentheses for structs --- compiler/rustc_lint/src/unused.rs | 18 +++++++++++++ tests/ui/lint/lint-struct-necessary.rs | 31 ++++++++++++++++++++++ tests/ui/lint/lint-struct-necessary.stderr | 19 +++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 tests/ui/lint/lint-struct-necessary.rs create mode 100644 tests/ui/lint/lint-struct-necessary.stderr diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index a3c5ca4cda19c..48d5b59992798 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -666,6 +666,24 @@ trait UnusedDelimLint { if !followed_by_block { return false; } + + // Check if we need parens for `match &( Struct { feild: }) {}`. + { + let mut innermost = inner; + loop { + innermost = match &innermost.kind { + ExprKind::AddrOf(_, _, expr) => expr, + _ => { + if parser::contains_exterior_struct_lit(&innermost) { + return true; + } else { + break; + } + } + } + } + } + let mut innermost = inner; loop { innermost = match &innermost.kind { diff --git a/tests/ui/lint/lint-struct-necessary.rs b/tests/ui/lint/lint-struct-necessary.rs new file mode 100644 index 0000000000000..8bc3c12054a20 --- /dev/null +++ b/tests/ui/lint/lint-struct-necessary.rs @@ -0,0 +1,31 @@ +#![allow(dead_code)] +#![deny(unused_parens)] + +enum State { + Waiting { start_at: u64 } +} +struct Foo {} + +fn main() { + let e = &mut State::Waiting { start_at: 0u64 }; + match (&mut State::Waiting { start_at: 0u64 }) { + _ => {} + } + + match (e) { + //~^ ERROR unnecessary parentheses around `match` scrutinee expression + _ => {} + } + + match &(State::Waiting { start_at: 0u64 }) { + _ => {} + } + + match (State::Waiting { start_at: 0u64 }) { + _ => {} + } + + match (&&Foo {}) { + _ => {} + } +} diff --git a/tests/ui/lint/lint-struct-necessary.stderr b/tests/ui/lint/lint-struct-necessary.stderr new file mode 100644 index 0000000000000..eb65a9e98c6e7 --- /dev/null +++ b/tests/ui/lint/lint-struct-necessary.stderr @@ -0,0 +1,19 @@ +error: unnecessary parentheses around `match` scrutinee expression + --> $DIR/lint-struct-necessary.rs:15:11 + | +LL | match (e) { + | ^ ^ + | +note: the lint level is defined here + --> $DIR/lint-struct-necessary.rs:2:9 + | +LL | #![deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - match (e) { +LL + match e { + | + +error: aborting due to previous error +