Skip to content

Commit 9db03b9

Browse files
committed
suggest swapping a struct and a trait
fmt
1 parent 395a09c commit 9db03b9

7 files changed

+107
-2
lines changed

compiler/rustc_resolve/src/late.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,9 @@ struct DiagnosticMetadata<'ast> {
496496

497497
/// The current impl items (used to suggest).
498498
current_impl_items: Option<&'ast [P<AssocItem>]>,
499+
500+
/// When processing impl trait
501+
currently_processing_impl_trait: Option<(TraitRef, Ty)>,
499502
}
500503

501504
struct LateResolutionVisitor<'a, 'b, 'ast> {
@@ -2066,18 +2069,22 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
20662069
fn with_optional_trait_ref<T>(
20672070
&mut self,
20682071
opt_trait_ref: Option<&TraitRef>,
2072+
self_type: &'ast Ty,
20692073
f: impl FnOnce(&mut Self, Option<DefId>) -> T,
20702074
) -> T {
20712075
let mut new_val = None;
20722076
let mut new_id = None;
20732077
if let Some(trait_ref) = opt_trait_ref {
20742078
let path: Vec<_> = Segment::from_path(&trait_ref.path);
2079+
self.diagnostic_metadata.currently_processing_impl_trait =
2080+
Some((trait_ref.clone(), self_type.clone()));
20752081
let res = self.smart_resolve_path_fragment(
20762082
None,
20772083
&path,
20782084
PathSource::Trait(AliasPossibility::No),
20792085
Finalize::new(trait_ref.ref_id, trait_ref.path.span),
20802086
);
2087+
self.diagnostic_metadata.currently_processing_impl_trait = None;
20812088
if let Some(def_id) = res.base_res().opt_def_id() {
20822089
new_id = Some(def_id);
20832090
new_val = Some((self.r.expect_module(def_id), trait_ref.clone()));
@@ -2118,7 +2125,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
21182125
this.with_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| {
21192126
this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter(item_id), |this| {
21202127
// Resolve the trait reference, if necessary.
2121-
this.with_optional_trait_ref(opt_trait_reference.as_ref(), |this, trait_id| {
2128+
this.with_optional_trait_ref(opt_trait_reference.as_ref(), self_type, |this, trait_id| {
21222129
let item_def_id = this.r.local_def_id(item_id);
21232130

21242131
// Register the trait definitions from here.

compiler/rustc_resolve/src/late/diagnostics.rs

+21
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
145145
let is_expected = &|res| source.is_expected(res);
146146
let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
147147

148+
debug!(?res, ?source);
149+
148150
// Make the base error.
149151
struct BaseError<'a> {
150152
msg: String,
@@ -248,6 +250,25 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
248250
let code = source.error_code(res.is_some());
249251
let mut err =
250252
self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code);
253+
if let Some((trait_ref, self_ty)) =
254+
self.diagnostic_metadata.currently_processing_impl_trait.clone()
255+
&& let TyKind::Path(_, self_ty_path) = &self_ty.kind
256+
&& let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None)
257+
&& let ModuleKind::Def(DefKind::Trait, ..) = module.kind
258+
&& trait_ref.path.span == span
259+
&& let PathSource::Trait(_) = source
260+
&& let Some(Res::Def(DefKind::Struct, _)) = res
261+
&& let Ok(self_ty_str) =
262+
self.r.session.source_map().span_to_snippet(self_ty.span)
263+
&& let Ok(trait_ref_str) =
264+
self.r.session.source_map().span_to_snippet(trait_ref.path.span)
265+
{
266+
err.multipart_suggestion(
267+
"consider swapping the struct and the trait",
268+
vec![(trait_ref.path.span, self_ty_str), (self_ty.span, trait_ref_str)],
269+
Applicability::MaybeIncorrect,
270+
);
271+
}
251272

252273
if let Some(sugg) = base_error.suggestion {
253274
err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect);

compiler/rustc_span/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ impl<'a> FileNameDisplay<'a> {
338338
pub fn to_string_lossy(&self) -> Cow<'a, str> {
339339
match self.inner {
340340
FileName::Real(ref inner) => inner.to_string_lossy(self.display_pref),
341-
_ => Cow::from(format!("{}", self)),
341+
_ => Cow::from(self.to_string()),
342342
}
343343
}
344344
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// edition:2021
2+
3+
pub trait Trait<'a, T> {}
4+
5+
pub struct Struct<T> {}
6+
7+
impl<'a, T> Struct<T> for Trait<'a, T> {}
8+
//~^ ERROR expected trait, found struct `Struct`
9+
//~| ERROR trait objects must include the `dyn` keyword
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0404]: expected trait, found struct `Struct`
2+
--> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:7:13
3+
|
4+
LL | impl<'a, T> Struct<T> for Trait<'a, T> {}
5+
| ^^^^^^^^^ not a trait
6+
|
7+
help: consider swapping the struct and the trait
8+
|
9+
LL | impl<'a, T> Trait<'a, T> for Struct<T> {}
10+
| ~~~~~~~~~~~~ ~~~~~~~~~
11+
12+
error[E0782]: trait objects must include the `dyn` keyword
13+
--> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:7:27
14+
|
15+
LL | impl<'a, T> Struct<T> for Trait<'a, T> {}
16+
| ^^^^^^^^^^^^
17+
|
18+
help: add `dyn` keyword before this trait
19+
|
20+
LL - impl<'a, T> Struct<T> for Trait<'a, T> {}
21+
LL + impl<'a, T> Struct<T> for dyn Trait<'a, T> {}
22+
|
23+
24+
error: aborting due to 2 previous errors
25+
26+
Some errors have detailed explanations: E0404, E0782.
27+
For more information about an error, try `rustc --explain E0404`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
pub trait Trait<'a, T> {}
2+
3+
pub struct Struct<T> {}
4+
5+
impl<'a, T> Struct<T> for Trait<'a, T> {}
6+
//~^ ERROR expected trait, found struct `Struct`
7+
//~| WARNING trait objects without an explicit `dyn` are deprecated
8+
//~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
9+
10+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error[E0404]: expected trait, found struct `Struct`
2+
--> $DIR/suggest-swapping-self-ty-and-trait.rs:5:13
3+
|
4+
LL | impl<'a, T> Struct<T> for Trait<'a, T> {}
5+
| ^^^^^^^^^ not a trait
6+
|
7+
help: consider swapping the struct and the trait
8+
|
9+
LL | impl<'a, T> Trait<'a, T> for Struct<T> {}
10+
| ~~~~~~~~~~~~ ~~~~~~~~~
11+
12+
warning: trait objects without an explicit `dyn` are deprecated
13+
--> $DIR/suggest-swapping-self-ty-and-trait.rs:5:27
14+
|
15+
LL | impl<'a, T> Struct<T> for Trait<'a, T> {}
16+
| ^^^^^^^^^^^^
17+
|
18+
= note: `#[warn(bare_trait_objects)]` on by default
19+
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
20+
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
21+
help: use `dyn`
22+
|
23+
LL - impl<'a, T> Struct<T> for Trait<'a, T> {}
24+
LL + impl<'a, T> Struct<T> for dyn Trait<'a, T> {}
25+
|
26+
27+
error: aborting due to previous error; 1 warning emitted
28+
29+
For more information about this error, try `rustc --explain E0404`.

0 commit comments

Comments
 (0)