Skip to content

Invalid dyn suggestions #88113

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

Open
m-ou-se opened this issue Aug 17, 2021 · 5 comments
Open

Invalid dyn suggestions #88113

m-ou-se opened this issue Aug 17, 2021 · 5 comments
Labels
A-edition-2021 Area: The 2021 edition A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` C-bug Category: This is a bug. D-invalid-suggestion Diagnostics: A structured suggestion resulting in incorrect code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@m-ou-se
Copy link
Member

m-ou-se commented Aug 17, 2021

In a small crater run for the 2021 edition migrations, a few problems turned up with the suggestions we give for missing dyn keywords:

@m-ou-se m-ou-se added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. C-bug Category: This is a bug. A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` D-invalid-suggestion Diagnostics: A structured suggestion resulting in incorrect code. A-edition-2021 Area: The 2021 edition labels Aug 17, 2021
@ehuss
Copy link
Contributor

ehuss commented Aug 19, 2021

There seems to be two issues here.

Duplicate diagnostic causes extra brackets.

use std::fmt;

#[derive(Debug)]
pub struct Foo;

impl fmt::Display for Foo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <fmt::Debug>::fmt(self, f)  // bare_trait_objects
    }
}

The above will trigger two warnings for bare_trait_objects. I thought the lint system had some deduplication support, so I'm unsure why that is happening (I also thought rustfix had some sort of overlap protection as well). When the fixes are applied, it results in <<dyn fmt::Debug>>::fmt(self, f) which is invalid syntax.

Transition from associated trait path to qualified path requires type parameters

trait Foo<T> {}

impl<T> dyn Foo<T> {
    fn hi(_x: T)  {}
}

fn main() {
    Foo::hi(123); // bare_trait_objects
}

Here, the suggestion wants to fix it to <dyn Foo>::hi(123);. However, that fails to compile because a qualified path requires the generic arguments to be specified, whereas the non-qualified path I believe uses inference?

@m-ou-se
Copy link
Member Author

m-ou-se commented Aug 30, 2021

The first case doesn't seem to be a case of doubly-applied suggestions:

let (sugg, app) = match self.tcx.sess.source_map().span_to_snippet(self_ty.span) {
Ok(s) if poly_trait_ref.trait_ref.path.is_global() => {
(format!("<dyn ({})>", s), Applicability::MachineApplicable)
}
Ok(s) => (format!("<dyn {}>", s), Applicability::MachineApplicable),
Err(_) => ("<dyn <type>>".to_string(), Applicability::HasPlaceholders),

Changing the <> there to «» results in:

 <«dyn fmt::Debug»>::fmt(self, f)

@m-ou-se
Copy link
Member Author

m-ou-se commented Aug 30, 2021

I've split the << .. >> issue into another issue: #88508

@camelid
Copy link
Member

camelid commented Sep 5, 2021

Transition from associated trait path to qualified path requires type parameters

trait Foo<T> {}

impl<T> dyn Foo<T> {
    fn hi(_x: T)  {}
}

fn main() {
    Foo::hi(123); // bare_trait_objects
}

Here, the suggestion wants to fix it to <dyn Foo>::hi(123);. However, that fails to compile because a qualified path requires the generic arguments to be specified, whereas the non-qualified path I believe uses inference?

I'll note that the error that is reported if you apply the <dyn Foo>::hi(123); suggestion further suggests that you add a generic parameter, so it's not great that rustc suggests invalid code, but at least rustc will then tell you how to fix it :)

   Compiling playground v0.0.1 (/playground)
error[E0107]: missing generics for trait `Foo`
 --> src/main.rs:8:10
  |
8 |     <dyn Foo>::hi(123); // bare_trait_objects
  |          ^^^ expected 1 generic argument
  |
note: trait defined here, with 1 generic parameter: `T`
 --> src/main.rs:1:7
  |
1 | trait Foo<T> {}
  |       ^^^ -
help: add missing generic argument
  |
8 |     <dyn Foo<T>>::hi(123); // bare_trait_objects
  |          ~~~~~~

For more information about this error, try `rustc --explain E0107`.
error: could not compile `playground` due to previous error

@estebank
Copy link
Contributor

estebank commented Jun 3, 2024

Triage:

warning: trait objects without an explicit `dyn` are deprecated
 --> f1000.rs:8:10
  |
8 |         <fmt::Debug>::fmt(self, f)  // bare_trait_objects
  |          ^^^^^^^^^^
  |
  = 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>
  = note: `#[warn(bare_trait_objects)]` on by default
help: if this is an object-safe trait, use `dyn`
  |
8 |         <dyn fmt::Debug>::fmt(self, f)  // bare_trait_objects
  |          +++

Consider the above solved, as the suggestion is now more targeted.


warning: trait objects without an explicit `dyn` are deprecated
 --> f1000.rs:8:5
  |
8 |     Foo::hi(123); // bare_trait_objects
  |     ^^^
  |
  = 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>
  = note: `#[warn(bare_trait_objects)]` on by default
help: if this is an object-safe trait, use `dyn`
  |
8 |     <dyn Foo>::hi(123); // bare_trait_objects
  |     ++++    +

Applying the suggestion results in

error[E0107]: missing generics for trait `Foo`
 --> f1000.rs:8:10
  |
8 |     <dyn Foo>::hi(123); // bare_trait_objects
  |          ^^^ expected 1 generic argument
  |
note: trait defined here, with 1 generic parameter: `T`
 --> f1000.rs:1:7
  |
1 | trait Foo<T> {}
  |       ^^^ -
help: add missing generic argument
  |
8 |     <dyn Foo<T>>::hi(123); // bare_trait_objects
  |             +++

Changing T with i32 results in successful compilation.

This is the same situation we originally had. I consider two "hops" of suggestions resulting in working code to be fairly good. Better if it is in a single "hop", but that's not always possible (otherwise we'd have to teach the parser to typeck, for example).

To fix that suggestion, we need to modify this bit of code

if is_global || needs_bracket {
sugg.push((
self_ty.span.shrink_to_hi(),
format!(
"{}{}",
if is_global { ")" } else { "" },
if needs_bracket { ">" } else { "" },
),
));
}

to check that poly_trait_ref.trait_ref.path has no args, and check whether the trait poly_trait_ref.trait_ref.trait_def_id() needs type params.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-edition-2021 Area: The 2021 edition A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` C-bug Category: This is a bug. D-invalid-suggestion Diagnostics: A structured suggestion resulting in incorrect code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants