suggest swapping a struct and a trait

fmt
This commit is contained in:
Takayuki Maeda 2022-06-07 02:33:23 +09:00
parent 395a09c3da
commit 9db03b9bc8
7 changed files with 107 additions and 2 deletions

View File

@ -496,6 +496,9 @@ struct DiagnosticMetadata<'ast> {
/// The current impl items (used to suggest). /// The current impl items (used to suggest).
current_impl_items: Option<&'ast [P<AssocItem>]>, current_impl_items: Option<&'ast [P<AssocItem>]>,
/// When processing impl trait
currently_processing_impl_trait: Option<(TraitRef, Ty)>,
} }
struct LateResolutionVisitor<'a, 'b, 'ast> { struct LateResolutionVisitor<'a, 'b, 'ast> {
@ -2066,18 +2069,22 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
fn with_optional_trait_ref<T>( fn with_optional_trait_ref<T>(
&mut self, &mut self,
opt_trait_ref: Option<&TraitRef>, opt_trait_ref: Option<&TraitRef>,
self_type: &'ast Ty,
f: impl FnOnce(&mut Self, Option<DefId>) -> T, f: impl FnOnce(&mut Self, Option<DefId>) -> T,
) -> T { ) -> T {
let mut new_val = None; let mut new_val = None;
let mut new_id = None; let mut new_id = None;
if let Some(trait_ref) = opt_trait_ref { if let Some(trait_ref) = opt_trait_ref {
let path: Vec<_> = Segment::from_path(&trait_ref.path); 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( let res = self.smart_resolve_path_fragment(
None, None,
&path, &path,
PathSource::Trait(AliasPossibility::No), PathSource::Trait(AliasPossibility::No),
Finalize::new(trait_ref.ref_id, trait_ref.path.span), 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() { if let Some(def_id) = res.base_res().opt_def_id() {
new_id = Some(def_id); new_id = Some(def_id);
new_val = Some((self.r.expect_module(def_id), trait_ref.clone())); 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_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| {
this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter(item_id), |this| { this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter(item_id), |this| {
// Resolve the trait reference, if necessary. // 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); let item_def_id = this.r.local_def_id(item_id);
// Register the trait definitions from here. // Register the trait definitions from here.

View File

@ -145,6 +145,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
let is_expected = &|res| source.is_expected(res); let is_expected = &|res| source.is_expected(res);
let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
debug!(?res, ?source);
// Make the base error. // Make the base error.
struct BaseError<'a> { struct BaseError<'a> {
msg: String, msg: String,
@ -248,6 +250,25 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
let code = source.error_code(res.is_some()); let code = source.error_code(res.is_some());
let mut err = let mut err =
self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code); self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code);
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, _)) = 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(
"consider swapping the struct and the trait",
vec![(trait_ref.path.span, self_ty_str), (self_ty.span, trait_ref_str)],
Applicability::MaybeIncorrect,
);
}
if let Some(sugg) = base_error.suggestion { if let Some(sugg) = base_error.suggestion {
err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect); err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect);

View File

@ -338,7 +338,7 @@ impl<'a> FileNameDisplay<'a> {
pub fn to_string_lossy(&self) -> Cow<'a, str> { pub fn to_string_lossy(&self) -> Cow<'a, str> {
match self.inner { match self.inner {
FileName::Real(ref inner) => inner.to_string_lossy(self.display_pref), FileName::Real(ref inner) => inner.to_string_lossy(self.display_pref),
_ => Cow::from(format!("{}", self)), _ => Cow::from(self.to_string()),
} }
} }
} }

View File

@ -0,0 +1,11 @@
// edition:2021
pub trait Trait<'a, T> {}
pub struct Struct<T> {}
impl<'a, T> Struct<T> for Trait<'a, T> {}
//~^ ERROR expected trait, found struct `Struct`
//~| ERROR trait objects must include the `dyn` keyword
fn main() {}

View File

@ -0,0 +1,27 @@
error[E0404]: expected trait, found struct `Struct`
--> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:7:13
|
LL | impl<'a, T> Struct<T> for Trait<'a, T> {}
| ^^^^^^^^^ not a trait
|
help: consider swapping the struct and the trait
|
LL | impl<'a, T> Trait<'a, T> for Struct<T> {}
| ~~~~~~~~~~~~ ~~~~~~~~~
error[E0782]: trait objects must include the `dyn` keyword
--> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:7: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 2 previous errors
Some errors have detailed explanations: E0404, E0782.
For more information about an error, try `rustc --explain E0404`.

View File

@ -0,0 +1,10 @@
pub trait Trait<'a, T> {}
pub struct Struct<T> {}
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!
fn main() {}

View File

@ -0,0 +1,29 @@
error[E0404]: expected trait, found struct `Struct`
--> $DIR/suggest-swapping-self-ty-and-trait.rs:5:13
|
LL | impl<'a, T> Struct<T> for Trait<'a, T> {}
| ^^^^^^^^^ not a trait
|
help: consider swapping the struct and the trait
|
LL | impl<'a, T> Trait<'a, T> for Struct<T> {}
| ~~~~~~~~~~~~ ~~~~~~~~~
warning: trait objects without an explicit `dyn` are deprecated
--> $DIR/suggest-swapping-self-ty-and-trait.rs:5: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 previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0404`.