Skip to content

Suggest to swap a struct and a trait in trait impls #97812

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,9 @@ struct DiagnosticMetadata<'ast> {

/// The current impl items (used to suggest).
current_impl_items: Option<&'ast [P<AssocItem>]>,

/// When processing impl trait
currently_processing_impl_trait: Option<(TraitRef, Ty)>,
}

struct LateResolutionVisitor<'a, 'b, 'ast> {
Expand Down Expand Up @@ -2066,18 +2069,22 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
fn with_optional_trait_ref<T>(
&mut self,
opt_trait_ref: Option<&TraitRef>,
self_type: &'ast Ty,
f: impl FnOnce(&mut Self, Option<DefId>) -> T,
) -> T {
let mut new_val = None;
let mut new_id = None;
if let Some(trait_ref) = opt_trait_ref {
let path: Vec<_> = Segment::from_path(&trait_ref.path);
self.diagnostic_metadata.currently_processing_impl_trait =
Some((trait_ref.clone(), self_type.clone()));
let res = self.smart_resolve_path_fragment(
None,
&path,
PathSource::Trait(AliasPossibility::No),
Finalize::new(trait_ref.ref_id, trait_ref.path.span),
);
self.diagnostic_metadata.currently_processing_impl_trait = None;
if let Some(def_id) = res.base_res().opt_def_id() {
new_id = Some(def_id);
new_val = Some((self.r.expect_module(def_id), trait_ref.clone()));
Expand Down Expand Up @@ -2118,7 +2125,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
this.with_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| {
this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter(item_id), |this| {
// Resolve the trait reference, if necessary.
this.with_optional_trait_ref(opt_trait_reference.as_ref(), |this, trait_id| {
this.with_optional_trait_ref(opt_trait_reference.as_ref(), self_type, |this, trait_id| {
let item_def_id = this.r.local_def_id(item_id);

// Register the trait definitions from here.
Expand Down
33 changes: 33 additions & 0 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
let is_expected = &|res| source.is_expected(res);
let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));

debug!(?res, ?source);

// Make the base error.
struct BaseError<'a> {
msg: String,
Expand Down Expand Up @@ -249,6 +251,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
let mut err =
self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code);

self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);

if let Some(sugg) = base_error.suggestion {
err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect);
}
Expand Down Expand Up @@ -683,6 +687,35 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
}
}

fn suggest_swapping_misplaced_self_ty_and_trait(
&mut self,
err: &mut Diagnostic,
source: PathSource<'_>,
res: Option<Res>,
span: Span,
) {
if let Some((trait_ref, self_ty)) =
self.diagnostic_metadata.currently_processing_impl_trait.clone()
&& let TyKind::Path(_, self_ty_path) = &self_ty.kind
&& let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None)
&& let ModuleKind::Def(DefKind::Trait, ..) = module.kind
&& trait_ref.path.span == span
&& let PathSource::Trait(_) = source
&& let Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)) = res
&& let Ok(self_ty_str) =
self.r.session.source_map().span_to_snippet(self_ty.span)
&& let Ok(trait_ref_str) =
self.r.session.source_map().span_to_snippet(trait_ref.path.span)
{
err.multipart_suggestion(
"`impl` items mention the trait being implemented first and the type it is being implemented for second",
vec![(trait_ref.path.span, self_ty_str), (self_ty.span, trait_ref_str)],
Applicability::MaybeIncorrect,
);
}
}

fn get_single_associated_item(
&mut self,
path: &[Segment],
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_span/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ impl<'a> FileNameDisplay<'a> {
pub fn to_string_lossy(&self) -> Cow<'a, str> {
match self.inner {
FileName::Real(ref inner) => inner.to_string_lossy(self.display_pref),
_ => Cow::from(format!("{}", self)),
_ => Cow::from(self.to_string()),
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// edition:2021

pub trait Trait<'a, T> {}

pub struct Struct<T>;
pub enum Enum<T> {}

pub union Union<T> {
f1: usize,
}

impl<'a, T> Struct<T> for Trait<'a, T> {}
//~^ ERROR expected trait, found struct `Struct`
//~| ERROR trait objects must include the `dyn` keyword

impl<'a, T> Enum<T> for Trait<'a, T> {}
//~^ ERROR expected trait, found enum `Enum`

impl<'a, T> Union<T> for Trait<'a, T> {}
//~^ ERROR expected trait, found union `Union`

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
error[E0404]: expected trait, found struct `Struct`
--> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:12:13
|
LL | impl<'a, T> Struct<T> for Trait<'a, T> {}
| ^^^^^^^^^ not a trait
|
help: `impl` items mention the trait being implemented first and the type it is being implemented for second
|
LL | impl<'a, T> Trait<'a, T> for Struct<T> {}
| ~~~~~~~~~~~~ ~~~~~~~~~

error[E0404]: expected trait, found enum `Enum`
--> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:16:13
|
LL | impl<'a, T> Enum<T> for Trait<'a, T> {}
| ^^^^^^^ not a trait
|
help: `impl` items mention the trait being implemented first and the type it is being implemented for second
|
LL | impl<'a, T> Trait<'a, T> for Enum<T> {}
| ~~~~~~~~~~~~ ~~~~~~~

error[E0404]: expected trait, found union `Union`
--> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:19:13
|
LL | impl<'a, T> Union<T> for Trait<'a, T> {}
| ^^^^^^^^ not a trait
|
help: `impl` items mention the trait being implemented first and the type it is being implemented for second
|
LL | impl<'a, T> Trait<'a, T> for Union<T> {}
| ~~~~~~~~~~~~ ~~~~~~~~

error[E0782]: trait objects must include the `dyn` keyword
--> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:12:27
|
LL | impl<'a, T> Struct<T> for Trait<'a, T> {}
| ^^^^^^^^^^^^
|
help: add `dyn` keyword before this trait
|
LL - impl<'a, T> Struct<T> for Trait<'a, T> {}
LL + impl<'a, T> Struct<T> for dyn Trait<'a, T> {}
|

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0404, E0782.
For more information about an error, try `rustc --explain E0404`.
21 changes: 21 additions & 0 deletions src/test/ui/suggestions/suggest-swapping-self-ty-and-trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
pub trait Trait<'a, T> {}

pub struct Struct<T>;
pub enum Enum<T> {}

pub union Union<T> {
f1: usize,
}

impl<'a, T> Struct<T> for Trait<'a, T> {}
//~^ ERROR expected trait, found struct `Struct`
//~| WARNING trait objects without an explicit `dyn` are deprecated
//~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!

impl<'a, T> Enum<T> for Trait<'a, T> {}
//~^ ERROR expected trait, found enum `Enum`

impl<'a, T> Union<T> for Trait<'a, T> {}
//~^ ERROR expected trait, found union `Union`

fn main() {}
51 changes: 51 additions & 0 deletions src/test/ui/suggestions/suggest-swapping-self-ty-and-trait.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
error[E0404]: expected trait, found struct `Struct`
--> $DIR/suggest-swapping-self-ty-and-trait.rs:10:13
|
LL | impl<'a, T> Struct<T> for Trait<'a, T> {}
| ^^^^^^^^^ not a trait
|
help: `impl` items mention the trait being implemented first and the type it is being implemented for second
|
LL | impl<'a, T> Trait<'a, T> for Struct<T> {}
| ~~~~~~~~~~~~ ~~~~~~~~~

error[E0404]: expected trait, found enum `Enum`
--> $DIR/suggest-swapping-self-ty-and-trait.rs:15:13
|
LL | impl<'a, T> Enum<T> for Trait<'a, T> {}
| ^^^^^^^ not a trait
|
help: `impl` items mention the trait being implemented first and the type it is being implemented for second
|
LL | impl<'a, T> Trait<'a, T> for Enum<T> {}
| ~~~~~~~~~~~~ ~~~~~~~

error[E0404]: expected trait, found union `Union`
--> $DIR/suggest-swapping-self-ty-and-trait.rs:18:13
|
LL | impl<'a, T> Union<T> for Trait<'a, T> {}
| ^^^^^^^^ not a trait
|
help: `impl` items mention the trait being implemented first and the type it is being implemented for second
|
LL | impl<'a, T> Trait<'a, T> for Union<T> {}
| ~~~~~~~~~~~~ ~~~~~~~~

warning: trait objects without an explicit `dyn` are deprecated
--> $DIR/suggest-swapping-self-ty-and-trait.rs:10:27
|
LL | impl<'a, T> Struct<T> for Trait<'a, T> {}
| ^^^^^^^^^^^^
|
= note: `#[warn(bare_trait_objects)]` on by default
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
help: use `dyn`
|
LL - impl<'a, T> Struct<T> for Trait<'a, T> {}
LL + impl<'a, T> Struct<T> for dyn Trait<'a, T> {}
|

error: aborting due to 3 previous errors; 1 warning emitted

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