mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
Merge #1512
1512: Infer ? operator r=unrealhoang a=unrealhoang Logical continuation of https://github.com/rust-analyzer/rust-analyzer/pull/1501 cc https://github.com/rust-analyzer/rust-analyzer/issues/1426 Co-authored-by: Unreal Hoang <unrealhoang@gmail.com>
This commit is contained in:
commit
35f28c538a
3 changed files with 107 additions and 18 deletions
|
@ -115,6 +115,9 @@ pub(crate) const ITER: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"iter
|
||||||
pub(crate) const INTO_ITERATOR: Name =
|
pub(crate) const INTO_ITERATOR: Name =
|
||||||
Name::new(SmolStr::new_inline_from_ascii(12, b"IntoIterator"));
|
Name::new(SmolStr::new_inline_from_ascii(12, b"IntoIterator"));
|
||||||
pub(crate) const ITEM: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"Item"));
|
pub(crate) const ITEM: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"Item"));
|
||||||
|
pub(crate) const OPS: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"ops"));
|
||||||
|
pub(crate) const TRY: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Try"));
|
||||||
|
pub(crate) const OK: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok"));
|
||||||
|
|
||||||
fn resolve_name(text: &SmolStr) -> SmolStr {
|
fn resolve_name(text: &SmolStr) -> SmolStr {
|
||||||
let raw_start = "r#";
|
let raw_start = "r#";
|
||||||
|
|
|
@ -40,7 +40,7 @@ use crate::{
|
||||||
PatId, Statement, UnaryOp,
|
PatId, Statement, UnaryOp,
|
||||||
},
|
},
|
||||||
generics::{GenericParams, HasGenericParams},
|
generics::{GenericParams, HasGenericParams},
|
||||||
name::{INTO_ITERATOR, ITEM, ITER, SELF_TYPE, STD},
|
name,
|
||||||
nameres::{Namespace, PerNs},
|
nameres::{Namespace, PerNs},
|
||||||
path::{GenericArg, GenericArgs, PathKind, PathSegment},
|
path::{GenericArg, GenericArgs, PathKind, PathSegment},
|
||||||
resolve::{
|
resolve::{
|
||||||
|
@ -843,7 +843,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
// Parent arguments are unknown, except for the receiver type
|
// Parent arguments are unknown, except for the receiver type
|
||||||
if let Some(parent_generics) = def_generics.and_then(|p| p.parent_params.clone()) {
|
if let Some(parent_generics) = def_generics.and_then(|p| p.parent_params.clone()) {
|
||||||
for param in &parent_generics.params {
|
for param in &parent_generics.params {
|
||||||
if param.name == SELF_TYPE {
|
if param.name == name::SELF_TYPE {
|
||||||
substs.push(receiver_ty.clone());
|
substs.push(receiver_ty.clone());
|
||||||
} else {
|
} else {
|
||||||
substs.push(Ty::Unknown);
|
substs.push(Ty::Unknown);
|
||||||
|
@ -1140,8 +1140,23 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
self.insert_type_vars(ty)
|
self.insert_type_vars(ty)
|
||||||
}
|
}
|
||||||
Expr::Try { expr } => {
|
Expr::Try { expr } => {
|
||||||
let _inner_ty = self.infer_expr(*expr, &Expectation::none());
|
let inner_ty = self.infer_expr(*expr, &Expectation::none());
|
||||||
Ty::Unknown
|
let ty = match self.resolve_ops_try_ok() {
|
||||||
|
Some(ops_try_ok_alias) => {
|
||||||
|
let ty = self.new_type_var();
|
||||||
|
let projection = ProjectionPredicate {
|
||||||
|
ty: ty.clone(),
|
||||||
|
projection_ty: ProjectionTy {
|
||||||
|
associated_ty: ops_try_ok_alias,
|
||||||
|
parameters: vec![inner_ty].into(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.obligations.push(Obligation::Projection(projection));
|
||||||
|
self.resolve_ty_as_possible(&mut vec![], ty)
|
||||||
|
}
|
||||||
|
None => Ty::Unknown,
|
||||||
|
};
|
||||||
|
ty
|
||||||
}
|
}
|
||||||
Expr::Cast { expr, type_ref } => {
|
Expr::Cast { expr, type_ref } => {
|
||||||
let _inner_ty = self.infer_expr(*expr, &Expectation::none());
|
let _inner_ty = self.infer_expr(*expr, &Expectation::none());
|
||||||
|
@ -1347,15 +1362,33 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
let into_iter_path = Path {
|
let into_iter_path = Path {
|
||||||
kind: PathKind::Abs,
|
kind: PathKind::Abs,
|
||||||
segments: vec![
|
segments: vec![
|
||||||
PathSegment { name: STD, args_and_bindings: None },
|
PathSegment { name: name::STD, args_and_bindings: None },
|
||||||
PathSegment { name: ITER, args_and_bindings: None },
|
PathSegment { name: name::ITER, args_and_bindings: None },
|
||||||
PathSegment { name: INTO_ITERATOR, args_and_bindings: None },
|
PathSegment { name: name::INTO_ITERATOR, args_and_bindings: None },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.resolver.resolve_path_segments(self.db, &into_iter_path).into_fully_resolved() {
|
match self.resolver.resolve_path_segments(self.db, &into_iter_path).into_fully_resolved() {
|
||||||
PerNs { types: Some(Def(Trait(trait_))), .. } => {
|
PerNs { types: Some(Def(Trait(trait_))), .. } => {
|
||||||
Some(trait_.associated_type_by_name(self.db, ITEM)?)
|
Some(trait_.associated_type_by_name(self.db, name::ITEM)?)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_ops_try_ok(&self) -> Option<TypeAlias> {
|
||||||
|
let ops_try_path = Path {
|
||||||
|
kind: PathKind::Abs,
|
||||||
|
segments: vec![
|
||||||
|
PathSegment { name: name::STD, args_and_bindings: None },
|
||||||
|
PathSegment { name: name::OPS, args_and_bindings: None },
|
||||||
|
PathSegment { name: name::TRY, args_and_bindings: None },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.resolver.resolve_path_segments(self.db, &ops_try_path).into_fully_resolved() {
|
||||||
|
PerNs { types: Some(Def(Trait(trait_))), .. } => {
|
||||||
|
Some(trait_.associated_type_by_name(self.db, name::OK)?)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,20 +20,58 @@ use crate::{
|
||||||
// against snapshots of the expected results using insta. Use cargo-insta to
|
// against snapshots of the expected results using insta. Use cargo-insta to
|
||||||
// update the snapshots.
|
// update the snapshots.
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_try() {
|
||||||
|
let (mut db, pos) = MockDatabase::with_position(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
let r: Result<i32, u64> = Result::Ok(1);
|
||||||
|
let v = r?;
|
||||||
|
v<|>;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /std.rs
|
||||||
|
|
||||||
|
#[prelude_import] use ops::*;
|
||||||
|
mod ops {
|
||||||
|
trait Try {
|
||||||
|
type Ok;
|
||||||
|
type Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[prelude_import] use result::*;
|
||||||
|
mod result {
|
||||||
|
enum Result<O, E> {
|
||||||
|
Ok(O),
|
||||||
|
Err(E)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O, E> crate::ops::Try for Result<O, E> {
|
||||||
|
type Ok = O;
|
||||||
|
type Error = E;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
db.set_crate_graph_from_fixture(crate_graph! {
|
||||||
|
"main": ("/main.rs", ["std"]),
|
||||||
|
"std": ("/std.rs", []),
|
||||||
|
});
|
||||||
|
assert_eq!("i32", type_at_pos(&db, pos));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_for_loop() {
|
fn infer_for_loop() {
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
let (mut db, pos) = MockDatabase::with_position(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs
|
||||||
struct Vec<T> {}
|
|
||||||
impl<T> Vec<T> {
|
|
||||||
fn new() -> Self { Vec {} }
|
|
||||||
fn push(&mut self, t: T) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ::std::iter::IntoIterator for Vec<T> {
|
use std::collections::Vec;
|
||||||
type Item=T;
|
|
||||||
}
|
|
||||||
fn test() {
|
fn test() {
|
||||||
let v = Vec::new();
|
let v = Vec::new();
|
||||||
v.push("foo");
|
v.push("foo");
|
||||||
|
@ -42,20 +80,35 @@ fn test() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /lib.rs
|
//- /std.rs
|
||||||
|
|
||||||
|
#[prelude_import] use iter::*;
|
||||||
mod iter {
|
mod iter {
|
||||||
trait IntoIterator {
|
trait IntoIterator {
|
||||||
type Item;
|
type Item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod collections {
|
||||||
|
struct Vec<T> {}
|
||||||
|
impl<T> Vec<T> {
|
||||||
|
fn new() -> Self { Vec {} }
|
||||||
|
fn push(&mut self, t: T) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> crate::iter::IntoIterator for Vec<T> {
|
||||||
|
type Item=T;
|
||||||
|
}
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
db.set_crate_graph_from_fixture(crate_graph! {
|
db.set_crate_graph_from_fixture(crate_graph! {
|
||||||
"main": ("/main.rs", ["std"]),
|
"main": ("/main.rs", ["std"]),
|
||||||
"std": ("/lib.rs", []),
|
"std": ("/std.rs", []),
|
||||||
});
|
});
|
||||||
assert_eq!("&str", type_at_pos(&db, pos));
|
assert_eq!("&str", type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_basics() {
|
fn infer_basics() {
|
||||||
assert_snapshot_matches!(
|
assert_snapshot_matches!(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue