Skip to content

Avoid expanding arguments in function completion depending on bounds #19635

@bbb651

Description

@bbb651

Currently when completing function items, whether arguments are expanded is controlled globally by rust-analyzer.completion.callable.snippets. Instead it should be based on the excepted type in the context the completion:

  • If the return type of the expanded function could be applicable and the function item cannot, i.e. there exists a combination of generics given to the function that will satisfy the bounds of the function it is used in, the expanded form is used
  • Otherwise, If the function item itself could be applicable the unexpanded form is used
  • Otherwise or if the bounds cannot be solved, it should fallback to the existing setting

The main motivation is adding systems in bevy (i.e. App::add_systems),

use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, set|)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d::default());
}
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup([commands]);)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d::default());
}

(where [] represents the selection)

I would expect it to complete:

use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup|)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d::default());
}

This case is simpler because the return type is () so we know it's never applicable.

As another example, completing arguments of std::convert::identity and std::hint::black_box it should be based on rust-analyzer.completion.callable.snippets, as both forms are applicable to the trait bounds.

Alternative: Attribute hints

If solving bounds isn't feasible in terms of complexity and performance, attributes that hint the intent could be used instead.
There are a couple of ways this could be done (terribly bikesheding syntax):

  • #[rust_analyzer::completion::callable::snippets(None)] to apply the rust-analyzer.completion.callable.snippets setting to an individual block
  • #[rust_analyzer::completion::callable::arguments("functions")] to hint the function commonly expects functions as arguments
  • #[rust_analyzer::completion::callable::expand(false)] to hint the function itself is commonly used as an argument

This won't work properly for higher order functions but it would still provide a much better experience for the bevy use case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-featureCategory: feature request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions