diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 73d6566e3cd9..688ba89c689c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -496,6 +496,9 @@ struct DiagnosticMetadata<'ast> { /// The current impl items (used to suggest). current_impl_items: Option<&'ast [P]>, + + /// When processing impl trait + currently_processing_impl_trait: Option<(TraitRef, Ty)>, } struct LateResolutionVisitor<'a, 'b, 'ast> { @@ -2066,18 +2069,22 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn with_optional_trait_ref( &mut self, opt_trait_ref: Option<&TraitRef>, + self_type: &'ast Ty, f: impl FnOnce(&mut Self, Option) -> 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())); @@ -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. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index a6a04ac9ea6f..6f9c6592ad7c 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -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, @@ -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); } @@ -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, + 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], diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index adf5a7440480..456cf6dbe270 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -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()), } } } diff --git a/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.rs b/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.rs new file mode 100644 index 000000000000..03c7ed347bdd --- /dev/null +++ b/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.rs @@ -0,0 +1,22 @@ +// edition:2021 + +pub trait Trait<'a, T> {} + +pub struct Struct; +pub enum Enum {} + +pub union Union { + f1: usize, +} + +impl<'a, T> Struct for Trait<'a, T> {} +//~^ ERROR expected trait, found struct `Struct` +//~| ERROR trait objects must include the `dyn` keyword + +impl<'a, T> Enum for Trait<'a, T> {} +//~^ ERROR expected trait, found enum `Enum` + +impl<'a, T> Union for Trait<'a, T> {} +//~^ ERROR expected trait, found union `Union` + +fn main() {} diff --git a/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.stderr b/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.stderr new file mode 100644 index 000000000000..dd6951e04741 --- /dev/null +++ b/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.stderr @@ -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 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 {} + | ~~~~~~~~~~~~ ~~~~~~~~~ + +error[E0404]: expected trait, found enum `Enum` + --> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:16:13 + | +LL | impl<'a, T> Enum 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 {} + | ~~~~~~~~~~~~ ~~~~~~~ + +error[E0404]: expected trait, found union `Union` + --> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:19:13 + | +LL | impl<'a, T> Union 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 {} + | ~~~~~~~~~~~~ ~~~~~~~~ + +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 for Trait<'a, T> {} + | ^^^^^^^^^^^^ + | +help: add `dyn` keyword before this trait + | +LL - impl<'a, T> Struct for Trait<'a, T> {} +LL + impl<'a, T> Struct 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`. diff --git a/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait.rs b/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait.rs new file mode 100644 index 000000000000..947bc2c79654 --- /dev/null +++ b/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait.rs @@ -0,0 +1,21 @@ +pub trait Trait<'a, T> {} + +pub struct Struct; +pub enum Enum {} + +pub union Union { + f1: usize, +} + +impl<'a, T> Struct 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 for Trait<'a, T> {} +//~^ ERROR expected trait, found enum `Enum` + +impl<'a, T> Union for Trait<'a, T> {} +//~^ ERROR expected trait, found union `Union` + +fn main() {} diff --git a/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait.stderr b/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait.stderr new file mode 100644 index 000000000000..86ab8474c429 --- /dev/null +++ b/src/test/ui/suggestions/suggest-swapping-self-ty-and-trait.stderr @@ -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 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 {} + | ~~~~~~~~~~~~ ~~~~~~~~~ + +error[E0404]: expected trait, found enum `Enum` + --> $DIR/suggest-swapping-self-ty-and-trait.rs:15:13 + | +LL | impl<'a, T> Enum 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 {} + | ~~~~~~~~~~~~ ~~~~~~~ + +error[E0404]: expected trait, found union `Union` + --> $DIR/suggest-swapping-self-ty-and-trait.rs:18:13 + | +LL | impl<'a, T> Union 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 {} + | ~~~~~~~~~~~~ ~~~~~~~~ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/suggest-swapping-self-ty-and-trait.rs:10:27 + | +LL | impl<'a, T> Struct 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 +help: use `dyn` + | +LL - impl<'a, T> Struct for Trait<'a, T> {} +LL + impl<'a, T> Struct 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`.