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

Assist/Quickfix for converting method call to function call syntax #13247

Closed
jonas-schievink opened this issue Sep 16, 2022 · 11 comments · Fixed by #16100
Closed

Assist/Quickfix for converting method call to function call syntax #13247

jonas-schievink opened this issue Sep 16, 2022 · 11 comments · Fixed by #16100
Assignees
Labels
A-diagnostics diagnostics / error reporting C-feature Category: feature request S-actionable Someone could pick this issue up and work on it right now

Comments

@jonas-schievink
Copy link
Contributor

jonas-schievink commented Sep 16, 2022

It's not uncommon to accidentally write code like this:

fn main() {
    let b = Box::new(0);
    let ptr = b.into_raw();
}

But Box::into_raw is an associated function, not a method. It would be nice to detect this and offer an assist. A diagnostic + quickfix would also be possible (though maybe there's some use case in converting this, even if it is a method, in which case it should probably be a normal assist?)

@jonas-schievink jonas-schievink added S-actionable Someone could pick this issue up and work on it right now A-assists C-feature Category: feature request labels Sep 16, 2022
@Veykril
Copy link
Member

Veykril commented Sep 17, 2022

This is somewhat similar to #12497. So I think this issue should track the diagnostic side of things?

@Young-Flash
Copy link
Member

seems there has a diagnostic and quickfix from rustc side

error[E0599]: no method named `into_raw` found for struct `Box<{integer}>` in the current scope
  --> src/main.rs:17:17
   |
17 |     let ptr = b.into_raw();
   |               --^^^^^^^^--
   |               | |
   |               | this is an associated function, not a method
   |               help: use associated function syntax instead: `Box::<_>::into_raw(b)`
   |
   = note: found the following associated functions; to be used as methods, functions must have a `self` parameter
   = note: the candidate is defined in an impl for the type `Box<T, A>`

@Veykril Veykril added A-diagnostics diagnostics / error reporting and removed A-assists labels Nov 10, 2023
@Young-Flash
Copy link
Member

after apply the quickfix, let ptr = b.into_raw(); will be reafactored to let ptr = Box::<_>::into_raw(b);, which can pass the compilation

I think there isn't necessary to make a same diagnostic in rust-analyzer side, maybe this issue can be close? cc @jonas-schievink, @Veykril

@Veykril
Copy link
Member

Veykril commented Nov 17, 2023

No, ultimately we want to have every rustc diagnostic in r-a as well, as relying on cargo check is slow and cumbersome.

@Young-Flash
Copy link
Member

Young-Flash commented Nov 17, 2023

Is there any feature else that r-a rely on cargo check? if so, are we going to impl them in r-a from scratch?

Same feature in r-a and rustc have their respective implementation, seems a maintenance pressure. It would be nice if these feature could be extract into a crate and be use in different project (there maybe some historical reasons which I don't know quite well, like different AST, parser or something else, causing this phenomenon)

@Veykril
Copy link
Member

Veykril commented Nov 17, 2023

In an ideal world we could plug rustc's diagnostics into r-a / share them with r-a, but that is unlikely to ever be feasible, so we'll try to implement the most useful ones ourselves at least.

In case of this issue, note that we already have an assist for this https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/qualify_method_call.rs, so diagnosing this error we could re-use the assist functionality for the diagnostic quickfix (as the assist itself does not trigger on these incorrect method calls).

@Young-Flash
Copy link
Member

In case of this issue, note that we already have an assist for this https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/qualify_method_call.rs, so diagnosing this error we could re-use the assist functionality for the diagnostic quickfix (as the assist itself does not trigger on these incorrect method calls).

qualify_method_call assist can only apply over valid method call, when we deal with a unresolved method call, apply resolve_method_call over it will get None

let resolved_call = ctx.sema.resolve_method_call(&call)?;

@Veykril
Copy link
Member

Veykril commented Nov 23, 2023

Oh ye sorry, I meant adjusting the common functionality to be able to figure this invalid case out as well (via a toggle). Then the assist should stick to keep working only on valid ones, while the diagnostic will expose a quickfix for the invalid case.

@Young-Flash
Copy link
Member

Young-Flash commented Nov 28, 2023

Sorry, I don't quite understand, via a toggle? Could you be more specific?

  • Since we can't apply resolve_method_call over an unresolved method, my idea is: constructing a potenial valid function call according this unresolved method, for example: a.hello() -> A::hello(), which can be achieved with the current internal api, and replace the unresolved method with this potenial valid function call. It will not involve qualify_method_call assist functionality. But we can't ensure the function call we constructed is vaild, cause it isn't part of the current source file so we stiil can't apply resolve_method_call over it to verify whether it is vaild or not.
struct A {}
impl A {
    fn hello() {}
}
fn main() {
    let a = A{};
    a.hello$0();
}
  • Another approach is: find out all the method or function(and trait method or function) for the method call receiver type, check if there is a function with the same name as the unresolved method. If there is, replace the unresolved method with it. But I can't find some api to get method or function for the method call receiver type. Any suggestion?

@Veykril
Copy link
Member

Veykril commented Nov 28, 2023

Right, its not the simple.

I see two options here:

  • Do what you basically proposed, search through the available functions (of impls) for the receiver and check if the called method name exists as a function with the receiver as the first arg in the quickfix for the diagnostic
  • Or, which I think would probably be nicer as we will be able to continue typechecking properly then, recover in method resolution when doing type inference by doing basically the same as point 1 but then record that as the method call in the inference result while still emitting the diagnostic. Then we just need to disable the assist in this case somehow and give the diagnostic the quickfix (which is basically just the assist), the shuffling is mainly for UX purposes.

Regarding the latter option, you'd need to "retry" resolution in the diagnostic branch here

None => {
// no field found,
let method_with_same_name_exists = {
self.get_traits_in_scope();
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
method_resolution::lookup_method(
self.db,
&canonicalized_receiver.value,
self.table.trait_env.clone(),
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
VisibleFromModule::Filter(self.resolver.module()),
name,
)
.is_some()
};
self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField {
expr: tgt_expr,
receiver: receiver_ty,
name: name.clone(),
method_with_same_name_exists,
});
self.err_ty()
}
, then call method_resolution::iterate_method_candidates with method_resolution::LookupMode::Path similar to how its done in
let res = method_resolution::iterate_method_candidates(
&canonical_ty.value,
self.db,
self.table.trait_env.clone(),
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
VisibleFromModule::Filter(self.resolver.module()),
Some(name),
method_resolution::LookupMode::Path,
|_ty, item, visible| {
if visible {
Some((item, true))
} else {
if not_visible.is_none() {
not_visible = Some((item, false));
}
None
}
},
);
with the receiver type as the first arg to it. Effectively we are retrying to resolve the method call as a qualified path call as you described that way. Then you can just treat that result (if it succeeds) as a successful method resolution result and proceed accordingly again (+ still emitting the unresolved method call diagnostic).

@Young-Flash
Copy link
Member

@rustbot claim

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics diagnostics / error reporting C-feature Category: feature request S-actionable Someone could pick this issue up and work on it right now
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants