mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2026-03-06 19:39:12 +00:00
Merge pull request #21752 from ChayimFriedman2/question-mark-conversion
feat: When going to def on `?` on `Result` that goes through `From`, go to the `From` impl
This commit is contained in:
commit
e8ee51ab9e
@ -6158,6 +6158,13 @@ impl<'db> Type<'db> {
|
||||
Some(adt.into())
|
||||
}
|
||||
|
||||
/// Holes in the args can come from lifetime/const params.
|
||||
pub fn as_adt_with_args(&self) -> Option<(Adt, Vec<Option<Type<'db>>>)> {
|
||||
let (adt, args) = self.ty.as_adt()?;
|
||||
let args = args.iter().map(|arg| Some(self.derived(arg.ty()?))).collect();
|
||||
Some((adt.into(), args))
|
||||
}
|
||||
|
||||
pub fn as_builtin(&self) -> Option<BuiltinType> {
|
||||
self.ty.as_builtin().map(|inner| BuiltinType { inner })
|
||||
}
|
||||
|
||||
@ -1819,6 +1819,28 @@ impl<'db> SemanticsImpl<'db> {
|
||||
self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
|
||||
}
|
||||
|
||||
/// The type that the associated `try` block, closure or function expects.
|
||||
pub fn try_expr_returned_type(&self, try_expr: &ast::TryExpr) -> Option<Type<'db>> {
|
||||
self.ancestors_with_macros(try_expr.syntax().clone()).find_map(|parent| {
|
||||
if let Some(try_block) = ast::BlockExpr::cast(parent.clone())
|
||||
&& try_block.try_block_modifier().is_some()
|
||||
{
|
||||
Some(self.type_of_expr(&try_block.into())?.original)
|
||||
} else if let Some(closure) = ast::ClosureExpr::cast(parent.clone()) {
|
||||
Some(
|
||||
self.type_of_expr(&closure.into())?
|
||||
.original
|
||||
.as_callable(self.db)?
|
||||
.return_type(),
|
||||
)
|
||||
} else if let Some(function) = ast::Fn::cast(parent) {
|
||||
Some(self.to_def(&function)?.ret_type(self.db))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// This does not resolve the method call to the correct trait impl!
|
||||
// We should probably fix that.
|
||||
pub fn resolve_method_call_as_callable(
|
||||
|
||||
@ -95,6 +95,13 @@ pub(crate) fn goto_definition(
|
||||
continue;
|
||||
}
|
||||
|
||||
let parent = token.value.parent()?;
|
||||
|
||||
if let Some(question_mark_conversion) = goto_question_mark_conversions(sema, &parent) {
|
||||
navs.extend(def_to_nav(sema, question_mark_conversion.into()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(token) = ast::String::cast(token.value.clone())
|
||||
&& let Some(original_token) = ast::String::cast(original_token.clone())
|
||||
&& let Some((analysis, fixture_analysis)) =
|
||||
@ -113,8 +120,6 @@ pub(crate) fn goto_definition(
|
||||
});
|
||||
}
|
||||
|
||||
let parent = token.value.parent()?;
|
||||
|
||||
let token_file_id = token.file_id;
|
||||
if let Some(token) = ast::String::cast(token.value.clone())
|
||||
&& let Some(x) =
|
||||
@ -149,6 +154,45 @@ pub(crate) fn goto_definition(
|
||||
Some(RangeInfo::new(original_token.text_range(), navs))
|
||||
}
|
||||
|
||||
/// When the `?` operator is used on `Result`, go to the `From` impl if it exists as this provides more value.
|
||||
fn goto_question_mark_conversions(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
node: &SyntaxNode,
|
||||
) -> Option<hir::Function> {
|
||||
let node = ast::TryExpr::cast(node.clone())?;
|
||||
let try_expr_ty = sema.type_of_expr(&node.expr()?)?.adjusted();
|
||||
|
||||
let fd = FamousDefs(sema, try_expr_ty.krate(sema.db));
|
||||
let result_enum = fd.core_result_Result()?.into();
|
||||
|
||||
let (try_expr_ty_adt, try_expr_ty_args) = try_expr_ty.as_adt_with_args()?;
|
||||
if try_expr_ty_adt != result_enum {
|
||||
// FIXME: Support `Poll<Result>`.
|
||||
return None;
|
||||
}
|
||||
let original_err_ty = try_expr_ty_args.get(1)?.clone()?;
|
||||
|
||||
let returned_ty = sema.try_expr_returned_type(&node)?;
|
||||
let (returned_adt, returned_ty_args) = returned_ty.as_adt_with_args()?;
|
||||
if returned_adt != result_enum {
|
||||
return None;
|
||||
}
|
||||
let returned_err_ty = returned_ty_args.get(1)?.clone()?;
|
||||
|
||||
if returned_err_ty.could_unify_with_deeply(sema.db, &original_err_ty) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let from_trait = fd.core_convert_From()?;
|
||||
let from_fn = from_trait.function(sema.db, sym::from)?;
|
||||
sema.resolve_trait_impl_method(
|
||||
returned_err_ty.clone(),
|
||||
from_trait,
|
||||
from_fn,
|
||||
[returned_err_ty, original_err_ty],
|
||||
)
|
||||
}
|
||||
|
||||
// If the token is into(), try_into(), search the definition of From, TryFrom.
|
||||
fn find_definition_for_known_blanket_dual_impls(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
@ -4034,4 +4078,25 @@ where
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn question_mark_on_result_goes_to_conversion() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: try, result, from
|
||||
|
||||
struct Foo;
|
||||
struct Bar;
|
||||
impl From<Foo> for Bar {
|
||||
fn from(_: Foo) -> Bar { Bar }
|
||||
// ^^^^
|
||||
}
|
||||
|
||||
fn foo() -> Result<(), Bar> {
|
||||
Err(Foo)?$0;
|
||||
Ok(())
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user