Skip to content
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

Improvements to "Find Usages" #9477

Open
3 tasks
afetisov opened this issue Oct 6, 2022 · 0 comments
Open
3 tasks

Improvements to "Find Usages" #9477

afetisov opened this issue Oct 6, 2022 · 0 comments

Comments

@afetisov
Copy link
Contributor

afetisov commented Oct 6, 2022

Environment

  • IntelliJ Rust plugin version: 0.4.181.4920-222-nightly
  • Rust toolchain version: 1.64.0 (a55dd71d5 2022-09-19) x86_64-unknown-linux-gnu
  • IDE name and version: CLion 2022.2.3 (CL-222.4167.35)
  • Operating system: Linux 5.4.0-126-generic
  • Macro expansion: enabled
  • Additional experimental features: org.rust.cargo.features.settings.gutter, org.rust.cargo.emulate.terminal, org.rust.cargo.evaluate.build.scripts, org.rust.macros.proc

Suggestion

Find Usages is currently very limited on large projects, since many natural categories of usages are lacking. I propose the following additions:

  • For traits, add usage categories

    • Trait objects (dyn Trait usages)
    • Implementations (impl Trait for Foo)
    • Trait bounds (foo<T1, T2: Trait>(x: impl Trait) where T1: Trait, as well as bounds on generics parameters and associated types).
    • Opaque types (foo(_: impl Trait) -> impl Trait, as well as impl Trait in type aliases and nested types).
    • Supertrait (trait T: Trait).
  • For structs, there are ostensibly many categories (although some don't make sense, what the hell is "dot expr"?). However, in practice the categorization is very incoherent, and the same type of expression appear to be dumped in different categories ostensibly at random. Also, almost all usages are dumped in the catch-all "type reference" category. For example, searching for std::option::Option in all of my dependencies gives me the following category counts:

    • Unclassified - 7
    • argument - 20
    • dot expr - 2
    • expr - 3
    • function call - 17
    • impl - 82
    • type reference - 10 235 (sic!)
    • use - 22
      This is plain useless. The only categories which seem to work as indended are impl and use. type_reference is just a dump which can't meaningfully be analyzed, and the others contain expressions like Option::from in all of them.

    I propose the following categories:

    • impl - as above, impl Trait for Type.
    • use - as above, all use foo::Type and pub use bar::Type statements.
    • inherent method calls - all calls of inherent methods on Type.
    • trait method calls - all calls of trait methods on Trait. We separate it from inherent methods, since the set of inherent methods is closed (only in the declaring crate) and usually small, while anyone can add trait methods.
    • usage in return type - foo() -> Struct
    • usage in argument type - foo(_: Struct). We separate return and argument position, since they are quite different from the PoV of an API consumer. One is the way to use a type, while the other is a way to construct it. Include type in nested positions (foo(_: Box<Struct>) -> Vec<Struct>), because they are part of the same use-vs-create paradigm.
    • field type - struct Foo { a: Struct }, struct Foo(Struct), and same with enums and unions. As above, we include type in nested position.
    • associated type - impl Trait for Foo { type T = Struct; }, and also default associated types (unstable).
    • type aliases - type Foo = Struct. This includes inherent type aliases on types (unstable, impl Foo { type Bar = Struct; }). This is different from associated types, even though the syntax is similar. Associated types are more widely used, and they have semantic meaning (the return value of a type-level function corresponding to a trait), while type aliases are just a convenient name for a more complex type, and are semantically equivalent to that type.
    • trait bounds - .. where Struct: Trait. This is an uncommon usage, since the trait bounds are usually placed directly on generic parameters (e.g. T: Trait rather than Option<T>: Trait), but it is allowed by the language and is occasionally useful. For example, look at the trait bounds in futures crate. It would be more widely used if chalk was stable, since the current trait resolution hits critical bugs for more complex types in where-clauses.
    • qualified paths - Struct::foo. Note that it's separate from methods, which don't explicitly mention the type.
    • type ascription - let x: Struct = ...;. Note that type ascription expressions were removed from the language, so only let-bindings need to be searched.
    • expression generic parameters - foo::<Struct>().
    • literal expressions - Struct { a: 3, b: 4 }, Enum::Variant.
    • All other usages. This will likely be small.
  • It should be possible to select the required usage categories from the "Find Usages" dialogue. If I'm only interested in trait impls, there is no point in dumping all other usages on me. This also saves time and memory when dealing with huge number of usages (like for std types).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant