Skip to content

Commit d7a6212

Browse files
committed
review comments
1 parent c305ac3 commit d7a6212

File tree

8 files changed

+204
-216
lines changed

8 files changed

+204
-216
lines changed

src/librustc/traits/error_reporting/mod.rs

-32
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2525
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
2626
use rustc_hir as hir;
2727
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
28-
use rustc_hir::intravisit::Visitor;
2928
use rustc_span::source_map::SourceMap;
3029
use rustc_span::{ExpnKind, Span, DUMMY_SP};
3130
use std::fmt;
@@ -1411,34 +1410,3 @@ pub fn suggest_constraining_type_param(
14111410
}
14121411
false
14131412
}
1414-
1415-
/// Collect all the returned expressions within the input expression.
1416-
/// Used to point at the return spans when we want to suggest some change to them.
1417-
struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>);
1418-
1419-
impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
1420-
type Map = rustc::hir::map::Map<'v>;
1421-
1422-
fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<'_, Self::Map> {
1423-
hir::intravisit::NestedVisitorMap::None
1424-
}
1425-
1426-
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
1427-
match ex.kind {
1428-
hir::ExprKind::Ret(Some(ex)) => self.0.push(ex),
1429-
_ => {}
1430-
}
1431-
hir::intravisit::walk_expr(self, ex);
1432-
}
1433-
1434-
fn visit_body(&mut self, body: &'v hir::Body<'v>) {
1435-
if body.generator_kind().is_none() {
1436-
if let hir::ExprKind::Block(block, None) = body.value.kind {
1437-
if let Some(expr) = block.expr {
1438-
self.0.push(expr);
1439-
}
1440-
}
1441-
}
1442-
hir::intravisit::walk_body(self, body);
1443-
}
1444-
}

src/librustc/traits/error_reporting/suggestions.rs

+137-133
Original file line numberDiff line numberDiff line change
@@ -563,157 +563,159 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
563563
let hir = self.tcx.hir();
564564
let parent_node = hir.get_parent_node(obligation.cause.body_id);
565565
let node = hir.find(parent_node);
566-
if let Some(hir::Node::Item(hir::Item {
567-
kind: hir::ItemKind::Fn(sig, _, body_id), ..
566+
let (sig, body_id) = if let Some(hir::Node::Item(hir::Item {
567+
kind: hir::ItemKind::Fn(sig, _, body_id),
568+
..
568569
})) = node
569570
{
570-
let body = hir.body(*body_id);
571-
let trait_ref = self.resolve_vars_if_possible(trait_ref);
572-
let ty = trait_ref.skip_binder().self_ty();
573-
let is_object_safe;
574-
match ty.kind {
575-
ty::Dynamic(predicates, _) => {
576-
// The `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`.
577-
is_object_safe = predicates.principal_def_id().map_or(true, |def_id| {
578-
!object_safety_violations(self.tcx, def_id).is_empty()
579-
})
580-
}
581-
// We only want to suggest `impl Trait` to `dyn Trait`s.
582-
// For example, `fn foo() -> str` needs to be filtered out.
583-
_ => return false,
571+
(sig, body_id)
572+
} else {
573+
return false;
574+
};
575+
let body = hir.body(*body_id);
576+
let trait_ref = self.resolve_vars_if_possible(trait_ref);
577+
let ty = trait_ref.skip_binder().self_ty();
578+
let is_object_safe = match ty.kind {
579+
ty::Dynamic(predicates, _) => {
580+
// If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`.
581+
predicates
582+
.principal_def_id()
583+
.map_or(true, |def_id| object_safety_violations(self.tcx, def_id).is_empty())
584584
}
585+
// We only want to suggest `impl Trait` to `dyn Trait`s.
586+
// For example, `fn foo() -> str` needs to be filtered out.
587+
_ => return false,
588+
};
585589

586-
let ret_ty = if let hir::FunctionRetTy::Return(ret_ty) = sig.decl.output {
587-
ret_ty
588-
} else {
589-
return false;
590-
};
591-
592-
// Use `TypeVisitor` instead of the output type directly to find the span of `ty` for
593-
// cases like `fn foo() -> (dyn Trait, i32) {}`.
594-
// Recursively look for `TraitObject` types and if there's only one, use that span to
595-
// suggest `impl Trait`.
596-
597-
// Visit to make sure there's a single `return` type to suggest `impl Trait`,
598-
// otherwise suggest using `Box<dyn Trait>` or an enum.
599-
let mut visitor = ReturnsVisitor(vec![]);
600-
visitor.visit_body(&body);
601-
602-
let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap();
590+
let ret_ty = if let hir::FunctionRetTy::Return(ret_ty) = sig.decl.output {
591+
ret_ty
592+
} else {
593+
return false;
594+
};
603595

604-
let mut all_returns_conform_to_trait = true;
605-
let mut all_returns_have_same_type = true;
606-
let mut last_ty = None;
607-
if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) {
608-
let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id);
609-
let param_env = ty::ParamEnv::empty();
610-
if let ty::Dynamic(predicates, _) = &ty_ret_ty.kind {
611-
for expr in &visitor.0 {
612-
if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) {
613-
all_returns_have_same_type &=
614-
Some(returned_ty) == last_ty || last_ty.is_none();
615-
last_ty = Some(returned_ty);
616-
for predicate in predicates.iter() {
617-
let pred = predicate.with_self_ty(self.tcx, returned_ty);
618-
let obl = Obligation::new(cause.clone(), param_env, pred);
619-
all_returns_conform_to_trait &= self.predicate_may_hold(&obl);
620-
}
596+
// Use `TypeVisitor` instead of the output type directly to find the span of `ty` for
597+
// cases like `fn foo() -> (dyn Trait, i32) {}`.
598+
// Recursively look for `TraitObject` types and if there's only one, use that span to
599+
// suggest `impl Trait`.
600+
601+
// Visit to make sure there's a single `return` type to suggest `impl Trait`,
602+
// otherwise suggest using `Box<dyn Trait>` or an enum.
603+
let mut visitor = ReturnsVisitor(vec![]);
604+
visitor.visit_body(&body);
605+
606+
let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap();
607+
608+
let mut all_returns_conform_to_trait = true;
609+
let mut all_returns_have_same_type = true;
610+
let mut last_ty = None;
611+
if let Some(ty_ret_ty) = tables.node_type_opt(ret_ty.hir_id) {
612+
let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id);
613+
let param_env = ty::ParamEnv::empty();
614+
if let ty::Dynamic(predicates, _) = &ty_ret_ty.kind {
615+
for expr in &visitor.0 {
616+
if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) {
617+
all_returns_have_same_type &=
618+
Some(returned_ty) == last_ty || last_ty.is_none();
619+
last_ty = Some(returned_ty);
620+
for predicate in predicates.iter() {
621+
let pred = predicate.with_self_ty(self.tcx, returned_ty);
622+
let obl = Obligation::new(cause.clone(), param_env, pred);
623+
all_returns_conform_to_trait &= self.predicate_may_hold(&obl);
621624
}
622625
}
623626
}
624-
} else {
625-
// We still want to verify whether all the return types conform to each other.
626-
for expr in &visitor.0 {
627-
let returned_ty = tables.node_type_opt(expr.hir_id);
628-
all_returns_have_same_type &= last_ty == returned_ty || last_ty.is_none();
629-
last_ty = returned_ty;
630-
}
631627
}
628+
} else {
629+
// We still want to verify whether all the return types conform to each other.
630+
for expr in &visitor.0 {
631+
let returned_ty = tables.node_type_opt(expr.hir_id);
632+
all_returns_have_same_type &= last_ty == returned_ty || last_ty.is_none();
633+
last_ty = returned_ty;
634+
}
635+
}
632636

633-
let (snippet, last_ty) =
634-
if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true, Some(last_ty)) = (
635-
// Verify that we're dealing with a return `dyn Trait`
636-
ret_ty.span.overlaps(span),
637-
&ret_ty.kind,
638-
self.tcx.sess.source_map().span_to_snippet(ret_ty.span),
639-
// If any of the return types does not conform to the trait, then we can't
640-
// suggest `impl Trait` nor trait objects, it is a type mismatch error.
641-
all_returns_conform_to_trait,
642-
last_ty,
643-
) {
644-
(snippet, last_ty)
645-
} else {
646-
return false;
647-
};
648-
err.code(error_code!(E0746));
649-
err.set_primary_message("return type cannot have an unboxed trait object");
650-
err.children.clear();
651-
let impl_trait_msg = "for information on `impl Trait`, see \
652-
<https://doc.rust-lang.org/book/ch10-02-traits.html\
653-
#returning-types-that-implement-traits>";
654-
let trait_obj_msg = "for information on trait objects, see \
655-
<https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
656-
#using-trait-objects-that-allow-for-values-of-different-types>";
657-
let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn");
658-
let trait_obj = if has_dyn { &snippet[4..] } else { &snippet[..] };
659-
if all_returns_have_same_type {
660-
// Suggest `-> impl Trait`.
661-
err.span_suggestion(
637+
let (snippet, last_ty) =
638+
if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true, Some(last_ty)) = (
639+
// Verify that we're dealing with a return `dyn Trait`
640+
ret_ty.span.overlaps(span),
641+
&ret_ty.kind,
642+
self.tcx.sess.source_map().span_to_snippet(ret_ty.span),
643+
// If any of the return types does not conform to the trait, then we can't
644+
// suggest `impl Trait` nor trait objects, it is a type mismatch error.
645+
all_returns_conform_to_trait,
646+
last_ty,
647+
) {
648+
(snippet, last_ty)
649+
} else {
650+
return false;
651+
};
652+
err.code(error_code!(E0746));
653+
err.set_primary_message("return type cannot have an unboxed trait object");
654+
err.children.clear();
655+
let impl_trait_msg = "for information on `impl Trait`, see \
656+
<https://doc.rust-lang.org/book/ch10-02-traits.html\
657+
#returning-types-that-implement-traits>";
658+
let trait_obj_msg = "for information on trait objects, see \
659+
<https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
660+
#using-trait-objects-that-allow-for-values-of-different-types>";
661+
let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn");
662+
let trait_obj = if has_dyn { &snippet[4..] } else { &snippet[..] };
663+
if all_returns_have_same_type {
664+
// Suggest `-> impl Trait`.
665+
err.span_suggestion(
666+
ret_ty.span,
667+
&format!(
668+
"return `impl {1}` instead, as all return paths are of type `{}`, \
669+
which implements `{1}`",
670+
last_ty, trait_obj,
671+
),
672+
format!("impl {}", trait_obj),
673+
Applicability::MachineApplicable,
674+
);
675+
err.note(impl_trait_msg);
676+
} else {
677+
if is_object_safe {
678+
// Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
679+
// Get all the return values and collect their span and suggestion.
680+
let mut suggestions = visitor
681+
.0
682+
.iter()
683+
.map(|expr| {
684+
(
685+
expr.span,
686+
format!(
687+
"Box::new({})",
688+
self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap()
689+
),
690+
)
691+
})
692+
.collect::<Vec<_>>();
693+
// Add the suggestion for the return type.
694+
suggestions.push((
662695
ret_ty.span,
663-
&format!(
664-
"return `impl {1}` instead, as all return paths are of type `{}`, \
665-
which implements `{1}`",
666-
last_ty, trait_obj,
667-
),
668-
format!("impl {}", trait_obj),
669-
Applicability::MachineApplicable,
696+
format!("Box<{}{}>", if has_dyn { "" } else { "dyn " }, snippet),
697+
));
698+
err.multipart_suggestion(
699+
"return a trait object instead",
700+
suggestions,
701+
Applicability::MaybeIncorrect,
670702
);
671-
err.note(impl_trait_msg);
672703
} else {
673-
if is_object_safe {
674-
// Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
675-
// Get all the return values and collect their span and suggestion.
676-
let mut suggestions = visitor
677-
.0
678-
.iter()
679-
.map(|expr| {
680-
(
681-
expr.span,
682-
format!(
683-
"Box::new({})",
684-
self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap()
685-
),
686-
)
687-
})
688-
.collect::<Vec<_>>();
689-
// Add the suggestion for the return type.
690-
suggestions.push((
691-
ret_ty.span,
692-
format!("Box<{}{}>", if has_dyn { "" } else { "dyn " }, snippet),
693-
));
694-
err.multipart_suggestion(
695-
"return a trait object instead",
696-
suggestions,
697-
Applicability::MaybeIncorrect,
698-
);
699-
} else {
700-
err.note(&format!(
701-
"if trait `{}` was object safe, you could return a trait object",
702-
trait_obj,
703-
));
704-
}
705704
err.note(&format!(
706-
"if all the returned values were of the same type you could use \
707-
`impl {}` as the return type",
705+
"if trait `{}` was object safe, you could return a trait object",
708706
trait_obj,
709707
));
710-
err.note(impl_trait_msg);
711-
err.note(trait_obj_msg);
712-
err.note("you can create a new `enum` with a variant for each returned type");
713708
}
714-
return true;
709+
err.note(trait_obj_msg);
710+
err.note(&format!(
711+
"if all the returned values were of the same type you could use \
712+
`impl {}` as the return type",
713+
trait_obj,
714+
));
715+
err.note(impl_trait_msg);
716+
err.note("you can create a new `enum` with a variant for each returned type");
715717
}
716-
false
718+
true
717719
}
718720

719721
crate fn point_at_returns_when_relevant(
@@ -1686,6 +1688,8 @@ pub fn suggest_constraining_type_param(
16861688
false
16871689
}
16881690

1691+
/// Collect all the returned expressions within the input expression.
1692+
/// Used to point at the return spans when we want to suggest some change to them.
16891693
struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>);
16901694

16911695
impl<'v> Visitor<'v> for ReturnsVisitor<'v> {

src/librustc/traits/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1171,7 +1171,7 @@ impl<'tcx> ObligationCause<'tcx> {
11711171
}
11721172
}
11731173

1174-
impl<'tcx> ObligationCauseCode<'tcx> {
1174+
impl ObligationCauseCode<'_> {
11751175
// Return the base obligation, ignoring derived obligations.
11761176
pub fn peel_derives(&self) -> &Self {
11771177
let mut base_cause = self;

src/librustc_error_codes/error_codes/E0746.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ impl T for S {
1212
fn bar(&self) {}
1313
}
1414
15-
// Having the trait `T` as return type is invalid because bare traits do not
16-
// have a statically known size:
15+
// Having the trait `T` as return type is invalid because
16+
// bare trait objects do not have a statically known size:
1717
fn foo() -> dyn T {
1818
S(42)
1919
}
@@ -32,15 +32,15 @@ If there is a single type involved, you can use [`impl Trait`]:
3232
# fn bar(&self) {}
3333
# }
3434
// The compiler will select `S(usize)` as the materialized return type of this
35-
// function, but callers will only be able to access associated items from `T`.
35+
// function, but callers will only know that the return type implements `T`.
3636
fn foo() -> impl T {
3737
S(42)
3838
}
3939
```
4040

4141
If there are multiple types involved, the only way you care to interact with
42-
them is through the trait's interface and having to rely on dynamic dispatch is
43-
acceptable, then you can use [trait objects] with `Box`, or other container
42+
them is through the trait's interface, and having to rely on dynamic dispatch
43+
is acceptable, then you can use [trait objects] with `Box`, or other container
4444
types like `Rc` or `Arc`:
4545

4646
```

0 commit comments

Comments
 (0)