diff options
Diffstat (limited to 'crates/ra_hir/src/ty')
-rw-r--r-- | crates/ra_hir/src/ty/infer.rs | 58 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 95 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 36 |
3 files changed, 160 insertions, 29 deletions
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index cff7e7481..573115321 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -36,7 +36,9 @@ use crate::{ | |||
36 | path::{GenericArgs, GenericArg}, | 36 | path::{GenericArgs, GenericArg}, |
37 | adt::VariantDef, | 37 | adt::VariantDef, |
38 | resolve::{Resolver, Resolution}, | 38 | resolve::{Resolver, Resolution}, |
39 | nameres::Namespace | 39 | nameres::Namespace, |
40 | ty::infer::diagnostics::InferenceDiagnostic, | ||
41 | diagnostics::DiagnosticSink, | ||
40 | }; | 42 | }; |
41 | use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; | 43 | use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; |
42 | 44 | ||
@@ -96,6 +98,7 @@ pub struct InferenceResult { | |||
96 | field_resolutions: FxHashMap<ExprId, StructField>, | 98 | field_resolutions: FxHashMap<ExprId, StructField>, |
97 | /// For each associated item record what it resolves to | 99 | /// For each associated item record what it resolves to |
98 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, | 100 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, |
101 | diagnostics: Vec<InferenceDiagnostic>, | ||
99 | pub(super) type_of_expr: ArenaMap<ExprId, Ty>, | 102 | pub(super) type_of_expr: ArenaMap<ExprId, Ty>, |
100 | pub(super) type_of_pat: ArenaMap<PatId, Ty>, | 103 | pub(super) type_of_pat: ArenaMap<PatId, Ty>, |
101 | } | 104 | } |
@@ -113,6 +116,14 @@ impl InferenceResult { | |||
113 | pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> { | 116 | pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> { |
114 | self.assoc_resolutions.get(&id.into()).map(|it| *it) | 117 | self.assoc_resolutions.get(&id.into()).map(|it| *it) |
115 | } | 118 | } |
119 | pub(crate) fn add_diagnostics( | ||
120 | &self, | ||
121 | db: &impl HirDatabase, | ||
122 | owner: Function, | ||
123 | sink: &mut DiagnosticSink, | ||
124 | ) { | ||
125 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) | ||
126 | } | ||
116 | } | 127 | } |
117 | 128 | ||
118 | impl Index<ExprId> for InferenceResult { | 129 | impl Index<ExprId> for InferenceResult { |
@@ -143,6 +154,7 @@ struct InferenceContext<'a, D: HirDatabase> { | |||
143 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, | 154 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, |
144 | type_of_expr: ArenaMap<ExprId, Ty>, | 155 | type_of_expr: ArenaMap<ExprId, Ty>, |
145 | type_of_pat: ArenaMap<PatId, Ty>, | 156 | type_of_pat: ArenaMap<PatId, Ty>, |
157 | diagnostics: Vec<InferenceDiagnostic>, | ||
146 | /// The return type of the function being inferred. | 158 | /// The return type of the function being inferred. |
147 | return_ty: Ty, | 159 | return_ty: Ty, |
148 | } | 160 | } |
@@ -155,6 +167,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
155 | assoc_resolutions: FxHashMap::default(), | 167 | assoc_resolutions: FxHashMap::default(), |
156 | type_of_expr: ArenaMap::default(), | 168 | type_of_expr: ArenaMap::default(), |
157 | type_of_pat: ArenaMap::default(), | 169 | type_of_pat: ArenaMap::default(), |
170 | diagnostics: Vec::default(), | ||
158 | var_unification_table: InPlaceUnificationTable::new(), | 171 | var_unification_table: InPlaceUnificationTable::new(), |
159 | return_ty: Ty::Unknown, // set in collect_fn_signature | 172 | return_ty: Ty::Unknown, // set in collect_fn_signature |
160 | db, | 173 | db, |
@@ -181,6 +194,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
181 | assoc_resolutions: self.assoc_resolutions, | 194 | assoc_resolutions: self.assoc_resolutions, |
182 | type_of_expr: expr_types, | 195 | type_of_expr: expr_types, |
183 | type_of_pat: pat_types, | 196 | type_of_pat: pat_types, |
197 | diagnostics: self.diagnostics, | ||
184 | } | 198 | } |
185 | } | 199 | } |
186 | 200 | ||
@@ -807,7 +821,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
807 | } | 821 | } |
808 | Expr::MethodCall { receiver, args, method_name, generic_args } => { | 822 | Expr::MethodCall { receiver, args, method_name, generic_args } => { |
809 | let receiver_ty = self.infer_expr(*receiver, &Expectation::none()); | 823 | let receiver_ty = self.infer_expr(*receiver, &Expectation::none()); |
810 | let resolved = receiver_ty.clone().lookup_method(self.db, method_name); | 824 | let resolved = |
825 | receiver_ty.clone().lookup_method(self.db, method_name, &self.resolver); | ||
811 | let (derefed_receiver_ty, method_ty, def_generics) = match resolved { | 826 | let (derefed_receiver_ty, method_ty, def_generics) = match resolved { |
812 | Some((ty, func)) => { | 827 | Some((ty, func)) => { |
813 | self.write_method_resolution(tgt_expr, func); | 828 | self.write_method_resolution(tgt_expr, func); |
@@ -915,9 +930,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
915 | Expr::StructLit { path, fields, spread } => { | 930 | Expr::StructLit { path, fields, spread } => { |
916 | let (ty, def_id) = self.resolve_variant(path.as_ref()); | 931 | let (ty, def_id) = self.resolve_variant(path.as_ref()); |
917 | let substs = ty.substs().unwrap_or_else(Substs::empty); | 932 | let substs = ty.substs().unwrap_or_else(Substs::empty); |
918 | for field in fields { | 933 | for (field_idx, field) in fields.into_iter().enumerate() { |
919 | let field_ty = def_id | 934 | let field_ty = def_id |
920 | .and_then(|it| it.field(self.db, &field.name)) | 935 | .and_then(|it| match it.field(self.db, &field.name) { |
936 | Some(field) => Some(field), | ||
937 | None => { | ||
938 | self.diagnostics.push(InferenceDiagnostic::NoSuchField { | ||
939 | expr: tgt_expr, | ||
940 | field: field_idx, | ||
941 | }); | ||
942 | None | ||
943 | } | ||
944 | }) | ||
921 | .map_or(Ty::Unknown, |field| field.ty(self.db)) | 945 | .map_or(Ty::Unknown, |field| field.ty(self.db)) |
922 | .subst(&substs); | 946 | .subst(&substs); |
923 | self.infer_expr(field.expr, &Expectation::has_type(field_ty)); | 947 | self.infer_expr(field.expr, &Expectation::has_type(field_ty)); |
@@ -1244,3 +1268,29 @@ impl Expectation { | |||
1244 | Expectation { ty: Ty::Unknown } | 1268 | Expectation { ty: Ty::Unknown } |
1245 | } | 1269 | } |
1246 | } | 1270 | } |
1271 | |||
1272 | mod diagnostics { | ||
1273 | use crate::{expr::ExprId, diagnostics::{DiagnosticSink, NoSuchField}, HirDatabase, Function}; | ||
1274 | |||
1275 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
1276 | pub(super) enum InferenceDiagnostic { | ||
1277 | NoSuchField { expr: ExprId, field: usize }, | ||
1278 | } | ||
1279 | |||
1280 | impl InferenceDiagnostic { | ||
1281 | pub(super) fn add_to( | ||
1282 | &self, | ||
1283 | db: &impl HirDatabase, | ||
1284 | owner: Function, | ||
1285 | sink: &mut DiagnosticSink, | ||
1286 | ) { | ||
1287 | match self { | ||
1288 | InferenceDiagnostic::NoSuchField { expr, field } => { | ||
1289 | let (file, _) = owner.source(db); | ||
1290 | let field = owner.body_source_map(db).field_syntax(*expr, *field); | ||
1291 | sink.push(NoSuchField { file, field }) | ||
1292 | } | ||
1293 | } | ||
1294 | } | ||
1295 | } | ||
1296 | } | ||
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index b1684acf9..3ac8dc46b 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -8,12 +8,12 @@ use rustc_hash::FxHashMap; | |||
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | HirDatabase, Module, Crate, Name, Function, Trait, | 10 | HirDatabase, Module, Crate, Name, Function, Trait, |
11 | ids::TraitId, | ||
12 | impl_block::{ImplId, ImplBlock, ImplItem}, | 11 | impl_block::{ImplId, ImplBlock, ImplItem}, |
13 | ty::{Ty, TypeCtor}, | 12 | ty::{Ty, TypeCtor}, |
14 | nameres::CrateModuleId, | 13 | nameres::CrateModuleId, resolve::Resolver, traits::TraitItem |
15 | 14 | ||
16 | }; | 15 | }; |
16 | use super::{ TraitRef, Substs}; | ||
17 | 17 | ||
18 | /// This is used as a key for indexing impls. | 18 | /// This is used as a key for indexing impls. |
19 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 19 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
@@ -38,7 +38,7 @@ pub struct CrateImplBlocks { | |||
38 | /// To make sense of the CrateModuleIds, we need the source root. | 38 | /// To make sense of the CrateModuleIds, we need the source root. |
39 | krate: Crate, | 39 | krate: Crate, |
40 | impls: FxHashMap<TyFingerprint, Vec<(CrateModuleId, ImplId)>>, | 40 | impls: FxHashMap<TyFingerprint, Vec<(CrateModuleId, ImplId)>>, |
41 | impls_by_trait: FxHashMap<TraitId, Vec<(CrateModuleId, ImplId)>>, | 41 | impls_by_trait: FxHashMap<Trait, Vec<(CrateModuleId, ImplId)>>, |
42 | } | 42 | } |
43 | 43 | ||
44 | impl CrateImplBlocks { | 44 | impl CrateImplBlocks { |
@@ -56,8 +56,7 @@ impl CrateImplBlocks { | |||
56 | &'a self, | 56 | &'a self, |
57 | tr: &Trait, | 57 | tr: &Trait, |
58 | ) -> impl Iterator<Item = ImplBlock> + 'a { | 58 | ) -> impl Iterator<Item = ImplBlock> + 'a { |
59 | let id = tr.id; | 59 | self.impls_by_trait.get(&tr).into_iter().flat_map(|i| i.iter()).map( |
60 | self.impls_by_trait.get(&id).into_iter().flat_map(|i| i.iter()).map( | ||
61 | move |(module_id, impl_id)| { | 60 | move |(module_id, impl_id)| { |
62 | let module = Module { krate: self.krate, module_id: *module_id }; | 61 | let module = Module { krate: self.krate, module_id: *module_id }; |
63 | ImplBlock::from_id(module, *impl_id) | 62 | ImplBlock::from_id(module, *impl_id) |
@@ -73,18 +72,18 @@ impl CrateImplBlocks { | |||
73 | 72 | ||
74 | let target_ty = impl_block.target_ty(db); | 73 | let target_ty = impl_block.target_ty(db); |
75 | 74 | ||
76 | if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { | ||
77 | self.impls | ||
78 | .entry(target_ty_fp) | ||
79 | .or_insert_with(Vec::new) | ||
80 | .push((module.module_id, impl_id)); | ||
81 | } | ||
82 | |||
83 | if let Some(tr) = impl_block.target_trait(db) { | 75 | if let Some(tr) = impl_block.target_trait(db) { |
84 | self.impls_by_trait | 76 | self.impls_by_trait |
85 | .entry(tr.id) | 77 | .entry(tr) |
86 | .or_insert_with(Vec::new) | 78 | .or_insert_with(Vec::new) |
87 | .push((module.module_id, impl_id)); | 79 | .push((module.module_id, impl_id)); |
80 | } else { | ||
81 | if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { | ||
82 | self.impls | ||
83 | .entry(target_ty_fp) | ||
84 | .or_insert_with(Vec::new) | ||
85 | .push((module.module_id, impl_id)); | ||
86 | } | ||
88 | } | 87 | } |
89 | } | 88 | } |
90 | 89 | ||
@@ -109,6 +108,20 @@ impl CrateImplBlocks { | |||
109 | } | 108 | } |
110 | } | 109 | } |
111 | 110 | ||
111 | /// Rudimentary check whether an impl exists for a given type and trait; this | ||
112 | /// will actually be done by chalk. | ||
113 | pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> bool { | ||
114 | // FIXME use all trait impls in the whole crate graph | ||
115 | let krate = trait_ref.trait_.module(db).krate(db); | ||
116 | let krate = match krate { | ||
117 | Some(krate) => krate, | ||
118 | None => return false, | ||
119 | }; | ||
120 | let crate_impl_blocks = db.impls_in_crate(krate); | ||
121 | let mut impl_blocks = crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_); | ||
122 | impl_blocks.any(|impl_block| &impl_block.target_ty(db) == trait_ref.self_ty()) | ||
123 | } | ||
124 | |||
112 | fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> { | 125 | fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> { |
113 | match ty { | 126 | match ty { |
114 | Ty::Apply(a_ty) => match a_ty.ctor { | 127 | Ty::Apply(a_ty) => match a_ty.ctor { |
@@ -120,20 +133,64 @@ fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> { | |||
120 | } | 133 | } |
121 | 134 | ||
122 | impl Ty { | 135 | impl Ty { |
123 | // FIXME: cache this as a query? | ||
124 | // - if so, what signature? (TyFingerprint, Name)? | ||
125 | // - or maybe cache all names and def_ids of methods per fingerprint? | ||
126 | /// Look up the method with the given name, returning the actual autoderefed | 136 | /// Look up the method with the given name, returning the actual autoderefed |
127 | /// receiver type (but without autoref applied yet). | 137 | /// receiver type (but without autoref applied yet). |
128 | pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<(Ty, Function)> { | 138 | pub fn lookup_method( |
129 | self.iterate_methods(db, |ty, f| { | 139 | self, |
140 | db: &impl HirDatabase, | ||
141 | name: &Name, | ||
142 | resolver: &Resolver, | ||
143 | ) -> Option<(Ty, Function)> { | ||
144 | // FIXME: trait methods should be used before autoderefs | ||
145 | let inherent_method = self.clone().iterate_methods(db, |ty, f| { | ||
130 | let sig = f.signature(db); | 146 | let sig = f.signature(db); |
131 | if sig.name() == name && sig.has_self_param() { | 147 | if sig.name() == name && sig.has_self_param() { |
132 | Some((ty.clone(), f)) | 148 | Some((ty.clone(), f)) |
133 | } else { | 149 | } else { |
134 | None | 150 | None |
135 | } | 151 | } |
136 | }) | 152 | }); |
153 | inherent_method.or_else(|| self.lookup_trait_method(db, name, resolver)) | ||
154 | } | ||
155 | |||
156 | fn lookup_trait_method( | ||
157 | self, | ||
158 | db: &impl HirDatabase, | ||
159 | name: &Name, | ||
160 | resolver: &Resolver, | ||
161 | ) -> Option<(Ty, Function)> { | ||
162 | let mut candidates = Vec::new(); | ||
163 | for t in resolver.traits_in_scope() { | ||
164 | let data = t.trait_data(db); | ||
165 | for item in data.items() { | ||
166 | match item { | ||
167 | &TraitItem::Function(m) => { | ||
168 | let sig = m.signature(db); | ||
169 | if sig.name() == name && sig.has_self_param() { | ||
170 | candidates.push((t, m)); | ||
171 | } | ||
172 | } | ||
173 | _ => {} | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | // FIXME: | ||
178 | // - we might not actually be able to determine fully that the type | ||
179 | // implements the trait here; it's enough if we (well, Chalk) determine | ||
180 | // that it's possible. | ||
181 | // - when the trait method is picked, we need to register an | ||
182 | // 'obligation' somewhere so that we later check that it's really | ||
183 | // implemented | ||
184 | // - both points go for additional requirements from where clauses as | ||
185 | // well (in fact, the 'implements' condition could just be considered a | ||
186 | // 'where Self: Trait' clause) | ||
187 | candidates.retain(|(t, _m)| { | ||
188 | let trait_ref = TraitRef { trait_: *t, substs: Substs::single(self.clone()) }; | ||
189 | db.implements(trait_ref) | ||
190 | }); | ||
191 | // FIXME if there's multiple candidates here, that's an ambiguity error | ||
192 | let (_chosen_trait, chosen_method) = candidates.first()?; | ||
193 | Some((self.clone(), *chosen_method)) | ||
137 | } | 194 | } |
138 | 195 | ||
139 | // This would be nicer if it just returned an iterator, but that runs into | 196 | // This would be nicer if it just returned an iterator, but that runs into |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 5d8ad4aa7..655f3c522 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -1272,8 +1272,8 @@ fn test() { | |||
1272 | [241; 252) 'Struct::FOO': u32 | 1272 | [241; 252) 'Struct::FOO': u32 |
1273 | [262; 263) 'y': u32 | 1273 | [262; 263) 'y': u32 |
1274 | [266; 275) 'Enum::BAR': u32 | 1274 | [266; 275) 'Enum::BAR': u32 |
1275 | [285; 286) 'z': u32 | 1275 | [285; 286) 'z': {unknown} |
1276 | [289; 302) 'TraitTest::ID': u32"### | 1276 | [289; 302) 'TraitTest::ID': {unknown}"### |
1277 | ); | 1277 | ); |
1278 | } | 1278 | } |
1279 | 1279 | ||
@@ -1918,9 +1918,9 @@ fn test() { | |||
1918 | [110; 114) 'self': &{unknown} | 1918 | [110; 114) 'self': &{unknown} |
1919 | [170; 228) '{ ...i128 }': () | 1919 | [170; 228) '{ ...i128 }': () |
1920 | [176; 178) 'S1': S1 | 1920 | [176; 178) 'S1': S1 |
1921 | [176; 187) 'S1.method()': {unknown} | 1921 | [176; 187) 'S1.method()': u32 |
1922 | [203; 205) 'S2': S2 | 1922 | [203; 205) 'S2': S2 |
1923 | [203; 214) 'S2.method()': {unknown}"### | 1923 | [203; 214) 'S2.method()': i128"### |
1924 | ); | 1924 | ); |
1925 | } | 1925 | } |
1926 | 1926 | ||
@@ -1964,10 +1964,10 @@ mod bar_test { | |||
1964 | [169; 173) 'self': &{unknown} | 1964 | [169; 173) 'self': &{unknown} |
1965 | [300; 337) '{ ... }': () | 1965 | [300; 337) '{ ... }': () |
1966 | [310; 311) 'S': S | 1966 | [310; 311) 'S': S |
1967 | [310; 320) 'S.method()': {unknown} | 1967 | [310; 320) 'S.method()': u32 |
1968 | [416; 454) '{ ... }': () | 1968 | [416; 454) '{ ... }': () |
1969 | [426; 427) 'S': S | 1969 | [426; 427) 'S': S |
1970 | [426; 436) 'S.method()': {unknown}"### | 1970 | [426; 436) 'S.method()': i128"### |
1971 | ); | 1971 | ); |
1972 | } | 1972 | } |
1973 | 1973 | ||
@@ -2319,3 +2319,27 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { | |||
2319 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) | 2319 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) |
2320 | } | 2320 | } |
2321 | } | 2321 | } |
2322 | |||
2323 | #[test] | ||
2324 | fn no_such_field_diagnostics() { | ||
2325 | let diagnostics = MockDatabase::with_files( | ||
2326 | r" | ||
2327 | //- /lib.rs | ||
2328 | struct S { foo: i32, bar: () } | ||
2329 | impl S { | ||
2330 | fn new() -> S { | ||
2331 | S { | ||
2332 | foo: 92, | ||
2333 | baz: 62, | ||
2334 | } | ||
2335 | } | ||
2336 | } | ||
2337 | ", | ||
2338 | ) | ||
2339 | .diagnostics(); | ||
2340 | |||
2341 | assert_snapshot_matches!(diagnostics, @r###" | ||
2342 | "baz: 62": no such field | ||
2343 | "### | ||
2344 | ); | ||
2345 | } | ||