aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/ty')
-rw-r--r--crates/ra_hir/src/ty/infer.rs58
-rw-r--r--crates/ra_hir/src/ty/method_resolution.rs95
-rw-r--r--crates/ra_hir/src/ty/tests.rs36
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};
41use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; 43use 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
118impl Index<ExprId> for InferenceResult { 129impl 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
1272mod 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
9use crate::{ 9use 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};
16use 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
44impl CrateImplBlocks { 44impl 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.
113pub(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
112fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> { 125fn 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
122impl Ty { 135impl 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]
2324fn 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}