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

compiler/rustc_hir_typeck/src/demand.rs
src/test/ui/type/binding-assigned-block-without-tail-expression.rs [new file with mode: 0644]
src/test/ui/type/binding-assigned-block-without-tail-expression.stderr [new file with mode: 0644]

index 9c6c53abf07488ce6d6b2c74243c0724ecd2b428..a9e6b1411700968b7c9bc09347192ba0083a521a 100644 (file)
@@ -75,6 +75,7 @@ pub fn emit_coerce_suggestions(
         self.note_need_for_fn_pointer(err, expected, expr_ty);
         self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
         self.check_for_range_as_method_call(err, expr, expr_ty, expected);
+        self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
     }
 
     /// Requires that the two types unify, and prints an error message if
@@ -1670,4 +1671,48 @@ pub fn check_for_range_as_method_call(
             Applicability::MachineApplicable,
         );
     }
+
+    /// Identify when the type error is because `()` is found in a binding that was assigned a
+    /// block without a tail expression.
+    fn check_for_binding_assigned_block_without_tail_expression(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        checked_ty: Ty<'tcx>,
+        expected_ty: Ty<'tcx>,
+    ) {
+        if !checked_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) = self.node_ty_opt(tail_expr.hir_id) else { return; };
+        if self.can_eq(self.param_env, expected_ty, ty).is_ok() {
+            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");
+        }
+    }
 }
diff --git a/src/test/ui/type/binding-assigned-block-without-tail-expression.rs b/src/test/ui/type/binding-assigned-block-without-tail-expression.rs
new file mode 100644 (file)
index 0000000..b5b23a9
--- /dev/null
@@ -0,0 +1,16 @@
+fn main() {
+    let x = {
+        println!("foo");
+        42;
+    };
+    let y = {};
+    let z = {
+        "hi";
+    };
+    println!("{}", x); //~ ERROR E0277
+    println!("{}", y); //~ ERROR E0277
+    println!("{}", z); //~ ERROR E0277
+    let _: i32 = x; //~ ERROR E0308
+    let _: i32 = y; //~ ERROR E0308
+    let _: i32 = z; //~ ERROR E0308
+}
diff --git a/src/test/ui/type/binding-assigned-block-without-tail-expression.stderr b/src/test/ui/type/binding-assigned-block-without-tail-expression.stderr
new file mode 100644 (file)
index 0000000..4630010
--- /dev/null
@@ -0,0 +1,79 @@
+error[E0277]: `()` doesn't implement `std::fmt::Display`
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:10:20
+   |
+LL |     println!("{}", x);
+   |                    ^ `()` cannot be formatted with the default formatter
+   |
+   = 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:11:20
+   |
+LL |     println!("{}", y);
+   |                    ^ `()` cannot be formatted with the default formatter
+   |
+   = 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
+   |
+LL |     println!("{}", z);
+   |                    ^ `()` cannot be formatted with the default formatter
+   |
+   = 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
+   |
+LL |     let _: i32 = x;
+   |            ---   ^ expected `i32`, found `()`
+   |            |
+   |            expected due to this
+   |
+help: remove this semicolon
+   |
+LL -         42;
+LL +         42
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:14:18
+   |
+LL |     let _: i32 = y;
+   |            ---   ^ expected `i32`, found `()`
+   |            |
+   |            expected due to this
+   |
+help: this empty block is missing a tail expression
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:6:13
+   |
+LL |     let y = {};
+   |             ^^
+
+error[E0308]: mismatched types
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:15:18
+   |
+LL |     let _: i32 = z;
+   |            ---   ^ expected `i32`, found `()`
+   |            |
+   |            expected due to this
+   |
+help: this block is missing a tail expression
+  --> $DIR/binding-assigned-block-without-tail-expression.rs:7:13
+   |
+LL |       let z = {
+   |  _____________^
+LL | |         "hi";
+LL | |     };
+   | |_____^
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0277, E0308.
+For more information about an error, try `rustc --explain E0277`.