diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index abb11ce739689..50d875dfae99a 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -498,14 +498,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() { @@ -608,7 +607,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>, @@ -617,7 +616,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..e91103f213015 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, @@ -464,16 +457,23 @@ 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 }); - 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 +497,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 +514,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 +601,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 +617,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 +650,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 be939560c459d..1a41786d251c2 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() @@ -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) }) @@ -679,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 => {} @@ -701,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: ({:?}, {:?})", @@ -1890,6 +1887,7 @@ fn restrict_capture_precision( return (place, curr_mode); } ProjectionKind::Deref => {} + ProjectionKind::OpaqueCast => {} ProjectionKind::Field(..) => {} // ignore } } @@ -1946,6 +1944,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_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/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index f482e3d7c1280..0eacb4a000b2d 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -213,9 +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 = 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_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 @@ -454,6 +451,13 @@ 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 + .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}` + 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/fn_null_check.rs b/compiler/rustc_lint/src/fn_null_check.rs deleted file mode 100644 index e3b33463ccfbc..0000000000000 --- a/compiler/rustc_lint/src/fn_null_check.rs +++ /dev/null @@ -1,112 +0,0 @@ -use crate::{lints::FnNullCheckDiag, 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. - /// - /// ### Example - /// - /// ```rust - /// # fn test() {} - /// let fn_ptr: fn() = /* somehow obtained nullable function pointer */ - /// # test; - /// - /// if (fn_ptr as *const ()).is_null() { /* ... */ } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Function pointers are assumed to be non-null, checking them for null will always - /// return false. - INCORRECT_FN_NULL_CHECKS, - Warn, - "incorrect checking of null function pointer" -} - -declare_lint_pass!(IncorrectFnNullChecks => [INCORRECT_FN_NULL_CHECKS]); - -fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let mut expr = expr.peel_blocks(); - 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; - } - had_at_least_one_cast && cx.typeck_results().expr_ty_adjusted(expr).is_fn() -} - -impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - match expr.kind { - // Catching: - // <* >::is_null(fn_ptr as * ) - ExprKind::Call(path, [arg]) - if 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_const_is_null | sym::ptr_is_null) - ) - && is_fn_ptr_cast(cx, arg) => - { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) - } - - // Catching: - // (fn_ptr as * ).is_null() - ExprKind::MethodCall(_, receiver, _, _) - if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && matches!( - cx.tcx.get_diagnostic_name(def_id), - Some(sym::ptr_const_is_null | sym::ptr_is_null) - ) - && is_fn_ptr_cast(cx, receiver) => - { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) - } - - ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => { - let to_check: &Expr<'_>; - if is_fn_ptr_cast(cx, left) { - to_check = right; - } else if is_fn_ptr_cast(cx, right) { - to_check = left; - } else { - return; - } - - match to_check.kind { - // Catching: - // (fn_ptr as * ) == (0 as ) - ExprKind::Cast(cast_expr, _) - 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) - }, - - // Catching: - // (fn_ptr as * ) == std::ptr::null() - ExprKind::Call(path, []) - if let ExprKind::Path(ref qpath) = path.kind - && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && 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) - }, - - _ => {}, - } - } - _ => {} - } - } -} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 96fd3ccf77444..a07b772d35a73 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 a6a48bf4ffa74..3d65ac26bd579 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -613,11 +613,23 @@ pub struct ExpectationNote { pub rationale: Symbol, } -// fn_null_check.rs +// ptr_nulls.rs #[derive(LintDiagnostic)] -#[diag(lint_fn_null_check)] -#[help] -pub struct FnNullCheckDiag; +pub enum PtrNullChecksDiag<'a> { + #[diag(lint_ptr_null_checks_fn_ptr)] + #[help(lint_help)] + FnPtr { + orig_ty: Ty<'a>, + #[label] + label: Span, + }, + #[diag(lint_ptr_null_checks_ref)] + Ref { + orig_ty: Ty<'a>, + #[label] + label: Span, + }, +} // for_loops_over_fallibles.rs #[derive(LintDiagnostic)] diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs new file mode 100644 index 0000000000000..02aff91032fd9 --- /dev/null +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -0,0 +1,146 @@ +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 `useless_ptr_null_checks` lint checks for useless null checks against pointers + /// obtained from non-null types. + /// + /// ### Example + /// + /// ```rust + /// # fn test() {} + /// let fn_ptr: fn() = /* somehow obtained nullable function pointer */ + /// # test; + /// + /// if (fn_ptr as *const ()).is_null() { /* ... */ } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Function pointers and references are assumed to be non-null, checking them for null + /// will always return false. + USELESS_PTR_NULL_CHECKS, + Warn, + "useless checking of non-null-typed pointer" +} + +declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]); + +/// 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; + 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; + }; + } +} + +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 { orig_ty, label: expr.span }) + } else if orig_ty.is_ref() { + Some(PtrNullChecksDiag::Ref { orig_ty, label: expr.span }) + } else { + None + } +} + +impl<'tcx> LateLintPass<'tcx> for PtrNullChecks { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + match expr.kind { + // Catching: + // <* >::is_null(fn_ptr as * ) + ExprKind::Call(path, [arg]) + if 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_const_is_null | sym::ptr_is_null) + ) + && let Some(diag) = incorrect_check(cx, arg) => + { + cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) + } + + // Catching: + // (fn_ptr as * ).is_null() + ExprKind::MethodCall(_, receiver, _, _) + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::ptr_const_is_null | sym::ptr_is_null) + ) + && let Some(diag) = incorrect_check(cx, receiver) => + { + 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: PtrNullChecksDiag<'_>; + if let Some(ddiag) = incorrect_check(cx, left) { + to_check = right; + diag = ddiag; + } else if let Some(ddiag) = incorrect_check(cx, right) { + to_check = left; + diag = ddiag; + } else { + return; + } + + match to_check.kind { + // Catching: + // (fn_ptr as * ) == (0 as ) + ExprKind::Cast(cast_expr, _) + if let ExprKind::Lit(spanned) = cast_expr.kind + && let LitKind::Int(v, _) = spanned.node && v == 0 => + { + cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) + }, + + // Catching: + // (fn_ptr as * ) == std::ptr::null() + ExprKind::Call(path, []) + if let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && 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(USELESS_PTR_NULL_CHECKS, expr.span, diag) + }, + + _ => {}, + } + } + _ => {} + } + } +} 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/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 42f2260497555..2e5c6a4457900 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 ff4620948fa1f..b907b69224c91 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1072,6 +1072,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/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 83d96ad8e7665..f5c4b13c5edf2 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 @@ -270,6 +272,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..553ef4b51c8f3 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 { @@ -2731,3 +2738,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/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index e6de51a673c85..3f13937f72e50 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 { @@ -2100,7 +2107,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 +2515,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 +2779,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 dc3b131e7f273..c0459789805a3 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -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/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index adb0d372a40df..f5681532b3af7 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, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index fed16278db57e..c709a0fc9da9a 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}; @@ -80,7 +81,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 +93,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 +116,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 +127,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 +140,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)?; if rc == RecoverComma::Yes { self.maybe_recover_unexpected_comma(first_pat.span, rt)?; } @@ -172,7 +179,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 +215,7 @@ impl<'a> Parser<'a> { rc, RecoverColon::No, CommaRecoveryMode::LikelyTuple, + Some(syntax_loc), )?; let colon = self.eat(&token::Colon); @@ -319,6 +327,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); @@ -358,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) { @@ -384,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() { @@ -485,7 +494,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 +550,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)) } @@ -566,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(); @@ -584,7 +593,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 @@ -810,10 +819,25 @@ impl<'a> Parser<'a> { /// 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))?) + Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) } else { None }; @@ -902,14 +926,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_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 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/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index eae13eb630290..4d85e2b60893e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2987,6 +2987,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/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 _ 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/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/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 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>`. 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/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/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`. diff --git a/tests/ui/lint/fn_null_check.rs b/tests/ui/lint/fn_null_check.rs deleted file mode 100644 index 7f01f2c428395..0000000000000 --- a/tests/ui/lint/fn_null_check.rs +++ /dev/null @@ -1,30 +0,0 @@ -// check-pass - -fn main() { - let fn_ptr = main; - - if (fn_ptr as *mut ()).is_null() {} - //~^ WARN function pointers are not nullable - if (fn_ptr as *const u8).is_null() {} - //~^ WARN function pointers are not nullable - if (fn_ptr as *const ()) == std::ptr::null() {} - //~^ WARN function pointers are not nullable - if (fn_ptr as *mut ()) == std::ptr::null_mut() {} - //~^ WARN function pointers are not nullable - if (fn_ptr as *const ()) == (0 as *const ()) {} - //~^ WARN function pointers are not nullable - if <*const _>::is_null(fn_ptr as *const ()) {} - //~^ 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 fn() as *const ()).is_null() {} - //~^ WARN function pointers are not nullable - - const ZPTR: *const () = 0 as *const _; - const NOT_ZPTR: *const () = 1 as *const _; - - // unlike the uplifted clippy::fn_null_check lint we do - // not lint on them - if (fn_ptr as *const ()) == ZPTR {} - if (fn_ptr as *const ()) == NOT_ZPTR {} -} diff --git a/tests/ui/lint/fn_null_check.stderr b/tests/ui/lint/fn_null_check.stderr deleted file mode 100644 index 0398c0da50feb..0000000000000 --- a/tests/ui/lint/fn_null_check.stderr +++ /dev/null @@ -1,67 +0,0 @@ -warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:6: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 - -warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/fn_null_check.rs:8:8 - | -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 - | -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 - | -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 - | -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 - | -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 - | -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 - | -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 - 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 + diff --git a/tests/ui/lint/ptr_null_checks.rs b/tests/ui/lint/ptr_null_checks.rs new file mode 100644 index 0000000000000..e677ea3094d5e --- /dev/null +++ b/tests/ui/lint/ptr_null_checks.rs @@ -0,0 +1,76 @@ +// check-pass + +#![feature(ptr_from_ref)] + +use std::ptr; + +extern "C" fn c_fn() {} +fn static_i32() -> &'static i32 { &1 } + +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() {} + //~^ WARN function pointers are not nullable + if (fn_ptr as *const ()) == std::ptr::null() {} + //~^ WARN function pointers are not nullable + if (fn_ptr as *mut ()) == std::ptr::null_mut() {} + //~^ WARN function pointers are not nullable + if (fn_ptr as *const ()) == (0 as *const ()) {} + //~^ WARN function pointers are not nullable + if <*const _>::is_null(fn_ptr as *const ()) {} + //~^ 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() {} + //~^ WARN function pointers are not nullable + + // ---------------- 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; + 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 + 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 _; + const NOT_ZPTR: *const () = 1 as *const _; + + // unlike the uplifted clippy::fn_null_check lint we do + // not lint on them + if (fn_ptr as *const ()) == ZPTR {} + if (fn_ptr as *const ()) == NOT_ZPTR {} +} diff --git a/tests/ui/lint/ptr_null_checks.stderr b/tests/ui/lint/ptr_null_checks.stderr new file mode 100644 index 0000000000000..3cee1804b62ae --- /dev/null +++ b/tests/ui/lint/ptr_null_checks.stderr @@ -0,0 +1,225 @@ +warning: function pointers are not nullable, so checking them for null will always return false + --> $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 + +warning: function pointers are not nullable, so checking them for null will always return false + --> $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 + +warning: function pointers are not nullable, so checking them for null will always return false + --> $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 + +warning: function pointers are not nullable, so checking them for null will always return false + --> $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 + +warning: function pointers are not nullable, so checking them for null will always return false + --> $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 + +warning: function pointers are not nullable, so checking them for null will always return false + --> $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 + +warning: function pointers are not nullable, so checking them for null will always return false + --> $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 + +warning: function pointers are not nullable, so checking them for null will always return false + --> $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 + +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() {} + | ^^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `fn() {main}` + | + = 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() {} + | ^--------------^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `fn()` + | + = 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: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 + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/ptr_null_checks.rs:38: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/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() {} + | ^--^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&i32` + +warning: references are not nullable, so checking them for null will always return false + --> $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() {} + | ^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&i32` + +warning: references are not nullable, so checking them for null will always return false + --> $DIR/ptr_null_checks.rs:53: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/ptr_null_checks.rs:55: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/ptr_null_checks.rs:57: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/ptr_null_checks.rs:59: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/ptr_null_checks.rs:61:8 + | +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:63: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:65:8 + | +LL | if (&*{ static_i32() } as *const i32).is_null() {} + | ^------------------^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expression has type `&i32` + +warning: 25 warnings emitted + 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 + 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/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`. 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;