Skip to content

Commit

Permalink
Rollup merge of rust-lang#58204 - estebank:impl-trait-semi, r=zackmdavis
Browse files Browse the repository at this point in the history
On return type `impl Trait` for block with no expr point at last semi

Partial solution, doesn't actually validate that the last statement in the function body can satisfy the trait bound, but it's a good incremental improvement over the status quo.

```
error[E0277]: the trait bound `(): Bar` is not satisfied
  --> $DIR/impl-trait-return-trailing-semicolon.rs:3:13
   |
LL | fn foo() -> impl Bar {
   |             ^^^^^^^^ the trait `Bar` is not implemented for `()`
LL |     5;
   |      - consider removing this semicolon
   |
   = note: the return type of a function must have a statically known size
```

Partially addresses rust-lang#54771.
  • Loading branch information
pietroalbini authored Mar 8, 2019
2 parents 19e39cb + e6387b6 commit 4797568
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 12 deletions.
64 changes: 52 additions & 12 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,11 +599,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}

pub fn report_selection_error(&self,
obligation: &PredicateObligation<'tcx>,
error: &SelectionError<'tcx>,
fallback_has_occurred: bool)
{
pub fn report_selection_error(
&self,
obligation: &PredicateObligation<'tcx>,
error: &SelectionError<'tcx>,
fallback_has_occurred: bool,
) {
let span = obligation.cause.span;

let mut err = match *error {
Expand Down Expand Up @@ -673,6 +674,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {

self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err);
self.suggest_remove_reference(&obligation, &mut err, &trait_ref);
self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref);

// Try to report a help message
if !trait_ref.has_infer_types() &&
Expand Down Expand Up @@ -901,9 +903,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {

/// When encountering an assignment of an unsized trait, like `let x = ""[..];`, provide a
/// suggestion to borrow the initializer in order to use have a slice instead.
fn suggest_borrow_on_unsized_slice(&self,
code: &ObligationCauseCode<'tcx>,
err: &mut DiagnosticBuilder<'tcx>) {
fn suggest_borrow_on_unsized_slice(
&self,
code: &ObligationCauseCode<'tcx>,
err: &mut DiagnosticBuilder<'tcx>,
) {
if let &ObligationCauseCode::VariableType(node_id) = code {
let parent_node = self.tcx.hir().get_parent_node(node_id);
if let Some(Node::Local(ref local)) = self.tcx.hir().find(parent_node) {
Expand All @@ -925,10 +929,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {

/// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
/// suggest removing these references until we reach a type that implements the trait.
fn suggest_remove_reference(&self,
obligation: &PredicateObligation<'tcx>,
err: &mut DiagnosticBuilder<'tcx>,
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>) {
fn suggest_remove_reference(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut DiagnosticBuilder<'tcx>,
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
) {
let trait_ref = trait_ref.skip_binder();
let span = obligation.cause.span;

Expand Down Expand Up @@ -970,6 +976,40 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}

fn suggest_semicolon_removal(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut DiagnosticBuilder<'tcx>,
span: Span,
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
) {
let hir = self.tcx.hir();
let parent_node = hir.get_parent_node(
hir.hir_to_node_id(obligation.cause.body_id),
);
let node = hir.find(parent_node);
if let Some(hir::Node::Item(hir::Item {
node: hir::ItemKind::Fn(decl, _, _, body_id),
..
})) = node {
let body = hir.body(*body_id);
if let hir::ExprKind::Block(blk, _) = &body.value.node {
if decl.output.span().overlaps(span) && blk.expr.is_none() &&
"()" == &trait_ref.self_ty().to_string()
{
// FIXME(estebank): When encountering a method with a trait
// bound not satisfied in the return type with a body that has
// no return, suggest removal of semicolon on last statement.
// Once that is added, close #54771.
if let Some(ref stmt) = blk.stmts.last() {
let sp = self.tcx.sess.source_map().end_point(stmt.span);
err.span_label(sp, "consider removing this semicolon");
}
}
}
}
}

/// Given some node representing a fn-like thing in the HIR map,
/// returns a span and `ArgKind` information that describes the
/// arguments it expects. This can be supplied to
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
trait Bar {}
impl Bar for u8 {}
fn foo() -> impl Bar {
5; //~^ ERROR the trait bound `(): Bar` is not satisfied
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0277]: the trait bound `(): Bar` is not satisfied
--> $DIR/impl-trait-return-trailing-semicolon.rs:3:13
|
LL | fn foo() -> impl Bar {
| ^^^^^^^^ the trait `Bar` is not implemented for `()`
LL | 5; //~^ ERROR the trait bound `(): Bar` is not satisfied
| - consider removing this semicolon
|
= note: the return type of a function must have a statically known size

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.

0 comments on commit 4797568

Please sign in to comment.