Fix handling of generics in tuple variants and refactor a bit

Also make them display a tiny bit nicer.

Fixes #860.
This commit is contained in:
Florian Diebold 2019-02-20 22:36:54 +01:00
parent c84561bb62
commit 72712b8a42
6 changed files with 122 additions and 41 deletions

View file

@ -87,4 +87,17 @@ impl GenericParams {
let parent_count = self.count_parent_params();
parent_count + self.params.len()
}
fn for_each_param<'a>(&'a self, f: &mut impl FnMut(&'a GenericParam)) {
if let Some(parent) = &self.parent_params {
parent.for_each_param(f);
}
self.params.iter().for_each(f);
}
pub fn params_including_parent(&self) -> Vec<&GenericParam> {
let mut vec = Vec::with_capacity(self.count_params_including_parent());
self.for_each_param(&mut |p| vec.push(p));
vec
}
}

View file

@ -40,7 +40,7 @@ use crate::{
name::KnownName,
expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self},
generics::GenericParams,
path::GenericArg,
path::{ GenericArgs, GenericArg},
adt::VariantDef,
resolve::{Resolver, Resolution}, nameres::Namespace
};
@ -165,17 +165,6 @@ impl Substs {
pub fn empty() -> Substs {
Substs(Arc::new([]))
}
/// Replaces the end of the substitutions by other ones.
pub(crate) fn replace_tail(self, replace_by: Vec<Ty>) -> Substs {
// again missing Arc::make_mut_slice...
let len = replace_by.len().min(self.0.len());
let parent_len = self.0.len() - len;
let mut result = Vec::with_capacity(parent_len + len);
result.extend(self.0.iter().take(parent_len).cloned());
result.extend(replace_by);
Substs(result.into())
}
}
/// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs).
@ -639,8 +628,11 @@ impl fmt::Display for Ty {
join(sig.input.iter()).surround_with("fn(", ")").separator(", ").to_fmt(f)?;
write!(f, " -> {}", sig.output)
}
Ty::FnDef { name, substs, sig, .. } => {
write!(f, "fn {}", name)?;
Ty::FnDef { def, name, substs, sig, .. } => {
match def {
CallableDef::Function(_) => write!(f, "fn {}", name)?,
CallableDef::Struct(_) | CallableDef::EnumVariant(_) => write!(f, "{}", name)?,
}
if substs.0.len() > 0 {
join(substs.0.iter()).surround_with("<", ">").separator(", ").to_fmt(f)?;
}
@ -712,16 +704,18 @@ fn type_for_enum_variant_constructor(db: &impl HirDatabase, def: EnumVariant) ->
.iter()
.map(|(_, field)| Ty::from_hir(db, &resolver, &field.type_ref))
.collect::<Vec<_>>();
let output = type_for_enum(db, def.parent_enum(db));
let sig = Arc::new(FnSig { input, output });
let substs = make_substs(&generics);
let output = type_for_enum(db, def.parent_enum(db)).apply_substs(substs.clone());
let sig = Arc::new(FnSig { input, output });
Ty::FnDef { def: def.into(), sig, name, substs }
}
fn make_substs(generics: &GenericParams) -> Substs {
Substs(
(0..generics.count_params_including_parent())
.map(|_p| Ty::Unknown)
generics
.params_including_parent()
.into_iter()
.map(|p| Ty::Param { idx: p.idx, name: p.name.clone() })
.collect::<Vec<_>>()
.into(),
)
@ -736,7 +730,7 @@ fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty {
}
}
pub(crate) fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty {
fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty {
let generics = s.generic_params(db);
Ty::Adt {
def_id: s.into(),
@ -1353,6 +1347,36 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
ty
}
fn substs_for_method_call(
&mut self,
def_generics: Option<Arc<GenericParams>>,
generic_args: &Option<GenericArgs>,
) -> Substs {
let (parent_param_count, param_count) =
def_generics.map_or((0, 0), |g| (g.count_parent_params(), g.params.len()));
let mut substs = Vec::with_capacity(parent_param_count + param_count);
for _ in 0..parent_param_count {
substs.push(Ty::Unknown);
}
// handle provided type arguments
if let Some(generic_args) = generic_args {
// if args are provided, it should be all of them, but we can't rely on that
for arg in generic_args.args.iter().take(param_count) {
match arg {
GenericArg::Type(type_ref) => {
let ty = self.make_ty(type_ref);
substs.push(ty);
}
}
}
};
let supplied_params = substs.len();
for _ in supplied_params..parent_param_count + param_count {
substs.push(Ty::Unknown);
}
Substs(substs.into())
}
fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
let body = Arc::clone(&self.body); // avoid borrow checker problem
let ty = match &body[tgt_expr] {
@ -1443,25 +1467,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
None => (Ty::Unknown, receiver_ty, None),
};
// handle provided type arguments
let method_ty = if let Some(generic_args) = generic_args {
// if args are provided, it should be all of them, but we can't rely on that
let param_count = def_generics.map(|g| g.params.len()).unwrap_or(0);
let mut new_substs = Vec::with_capacity(generic_args.args.len());
for arg in generic_args.args.iter().take(param_count) {
match arg {
GenericArg::Type(type_ref) => {
let ty = self.make_ty(type_ref);
new_substs.push(ty);
}
}
}
let substs = method_ty.substs().unwrap_or_else(Substs::empty);
let substs = substs.replace_tail(new_substs);
method_ty.apply_substs(substs)
} else {
method_ty
};
let substs = self.substs_for_method_call(def_generics, generic_args);
let method_ty = method_ty.apply_substs(substs);
let method_ty = self.insert_type_vars(method_ty);
let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty {
Ty::FnPtr(sig) => {

View file

@ -1,19 +1,19 @@
---
created: "2019-02-17T16:16:58.863630956Z"
created: "2019-02-20T21:31:12.910924715Z"
creator: insta@0.6.2
source: crates/ra_hir/src/ty/tests.rs
expression: "&result"
---
[72; 154) '{ ...a.c; }': ()
[82; 83) 'c': C
[86; 87) 'C': fn C(usize) -> C
[86; 87) 'C': C(usize) -> C
[86; 90) 'C(1)': C
[88; 89) '1': usize
[96; 97) 'B': B
[107; 108) 'a': A
[114; 133) 'A { b:...C(1) }': A
[121; 122) 'B': B
[127; 128) 'C': fn C(usize) -> C
[127; 128) 'C': C(usize) -> C
[127; 131) 'C(1)': C
[129; 130) '1': usize
[139; 140) 'a': A

View file

@ -0,0 +1,23 @@
---
created: "2019-02-20T21:31:12.911275141Z"
creator: insta@0.6.2
source: crates/ra_hir/src/ty/tests.rs
expression: "&result"
---
[77; 185) '{ ...one; }': ()
[83; 84) 'A': A<i32>(T) -> A<T>
[83; 88) 'A(42)': A<i32>
[85; 87) '42': i32
[94; 95) 'A': A<u128>(T) -> A<T>
[94; 103) 'A(42u128)': A<u128>
[96; 102) '42u128': u128
[109; 113) 'Some': Some<&str>(T) -> Option<T>
[109; 118) 'Some("x")': Option<&str>
[114; 117) '"x"': &str
[124; 136) 'Option::Some': Some<&str>(T) -> Option<T>
[124; 141) 'Option...e("x")': Option<&str>
[137; 140) '"x"': &str
[147; 151) 'None': Option<[unknown]>
[161; 162) 'x': Option<i64>
[178; 182) 'None': Option<i64>

View file

@ -465,6 +465,27 @@ fn test(a1: A<u32>, i: i32) {
);
}
#[test]
fn infer_tuple_struct_generics() {
check_inference(
"infer_tuple_struct_generics",
r#"
struct A<T>(T);
enum Option<T> { Some(T), None };
use Option::*;
fn test() {
A(42);
A(42u128);
Some("x");
Option::Some("x");
None;
let x: Option<i64> = None;
}
"#,
);
}
#[test]
fn infer_generics_in_patterns() {
check_inference(

View file

@ -163,6 +163,23 @@ mod tests {
assert_eq!(hover.info, "u32");
}
#[test]
fn hover_some() {
let (analysis, position) = single_file_with_position(
"
enum Option<T> { Some(T) }
use Option::Some;
fn main() {
So<|>me(12);
}
",
);
let hover = analysis.hover(position).unwrap().unwrap();
// not the nicest way to show it currently
assert_eq!(hover.info, "Some<i32>(T) -> Option<T>");
}
#[test]
fn hover_for_local_variable() {
let (analysis, position) = single_file_with_position("fn func(foo: i32) { fo<|>o; }");