Skip to content

Commit 943ea75

Browse files
committed
Move suggestions out of main error logic
1 parent b700e49 commit 943ea75

File tree

2 files changed

+132
-94
lines changed

2 files changed

+132
-94
lines changed

compiler/rustc_infer/src/traits/error_reporting/mod.rs

Lines changed: 131 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use rustc_ast::TraitObjectSyntax;
55
use rustc_data_structures::fx::FxIndexSet;
66
use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan};
77
use rustc_hir as hir;
8+
use rustc_hir::def::{DefKind, Res};
89
use rustc_hir::def_id::{DefId, LocalDefId};
910
use rustc_hir::intravisit::Visitor;
1011
use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -228,100 +229,10 @@ pub fn report_object_safety_error<'tcx>(
228229
let mut has_suggested = false;
229230
if let Some(hir_id) = hir_id {
230231
let node = tcx.hir_node(hir_id);
231-
if let hir::Node::Ty(ty) = node
232-
&& let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind
233-
{
234-
let mut hir_id = hir_id;
235-
while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) {
236-
hir_id = ty.hir_id;
237-
}
238-
if tcx.parent_hir_node(hir_id).fn_sig().is_some() {
239-
// Do not suggest `impl Trait` when dealing with things like super-traits.
240-
err.span_suggestion_verbose(
241-
ty.span.until(trait_ref.span),
242-
"consider using an opaque type instead",
243-
"impl ",
244-
Applicability::MaybeIncorrect,
245-
);
246-
has_suggested = true;
247-
}
248-
}
249-
if let hir::Node::Expr(expr) = node
250-
&& let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, path_segment)) = expr.kind
251-
&& let hir::TyKind::TraitObject([trait_ref, ..], _, trait_object_syntax) = ty.kind
252-
{
253-
if let TraitObjectSyntax::None = trait_object_syntax
254-
&& !expr.span.edition().at_least_rust_2021()
255-
{
256-
err.span_note(
257-
trait_ref.trait_ref.path.span,
258-
format!(
259-
"`{trait_str}` is the type for the trait in editions 2015 and 2018 and is \
260-
equivalent to writing `dyn {trait_str}`",
261-
),
262-
);
263-
}
264-
let segment = path_segment.ident;
265-
err.help(format!(
266-
"when writing `<dyn {trait_str}>::{segment}` you are requiring `{trait_str}` be \
267-
\"object safe\", which it isn't",
268-
));
269-
let (only, msg, sugg, appl) = if let [only] = &impls[..] {
270-
// We know there's a single implementation for this trait, so we can be explicit on
271-
// the type they should have used.
272-
let ty = tcx.type_of(*only).instantiate_identity();
273-
(
274-
true,
275-
format!(
276-
"specify the specific `impl` for type `{ty}` to avoid requiring \"object \
277-
safety\" from `{trait_str}`",
278-
),
279-
with_no_trimmed_paths!(format!("{ty} as ")),
280-
Applicability::MachineApplicable,
281-
)
282-
} else {
283-
(
284-
false,
285-
format!(
286-
"you might have meant to access the associated function of a specific \
287-
`impl` to avoid requiring \"object safety\" from `{trait_str}`, either \
288-
with some explicit type...",
289-
),
290-
"/* Type */ as ".to_string(),
291-
Applicability::HasPlaceholders,
292-
)
293-
};
294-
// `<dyn Trait>::segment()` or `<Trait>::segment()` to `<Type as Trait>::segment()`
295-
let sp = ty.span.until(trait_ref.trait_ref.path.span);
296-
err.span_suggestion_verbose(sp, msg, sugg, appl);
297-
if !only {
298-
// `<dyn Trait>::segment()` or `<Trait>::segment()` to `Trait::segment()`
299-
err.multipart_suggestion_verbose(
300-
"...or rely on inference if the compiler has enough context to identify the \
301-
desired type on its own...",
302-
vec![
303-
(expr.span.until(trait_ref.trait_ref.path.span), String::new()),
304-
(
305-
path_segment
306-
.ident
307-
.span
308-
.shrink_to_lo()
309-
.with_lo(trait_ref.trait_ref.path.span.hi()),
310-
"::".to_string(),
311-
),
312-
],
313-
Applicability::MaybeIncorrect,
314-
);
315-
// `<dyn Trait>::segment()` or `<Trait>::segment()` to `<_ as Trait>::segment()`
316-
err.span_suggestion_verbose(
317-
ty.span.until(trait_ref.trait_ref.path.span),
318-
"...which is equivalent to",
319-
format!("_ as "),
320-
Applicability::MaybeIncorrect,
321-
);
322-
}
323-
has_suggested = true;
232+
if let hir::Node::Ty(ty) = node {
233+
has_suggested |= suggest_impl_trait_on_bare_trait(tcx, &mut err, ty);
324234
}
235+
has_suggested |= suggest_path_on_bare_trait(tcx, &mut err, node);
325236
}
326237
match &impls[..] {
327238
_ if has_suggested => {}
@@ -366,3 +277,130 @@ pub fn report_object_safety_error<'tcx>(
366277

367278
err
368279
}
280+
281+
pub fn suggest_impl_trait_on_bare_trait(
282+
tcx: TyCtxt<'_>,
283+
err: &mut Diag<'_>,
284+
ty: &hir::Ty<'_>,
285+
) -> bool {
286+
let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind else { return false };
287+
let mut hir_id = ty.hir_id;
288+
while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) {
289+
hir_id = ty.hir_id;
290+
}
291+
if tcx.parent_hir_node(hir_id).fn_sig().is_none() {
292+
return false;
293+
}
294+
// Do not suggest `impl Trait` when dealing with things like super-traits.
295+
err.span_suggestion_verbose(
296+
ty.span.until(trait_ref.span),
297+
"consider using an opaque type instead",
298+
"impl ",
299+
Applicability::MaybeIncorrect,
300+
);
301+
true
302+
}
303+
304+
pub fn suggest_path_on_bare_trait(
305+
tcx: TyCtxt<'_>,
306+
err: &mut Diag<'_>,
307+
node: hir::Node<'_>,
308+
) -> bool {
309+
let hir::Node::Expr(expr) = node else { return false };
310+
let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, path_segment)) = expr.kind else {
311+
return false;
312+
};
313+
let hir::TyKind::TraitObject([trait_ref, ..], _, trait_object_syntax) = ty.kind else {
314+
return false;
315+
};
316+
let trait_def_id = match trait_ref.trait_ref.path.res {
317+
Res::Def(DefKind::Trait, def_id) => def_id,
318+
_ => return false,
319+
};
320+
let trait_str = tcx.def_path_str(trait_def_id);
321+
if let TraitObjectSyntax::None = trait_object_syntax
322+
&& !expr.span.edition().at_least_rust_2021()
323+
{
324+
err.span_note(
325+
trait_ref.trait_ref.path.span,
326+
format!(
327+
"`{trait_str}` is the type for the trait in editions 2015 and 2018 and is \
328+
equivalent to writing `dyn {trait_str}`",
329+
),
330+
);
331+
}
332+
let segment = path_segment.ident;
333+
err.help(format!(
334+
"when writing `<{}{trait_str}>::{segment}` you are requiring `{trait_str}` be \"object \
335+
safe\", which it isn't",
336+
if let TraitObjectSyntax::None = trait_object_syntax { "" } else { "dyn " },
337+
));
338+
let impls_of = tcx.trait_impls_of(trait_def_id);
339+
let impls = if impls_of.blanket_impls().is_empty() {
340+
impls_of
341+
.non_blanket_impls()
342+
.values()
343+
.flatten()
344+
.filter(|def_id| {
345+
!matches!(tcx.type_of(*def_id).instantiate_identity().kind(), ty::Dynamic(..))
346+
})
347+
.collect::<Vec<_>>()
348+
} else {
349+
vec![]
350+
};
351+
let (only, msg, sugg, appl) = if let [only] = &impls[..] {
352+
// We know there's a single implementation for this trait, so we can be explicit on
353+
// the type they should have used.
354+
let ty = tcx.type_of(*only).instantiate_identity();
355+
(
356+
true,
357+
format!(
358+
"specify the specific `impl` for type `{ty}` to avoid requiring \"object safety\" \
359+
from `{trait_str}`",
360+
),
361+
with_no_trimmed_paths!(format!("{ty} as ")),
362+
Applicability::MachineApplicable,
363+
)
364+
} else {
365+
(
366+
false,
367+
format!(
368+
"you might have meant to access the associated function of a specific `impl` to \
369+
avoid requiring \"object safety\" from `{trait_str}`, either with some explicit \
370+
type...",
371+
),
372+
"/* Type */ as ".to_string(),
373+
Applicability::HasPlaceholders,
374+
)
375+
};
376+
// `<dyn Trait>::segment()` or `<Trait>::segment()` to `<Type as Trait>::segment()`
377+
let sp = ty.span.until(trait_ref.trait_ref.path.span);
378+
err.span_suggestion_verbose(sp, msg, sugg, appl);
379+
if !only {
380+
// `<dyn Trait>::segment()` or `<Trait>::segment()` to `Trait::segment()`
381+
err.multipart_suggestion_verbose(
382+
"...or rely on inference if the compiler has enough context to identify the desired \
383+
type on its own...",
384+
vec![
385+
(expr.span.until(trait_ref.trait_ref.path.span), String::new()),
386+
(
387+
path_segment
388+
.ident
389+
.span
390+
.shrink_to_lo()
391+
.with_lo(trait_ref.trait_ref.path.span.hi()),
392+
"::".to_string(),
393+
),
394+
],
395+
Applicability::MaybeIncorrect,
396+
);
397+
// `<dyn Trait>::segment()` or `<Trait>::segment()` to `<_ as Trait>::segment()`
398+
err.span_suggestion_verbose(
399+
ty.span.until(trait_ref.trait_ref.path.span),
400+
"...which is equivalent to",
401+
format!("_ as "),
402+
Applicability::MaybeIncorrect,
403+
);
404+
}
405+
true
406+
}

tests/ui/dyn-keyword/trait-missing-dyn-in-qualified-path.edition2018.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ note: `Default` is the type for the trait in editions 2015 and 2018 and is equiv
2525
|
2626
LL | let x: u32 = <Default>::default();
2727
| ^^^^^^^
28-
= help: when writing `<dyn Default>::default` you are requiring `Default` be "object safe", which it isn't
28+
= help: when writing `<Default>::default` you are requiring `Default` be "object safe", which it isn't
2929
help: you might have meant to access the associated function of a specific `impl` to avoid requiring "object safety" from `Default`, either with some explicit type...
3030
|
3131
LL | let x: u32 = </* Type */ as Default>::default();

0 commit comments

Comments
 (0)