Suggestion: add attributes to instruct Rust Analyzer how to import items

Rust's crates/modules also serve as namespaces, allowing to give simpler names to types/traits/functions because the namespace already has some of their context. Even if my struct Foo only makes sense in the context of the module bar, because I can refer to it as bar::Foo I don't have to name it BarFoo just to give readers of the code easy time understanding what it means.

The problem is that if I use Rust Analyzer (or any other similar tool that may exist in the future) I'm going to have a hard time sticking to referring to it as bar::Foo. When I try to use autocompletion, Rust Analyzer will prefer to add a use bar::Foo; instead of adding the namespace. With code actions it would at offer to use the full qualified name, but if bar is a submodule of a crate baz the options will either be to import Foo or to use baz::bar::Foo - it won't offer to add use baz::bar; and then use bar::Foo.

A crate's author is the best suited to decide how the items of the crates should be imported, so I suggest adding attributes that crates can use to tell Rust Analyzer how to handle imports.

#[import_assist(namespace = ...)]

When Rust Analyzer sees something like this:

pub mod bar {
    #[import_assist(namespace = crate::bar)]
    pub struct Foo;
}

It should know that when the user tries to autocomplete Foo, it should add a use declaration to import bar from whatever crate it's in and then compelte the expression to bar::Foo.

This should also work for multi-levels:

pub mod baz {
    pub mod bar {
        #[import_assist(namespace = crate::baz::bar)]
        pub struct Foo;

        #[import_assist(namespace = crate::baz)]
        pub struct Qux;
    }
}

With this, Rust Analyzer will complete Foo to bar::Foo and Qux to baz::bar::Qux. After adding the appropriate use declarations, of course..

#[import_assist(prelude = ...)]

It is a standard practice for Rust crates to offer a prelude module that users can star-import to get the typical items needed to use the crate. The annotations support this as well:

pub mod bar {
    #[import_assist(prelude = crate::prelude)]
    pub struct Foo;
}

pub mod prelude {
    pub use crate::bar::Foo;
}

With this, Rust Analyzer should know that it can do use <crate-name>::prelude::*; in order to import Foo.

This may be a bit dangerous for autocompletion-generated import, so maybe it should be limited to code actions?

Also, maybe it would be better to put the attribute on the prelude module instead of every item that goes into it.

#[import_assist(underscore)] (maybe this one needs a better name?)

Rust supports underscore imports where a trait gets brought into the scope so that its methods may be used without needlessly polluting the namespace with it. We should have support that as well:

#[import_assist(underscore)]
trait Foo { ... }

With this, when Rust Analyzer offers to import Foo because you used one of its methods, it'll import it as _.

#[import(rename = ...)]

This one is placed on the module itself:

#[import_assist(rename = "b")]`
pub mod bar {
    #[import_assist(namespace = crate::bar)]
    pub struct Foo;
}

When autocompleting Foo, or when running a code action to import it, Rust Analyzer will add an use <crate_name>::bar as b; and refer to Foo as b::Foo.

6 Likes

nit: these should probably all be under diagnostics:: since they have no need to be covered in a hypothetical spec.

4 Likes

I'd definitely like to have some sort of hint about preferred paths.

Declaring it per individual item, and repeating the exact wanted path, is potentially very verbose, so I'd want to be able to do it on whole modules. Perhaps something like this:

#[import_this] // recommends importing `foo` but not its items
pub mod foo {
    #[import_this] // overrides parent to recommend importing `Foo` directly
    pub struct Foo;

    // the module is preferred for importing these
    pub struct Builder;
    pub struct Error;
}

This would recommend to tools that imports should be of the form:

use library_name::foo::{self, Foo};

so that usage below this looks like Foo, foo::Builder , and foo::Error. With the attribute working this way, it should be quite simple to apply and concise.

We’d also want a way to disable taking these suggestions, or override them in the dependent crate, because some of them might violate the dependent crate’s code style; this can be just an IDE feature but I can also imagining wanting it per-dependency.

5 Likes

Maybe this can be configured in Cargo.toml's [package.metadata] table?

another thing that bugs me is when some item is visible in multiple locations and rust-analyzer seems to like picking the path I don't want...e.g.:

// crate foo
pub use foo_proc_macros::bar;

pub mod m {
    #[bar] // autocomplete here tries to `use foo_proc_macros::bar` instead of `use crate::bar`
    pub struct S;
}

pub use m::S;

// crate baz
type T = S; // autocomplete here tries to use the non-preferred path for `S`
1 Like

rust-analyzer has a reserved #[rust_analyzer::] tool namespace, so all of those can go directly under it with no lang approval.

3 Likes

I don't know if this is just rust analyzer specific though, e.g. I'd very much like for std::X::Y to be recommended over core::X::Y in error messages from rustc. Something like @kpreid's import_this suggestion has a major advantage of relatively minimizing the import path related hints; as proposed I can imagine many crates wanting an import_assist on every single public item, which hurts readability and usability alike.

3 Likes

I don't think this premise holds true in general. I think this is very much a stylistic preference in most cases, not an objective one.

3 Likes

+1. I find it useful to import items from crates that help me implement the core functionality of a module, but to add crate names for items from crates that are incidental to the core functionality. What counts as core functionality differs in each module.

With Rust Analyzer, I use the Qualify menu item for my first use, then trim the path to my desired import path, then use the Import menu item on the module name to add the use statement. Once this step has been done, the Qualify menu item will then recommend the shorter path on subsequent imports. An extra menu item to do the initial step in one flow would be adequate.

1 Like

While I agree that imports are significantly a matter of style (as I wrote above, “some of [these suggestions] might violate the dependent crate’s code style”), note that the status quo is not “RA obeys the dependent crate’s choice of style” but “RA does one unconfigurable thing”. It would be good for RA to offer options to help authors write their preferred import style, and for libraries to be able to recommend default import style which could be overridden.

5 Likes

Seconded, I think the framing of this as “recommendation” rather than “enforcement” is a good one.

3 Likes