]> git.lizzy.rs Git - rust.git/commitdiff
Detect bindings assigned blocks without tail expressions in trait errors
authorEsteban Küber <esteban@kuber.com.ar>
Fri, 6 Jan 2023 02:43:16 +0000 (02:43 +0000)
committerEsteban Küber <esteban@kuber.com.ar>
Fri, 6 Jan 2023 02:43:16 +0000 (02:43 +0000)
Address  #44173 for trait errors.

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
src/test/ui/type/binding-assigned-block-without-tail-expression.rs
src/test/ui/type/binding-assigned-block-without-tail-expression.stderr

index 6996ddd8729948b00635b11c8eb9c29e3071a0f5..714fbcf4eaa68702b02734791a3b44b01bb51b7e 100644 (file)
@@ -771,7 +771,11 @@ fn report_selection_error(
                                 ),
                             }
                         };
-
+                        self.check_for_binding_assigned_block_without_tail_expression(
+                            &obligation,
+                            &mut err,
+                            trait_predicate,
+                        );
                         if self.suggest_add_reference_to_arg(
                             &obligation,
                             &mut err,
@@ -2267,23 +2271,7 @@ fn maybe_report_ambiguity(
                 if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) =
                     (body_id, subst.map(|subst| subst.unpack()))
                 {
-                    struct FindExprBySpan<'hir> {
-                        span: Span,
-                        result: Option<&'hir hir::Expr<'hir>>,
-                    }
-
-                    impl<'v> hir::intravisit::Visitor<'v> for FindExprBySpan<'v> {
-                        fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
-                            if self.span == ex.span {
-                                self.result = Some(ex);
-                            } else {
-                                hir::intravisit::walk_expr(self, ex);
-                            }
-                        }
-                    }
-
-                    let mut expr_finder = FindExprBySpan { span, result: None };
-
+                    let mut expr_finder = FindExprBySpan::new(span);
                     expr_finder.visit_expr(&self.tcx.hir().body(body_id).value);
 
                     if let Some(hir::Expr {
@@ -2770,6 +2758,36 @@ fn is_recursive_obligation(
     }
 }
 
+/// Crude way of getting back an `Expr` from a `Span`.
+pub struct FindExprBySpan<'hir> {
+    pub span: Span,
+    pub result: Option<&'hir hir::Expr<'hir>>,
+    pub ty_result: Option<&'hir hir::Ty<'hir>>,
+}
+
+impl<'hir> FindExprBySpan<'hir> {
+    fn new(span: Span) -> Self {
+        Self { span, result: None, ty_result: None }
+    }
+}
+
+impl<'v> Visitor<'v> for FindExprBySpan<'v> {
+    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+        if self.span == ex.span {
+            self.result = Some(ex);
+        } else {
+            hir::intravisit::walk_expr(self, ex);
+        }
+    }
+    fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+        if self.span == ty.span {
+            self.ty_result = Some(ty);
+        } else {
+            hir::intravisit::walk_ty(self, ty);
+        }
+    }
+}
+
 /// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
 /// `param: ?Sized` would be a valid constraint.
 struct FindTypeParam {
index 8c2c182877496587a207501851fe73cbc882761f..ad2711209e259426f9347c2415de937170b7d01d 100644 (file)
@@ -1,6 +1,9 @@
 // ignore-tidy-filelength
 
-use super::{DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation};
+use super::{
+    DefIdOrName, FindExprBySpan, Obligation, ObligationCause, ObligationCauseCode,
+    PredicateObligation,
+};
 
 use crate::autoderef::Autoderef;
 use crate::infer::InferCtxt;
@@ -196,6 +199,13 @@ fn suggest_fn_call(
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool;
 
+    fn check_for_binding_assigned_block_without_tail_expression(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    );
+
     fn suggest_add_reference_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
@@ -1032,6 +1042,66 @@ fn suggest_fn_call(
         true
     }
 
+    fn check_for_binding_assigned_block_without_tail_expression(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) {
+        let mut span = obligation.cause.span;
+        while span.from_expansion() {
+            // Remove all the desugaring and macro contexts.
+            span.remove_mark();
+        }
+        let mut expr_finder = FindExprBySpan::new(span);
+        let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else { return; };
+        expr_finder.visit_expr(&body);
+        let Some(expr) = expr_finder.result else { return; };
+        let Some(typeck) = &self.typeck_results else { return; };
+        let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else { return; };
+        if !ty.is_unit() {
+            return;
+        };
+        let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { return; };
+        let hir::def::Res::Local(hir_id) = path.res else { return; };
+        let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else {
+            return;
+        };
+        let Some(hir::Node::Local(hir::Local {
+            ty: None,
+            init: Some(init),
+            ..
+        })) = self.tcx.hir().find_parent(pat.hir_id) else { return; };
+        let hir::ExprKind::Block(block, None) = init.kind else { return; };
+        if block.expr.is_some() {
+            return;
+        }
+        let [.., stmt] = block.stmts else {
+            err.span_help(block.span, "this empty block is missing a tail expression");
+            return;
+        };
+        let hir::StmtKind::Semi(tail_expr) = stmt.kind else { return; };
+        let Some(ty) = typeck.expr_ty_opt(tail_expr) else {
+            err.span_help(block.span, "this block is missing a tail expression");
+            return;
+        };
+        let ty = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(ty));
+        let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, ty));
+
+        let new_obligation =
+            self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);
+        if self.predicate_must_hold_modulo_regions(&new_obligation) {
+            err.span_suggestion_verbose(
+                stmt.span.with_lo(tail_expr.span.hi()),
+                "remove this semicolon",
+                "",
+                Applicability::MachineApplicable,
+            );
+        } else {
+            err.span_help(block.span, "this block is missing a tail expression");
+        }
+    }
+
     fn suggest_add_reference_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
index b5b23a9e67282966d7f7baa697cd07845dba009d..09afd27a079ba6111003e9181a9c949950e41018 100644 (file)
@@ -1,3 +1,4 @@
+struct S;
 fn main() {
     let x = {
         println!("foo");
@@ -7,10 +8,15 @@ fn main() {
     let z = {
         "hi";
     };
+    let s = {
+        S;
+    };
     println!("{}", x); //~ ERROR E0277
     println!("{}", y); //~ ERROR E0277
     println!("{}", z); //~ ERROR E0277
+    println!("{}", s); //~ ERROR E0277
     let _: i32 = x; //~ ERROR E0308
     let _: i32 = y; //~ ERROR E0308
     let _: i32 = z; //~ ERROR E0308
+    let _: i32 = s; //~ ERROR E0308
 }
index 46300104b05a66e5bd7cf919883e269abe3000d5..646c632517aea401b8017b8bf801794497d74dd7 100644 (file)
@@ -1,5 +1,5 @@
 error[E0277]: `()` doesn't implement `std::fmt::Display`
-  --> $DIR/binding-assigned-block-without-tail-expression.rs:10:20
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:14:20
    |
 LL |     println!("{}", x);
    |                    ^ `()` cannot be formatted with the default formatter
@@ -7,19 +7,29 @@ LL |     println!("{}", x);
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: remove this semicolon
+   |
+LL -         42;
+LL +         42
+   |
 
 error[E0277]: `()` doesn't implement `std::fmt::Display`
-  --> $DIR/binding-assigned-block-without-tail-expression.rs:11:20
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:15:20
    |
 LL |     println!("{}", y);
    |                    ^ `()` cannot be formatted with the default formatter
    |
+help: this empty block is missing a tail expression
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:7:13
+   |
+LL |     let y = {};
+   |             ^^
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `()` doesn't implement `std::fmt::Display`
-  --> $DIR/binding-assigned-block-without-tail-expression.rs:12:20
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:16:20
    |
 LL |     println!("{}", z);
    |                    ^ `()` cannot be formatted with the default formatter
@@ -27,9 +37,32 @@ LL |     println!("{}", z);
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: remove this semicolon
+   |
+LL -         "hi";
+LL +         "hi"
+   |
+
+error[E0277]: `()` doesn't implement `std::fmt::Display`
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:17:20
+   |
+LL |     println!("{}", s);
+   |                    ^ `()` cannot be formatted with the default formatter
+   |
+help: this block is missing a tail expression
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:11:13
+   |
+LL |       let s = {
+   |  _____________^
+LL | |         S;
+LL | |     };
+   | |_____^
+   = help: the trait `std::fmt::Display` is not implemented for `()`
+   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0308]: mismatched types
-  --> $DIR/binding-assigned-block-without-tail-expression.rs:13:18
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:18:18
    |
 LL |     let _: i32 = x;
    |            ---   ^ expected `i32`, found `()`
@@ -43,7 +76,7 @@ LL +         42
    |
 
 error[E0308]: mismatched types
-  --> $DIR/binding-assigned-block-without-tail-expression.rs:14:18
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:19:18
    |
 LL |     let _: i32 = y;
    |            ---   ^ expected `i32`, found `()`
@@ -51,13 +84,13 @@ LL |     let _: i32 = y;
    |            expected due to this
    |
 help: this empty block is missing a tail expression
-  --> $DIR/binding-assigned-block-without-tail-expression.rs:6:13
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:7:13
    |
 LL |     let y = {};
    |             ^^
 
 error[E0308]: mismatched types
-  --> $DIR/binding-assigned-block-without-tail-expression.rs:15:18
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:20:18
    |
 LL |     let _: i32 = z;
    |            ---   ^ expected `i32`, found `()`
@@ -65,7 +98,7 @@ LL |     let _: i32 = z;
    |            expected due to this
    |
 help: this block is missing a tail expression
-  --> $DIR/binding-assigned-block-without-tail-expression.rs:7:13
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:8:13
    |
 LL |       let z = {
    |  _____________^
@@ -73,7 +106,24 @@ LL | |         "hi";
 LL | |     };
    | |_____^
 
-error: aborting due to 6 previous errors
+error[E0308]: mismatched types
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:21:18
+   |
+LL |     let _: i32 = s;
+   |            ---   ^ expected `i32`, found `()`
+   |            |
+   |            expected due to this
+   |
+help: this block is missing a tail expression
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:11:13
+   |
+LL |       let s = {
+   |  _____________^
+LL | |         S;
+LL | |     };
+   | |_____^
+
+error: aborting due to 8 previous errors
 
 Some errors have detailed explanations: E0277, E0308.
 For more information about an error, try `rustc --explain E0277`.