mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Correctly infer - and ! using std::ops::{Neg,Not}
This commit is contained in:
parent
95dc2de8e9
commit
7705209051
5 changed files with 114 additions and 23 deletions
|
@ -342,6 +342,14 @@ pub mod known {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn std_ops_neg() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::NEG_TYPE])
|
||||
}
|
||||
|
||||
pub fn std_ops_not() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::NOT_TYPE])
|
||||
}
|
||||
|
||||
pub fn std_result_result() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE])
|
||||
}
|
||||
|
|
|
@ -152,6 +152,8 @@ pub const RANGE_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(b"RangeInclusive")
|
|||
pub const RANGE_TO_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(b"RangeToInclusive");
|
||||
pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(b"RangeTo");
|
||||
pub const RANGE_TYPE: Name = Name::new_inline_ascii(b"Range");
|
||||
pub const NEG_TYPE: Name = Name::new_inline_ascii(b"Neg");
|
||||
pub const NOT_TYPE: Name = Name::new_inline_ascii(b"Not");
|
||||
|
||||
// Builtin Macros
|
||||
pub const FILE_MACRO: Name = Name::new_inline_ascii(b"file");
|
||||
|
|
|
@ -36,8 +36,8 @@ use ra_prof::profile;
|
|||
use super::{
|
||||
primitive::{FloatTy, IntTy},
|
||||
traits::{Guidance, Obligation, ProjectionPredicate, Solution},
|
||||
ApplicationTy, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
Uncertain,
|
||||
ApplicationTy, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
|
||||
TypeWalk, Uncertain,
|
||||
};
|
||||
use crate::{db::HirDatabase, infer::diagnostics::InferenceDiagnostic};
|
||||
|
||||
|
@ -433,6 +433,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
self.db.trait_data(trait_).associated_type_by_name(&name::OK_TYPE)
|
||||
}
|
||||
|
||||
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
|
||||
let path = known::std_ops_neg();
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name::OUTPUT_TYPE)
|
||||
}
|
||||
|
||||
fn resolve_ops_not_output(&self) -> Option<TypeAliasId> {
|
||||
let path = known::std_ops_not();
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name::OUTPUT_TYPE)
|
||||
}
|
||||
|
||||
fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
|
||||
let path = known::std_future_future();
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
|
||||
|
|
|
@ -332,31 +332,36 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
},
|
||||
UnaryOp::Neg => {
|
||||
match &inner_ty {
|
||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||
TypeCtor::Int(Uncertain::Unknown)
|
||||
| TypeCtor::Int(Uncertain::Known(IntTy {
|
||||
signedness: Signedness::Signed,
|
||||
..
|
||||
}))
|
||||
| TypeCtor::Float(..) => inner_ty,
|
||||
_ => Ty::Unknown,
|
||||
},
|
||||
Ty::Infer(InferTy::IntVar(..)) | Ty::Infer(InferTy::FloatVar(..)) => {
|
||||
inner_ty
|
||||
}
|
||||
// FIXME: resolve ops::Neg trait
|
||||
_ => Ty::Unknown,
|
||||
// Fast path for builtins
|
||||
Ty::Apply(ApplicationTy {
|
||||
ctor:
|
||||
TypeCtor::Int(Uncertain::Known(IntTy {
|
||||
signedness: Signedness::Signed,
|
||||
..
|
||||
})),
|
||||
..
|
||||
})
|
||||
| Ty::Apply(ApplicationTy {
|
||||
ctor: TypeCtor::Int(Uncertain::Unknown),
|
||||
..
|
||||
})
|
||||
| Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(_), .. })
|
||||
| Ty::Infer(InferTy::IntVar(..))
|
||||
| Ty::Infer(InferTy::FloatVar(..)) => inner_ty,
|
||||
// Otherwise we resolve via the std::ops::Neg trait
|
||||
_ => self
|
||||
.resolve_associated_type(inner_ty, self.resolve_ops_neg_output()),
|
||||
}
|
||||
}
|
||||
UnaryOp::Not => {
|
||||
match &inner_ty {
|
||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||
TypeCtor::Bool | TypeCtor::Int(_) => inner_ty,
|
||||
_ => Ty::Unknown,
|
||||
},
|
||||
Ty::Infer(InferTy::IntVar(..)) => inner_ty,
|
||||
// FIXME: resolve ops::Not trait for inner_ty
|
||||
_ => Ty::Unknown,
|
||||
// Fast path for builtins
|
||||
Ty::Apply(ApplicationTy { ctor: TypeCtor::Bool, .. })
|
||||
| Ty::Apply(ApplicationTy { ctor: TypeCtor::Int(_), .. })
|
||||
| Ty::Infer(InferTy::IntVar(..)) => inner_ty,
|
||||
// Otherwise we resolve via the std::ops::Not trait
|
||||
_ => self
|
||||
.resolve_associated_type(inner_ty, self.resolve_ops_not_output()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,6 +115,70 @@ mod collections {
|
|||
assert_eq!("&str", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_ops_neg() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
|
||||
struct Bar;
|
||||
struct Foo;
|
||||
|
||||
impl std::ops::Neg for Bar {
|
||||
type Output = Foo;
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let a = Bar;
|
||||
let b = -a;
|
||||
b<|>;
|
||||
}
|
||||
|
||||
//- /std.rs crate:std
|
||||
|
||||
#[prelude_import] use ops::*;
|
||||
mod ops {
|
||||
pub trait Neg {
|
||||
type Output;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("Foo", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_ops_not() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
|
||||
struct Bar;
|
||||
struct Foo;
|
||||
|
||||
impl std::ops::Not for Bar {
|
||||
type Output = Foo;
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let a = Bar;
|
||||
let b = !a;
|
||||
b<|>;
|
||||
}
|
||||
|
||||
//- /std.rs crate:std
|
||||
|
||||
#[prelude_import] use ops::*;
|
||||
mod ops {
|
||||
pub trait Not {
|
||||
type Output;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("Foo", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_from_bound_1() {
|
||||
assert_snapshot!(
|
||||
|
|
Loading…
Reference in a new issue