mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 05:45:12 +00:00
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:
parent
c84561bb62
commit
72712b8a42
6 changed files with 122 additions and 41 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
@ -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(
|
||||
|
|
|
@ -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; }");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue