diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 9 | ||||
-rw-r--r-- | crates/ra_hir/src/generics.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 40 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 19 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/infer/path.rs | 82 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 141 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 173 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_path.rs | 142 | ||||
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 74 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 14 | ||||
-rw-r--r-- | crates/ra_syntax/src/parsing/text_tree_sink.rs | 2 |
11 files changed, 597 insertions, 102 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index ae6ef7606..c97ea18a2 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -1053,4 +1053,13 @@ impl AssocItem { | |||
1053 | AssocItem::TypeAlias(t) => t.module(db), | 1053 | AssocItem::TypeAlias(t) => t.module(db), |
1054 | } | 1054 | } |
1055 | } | 1055 | } |
1056 | |||
1057 | pub fn container(self, db: &impl DefDatabase) -> Container { | ||
1058 | match self { | ||
1059 | AssocItem::Function(f) => f.container(db), | ||
1060 | AssocItem::Const(c) => c.container(db), | ||
1061 | AssocItem::TypeAlias(t) => t.container(db), | ||
1062 | } | ||
1063 | .expect("AssocItem without container") | ||
1064 | } | ||
1056 | } | 1065 | } |
diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index 52e1fbf29..9c261eda9 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs | |||
@@ -77,9 +77,10 @@ impl GenericParams { | |||
77 | let parent = match def { | 77 | let parent = match def { |
78 | GenericDef::Function(it) => it.container(db).map(GenericDef::from), | 78 | GenericDef::Function(it) => it.container(db).map(GenericDef::from), |
79 | GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from), | 79 | GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from), |
80 | GenericDef::Const(it) => it.container(db).map(GenericDef::from), | ||
80 | GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()), | 81 | GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()), |
81 | GenericDef::Adt(_) | GenericDef::Trait(_) => None, | 82 | GenericDef::Adt(_) | GenericDef::Trait(_) => None, |
82 | GenericDef::ImplBlock(_) | GenericDef::Const(_) => None, | 83 | GenericDef::ImplBlock(_) => None, |
83 | }; | 84 | }; |
84 | let mut generics = GenericParams { | 85 | let mut generics = GenericParams { |
85 | def, | 86 | def, |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 152bc71bd..a4ca59bba 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -27,9 +27,9 @@ use crate::{ | |||
27 | }, | 27 | }, |
28 | ids::LocationCtx, | 28 | ids::LocationCtx, |
29 | resolve::{ScopeDef, TypeNs, ValueNs}, | 29 | resolve::{ScopeDef, TypeNs, ValueNs}, |
30 | ty::method_resolution::implements_trait, | 30 | ty::method_resolution::{self, implements_trait}, |
31 | Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, MacroDef, Module, | 31 | AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, |
32 | Name, Path, Resolver, Static, Struct, Ty, | 32 | MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty, |
33 | }; | 33 | }; |
34 | 34 | ||
35 | fn try_get_resolver_for_node( | 35 | fn try_get_resolver_for_node( |
@@ -255,7 +255,9 @@ impl SourceAnalyzer { | |||
255 | 255 | ||
256 | let items = | 256 | let items = |
257 | self.resolver.resolve_module_path(db, &path).take_types().map(PathResolution::Def); | 257 | self.resolver.resolve_module_path(db, &path).take_types().map(PathResolution::Def); |
258 | types.or(values).or(items) | 258 | types.or(values).or(items).or_else(|| { |
259 | self.resolver.resolve_path_as_macro(db, &path).map(|def| PathResolution::Macro(def)) | ||
260 | }) | ||
259 | } | 261 | } |
260 | 262 | ||
261 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { | 263 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { |
@@ -325,16 +327,42 @@ impl SourceAnalyzer { | |||
325 | db: &impl HirDatabase, | 327 | db: &impl HirDatabase, |
326 | ty: Ty, | 328 | ty: Ty, |
327 | name: Option<&Name>, | 329 | name: Option<&Name>, |
328 | callback: impl FnMut(&Ty, Function) -> Option<T>, | 330 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, |
331 | ) -> Option<T> { | ||
332 | // There should be no inference vars in types passed here | ||
333 | // FIXME check that? | ||
334 | // FIXME replace Unknown by bound vars here | ||
335 | let canonical = crate::ty::Canonical { value: ty, num_vars: 0 }; | ||
336 | method_resolution::iterate_method_candidates( | ||
337 | &canonical, | ||
338 | db, | ||
339 | &self.resolver, | ||
340 | name, | ||
341 | method_resolution::LookupMode::MethodCall, | ||
342 | |ty, it| match it { | ||
343 | AssocItem::Function(f) => callback(ty, f), | ||
344 | _ => None, | ||
345 | }, | ||
346 | ) | ||
347 | } | ||
348 | |||
349 | pub fn iterate_path_candidates<T>( | ||
350 | &self, | ||
351 | db: &impl HirDatabase, | ||
352 | ty: Ty, | ||
353 | name: Option<&Name>, | ||
354 | callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
329 | ) -> Option<T> { | 355 | ) -> Option<T> { |
330 | // There should be no inference vars in types passed here | 356 | // There should be no inference vars in types passed here |
331 | // FIXME check that? | 357 | // FIXME check that? |
358 | // FIXME replace Unknown by bound vars here | ||
332 | let canonical = crate::ty::Canonical { value: ty, num_vars: 0 }; | 359 | let canonical = crate::ty::Canonical { value: ty, num_vars: 0 }; |
333 | crate::ty::method_resolution::iterate_method_candidates( | 360 | method_resolution::iterate_method_candidates( |
334 | &canonical, | 361 | &canonical, |
335 | db, | 362 | db, |
336 | &self.resolver, | 363 | &self.resolver, |
337 | name, | 364 | name, |
365 | method_resolution::LookupMode::Path, | ||
338 | callback, | 366 | callback, |
339 | ) | 367 | ) |
340 | } | 368 | } |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d2bfcdc7d..d1a9d7411 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -385,13 +385,22 @@ impl SubstsBuilder { | |||
385 | self.param_count - self.vec.len() | 385 | self.param_count - self.vec.len() |
386 | } | 386 | } |
387 | 387 | ||
388 | pub fn fill_with_bound_vars(mut self, starting_from: u32) -> Self { | 388 | pub fn fill_with_bound_vars(self, starting_from: u32) -> Self { |
389 | self.vec.extend((starting_from..starting_from + self.remaining() as u32).map(Ty::Bound)); | 389 | self.fill((starting_from..).map(Ty::Bound)) |
390 | self | 390 | } |
391 | |||
392 | pub fn fill_with_params(self) -> Self { | ||
393 | let start = self.vec.len() as u32; | ||
394 | self.fill((start..).map(|idx| Ty::Param { idx, name: Name::missing() })) | ||
395 | } | ||
396 | |||
397 | pub fn fill_with_unknown(self) -> Self { | ||
398 | self.fill(iter::repeat(Ty::Unknown)) | ||
391 | } | 399 | } |
392 | 400 | ||
393 | pub fn fill_with_unknown(mut self) -> Self { | 401 | pub fn fill(mut self, filler: impl Iterator<Item = Ty>) -> Self { |
394 | self.vec.extend(iter::repeat(Ty::Unknown).take(self.remaining())); | 402 | self.vec.extend(filler.take(self.remaining())); |
403 | assert_eq!(self.remaining(), 0); | ||
395 | self | 404 | self |
396 | } | 405 | } |
397 | 406 | ||
diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs index 77aa35ce1..59b7f7eb6 100644 --- a/crates/ra_hir/src/ty/infer/path.rs +++ b/crates/ra_hir/src/ty/infer/path.rs | |||
@@ -6,8 +6,8 @@ use super::{ExprOrPatId, InferenceContext, TraitRef}; | |||
6 | use crate::{ | 6 | use crate::{ |
7 | db::HirDatabase, | 7 | db::HirDatabase, |
8 | resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, | 8 | resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, |
9 | ty::{Substs, Ty, TypableDef, TypeWalk}, | 9 | ty::{method_resolution, Substs, Ty, TypableDef, TypeWalk}, |
10 | AssocItem, HasGenericParams, Namespace, Path, | 10 | AssocItem, Container, HasGenericParams, Name, Namespace, Path, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { | 13 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { |
@@ -39,7 +39,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
39 | let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty); | 39 | let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty); |
40 | self.resolve_ty_assoc_item( | 40 | self.resolve_ty_assoc_item( |
41 | ty, | 41 | ty, |
42 | path.segments.last().expect("path had at least one segment"), | 42 | &path.segments.last().expect("path had at least one segment").name, |
43 | id, | 43 | id, |
44 | )? | 44 | )? |
45 | } else { | 45 | } else { |
@@ -122,10 +122,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
122 | return None; | 122 | return None; |
123 | } | 123 | } |
124 | 124 | ||
125 | let ty = self.insert_type_vars(ty); | ||
126 | let ty = self.normalize_associated_types_in(ty); | ||
127 | |||
125 | let segment = | 128 | let segment = |
126 | remaining_segments.last().expect("there should be at least one segment here"); | 129 | remaining_segments.last().expect("there should be at least one segment here"); |
127 | 130 | ||
128 | self.resolve_ty_assoc_item(ty, segment, id) | 131 | self.resolve_ty_assoc_item(ty, &segment.name, id) |
129 | } | 132 | } |
130 | } | 133 | } |
131 | } | 134 | } |
@@ -162,7 +165,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
162 | }; | 165 | }; |
163 | let substs = Substs::build_for_def(self.db, item) | 166 | let substs = Substs::build_for_def(self.db, item) |
164 | .use_parent_substs(&trait_ref.substs) | 167 | .use_parent_substs(&trait_ref.substs) |
165 | .fill_with_unknown() | 168 | .fill_with_params() |
166 | .build(); | 169 | .build(); |
167 | 170 | ||
168 | self.write_assoc_resolution(id, item); | 171 | self.write_assoc_resolution(id, item); |
@@ -172,44 +175,51 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
172 | fn resolve_ty_assoc_item( | 175 | fn resolve_ty_assoc_item( |
173 | &mut self, | 176 | &mut self, |
174 | ty: Ty, | 177 | ty: Ty, |
175 | segment: &PathSegment, | 178 | name: &Name, |
176 | id: ExprOrPatId, | 179 | id: ExprOrPatId, |
177 | ) -> Option<(ValueNs, Option<Substs>)> { | 180 | ) -> Option<(ValueNs, Option<Substs>)> { |
178 | if let Ty::Unknown = ty { | 181 | if let Ty::Unknown = ty { |
179 | return None; | 182 | return None; |
180 | } | 183 | } |
181 | 184 | ||
182 | let krate = self.resolver.krate()?; | 185 | let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); |
183 | 186 | ||
184 | // Find impl | 187 | method_resolution::iterate_method_candidates( |
185 | // FIXME: consider trait candidates | 188 | &canonical_ty.value, |
186 | let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item { | 189 | self.db, |
187 | AssocItem::Function(func) => { | 190 | &self.resolver.clone(), |
188 | if segment.name == func.name(self.db) { | 191 | Some(name), |
189 | Some(AssocItem::Function(func)) | 192 | method_resolution::LookupMode::Path, |
190 | } else { | 193 | move |_ty, item| { |
191 | None | 194 | let def = match item { |
192 | } | 195 | AssocItem::Function(f) => ValueNs::Function(f), |
193 | } | 196 | AssocItem::Const(c) => ValueNs::Const(c), |
194 | 197 | AssocItem::TypeAlias(_) => unreachable!(), | |
195 | AssocItem::Const(konst) => { | 198 | }; |
196 | if konst.name(self.db).map_or(false, |n| n == segment.name) { | 199 | let substs = match item.container(self.db) { |
197 | Some(AssocItem::Const(konst)) | 200 | Container::ImplBlock(_) => self.find_self_types(&def, ty.clone()), |
198 | } else { | 201 | Container::Trait(t) => { |
199 | None | 202 | // we're picking this method |
200 | } | 203 | let trait_substs = Substs::build_for_def(self.db, t) |
201 | } | 204 | .push(ty.clone()) |
202 | AssocItem::TypeAlias(_) => None, | 205 | .fill(std::iter::repeat_with(|| self.new_type_var())) |
203 | })?; | 206 | .build(); |
204 | let def = match item { | 207 | let substs = Substs::build_for_def(self.db, item) |
205 | AssocItem::Function(f) => ValueNs::Function(f), | 208 | .use_parent_substs(&trait_substs) |
206 | AssocItem::Const(c) => ValueNs::Const(c), | 209 | .fill_with_params() |
207 | AssocItem::TypeAlias(_) => unreachable!(), | 210 | .build(); |
208 | }; | 211 | self.obligations.push(super::Obligation::Trait(TraitRef { |
209 | let substs = self.find_self_types(&def, ty); | 212 | trait_: t, |
213 | substs: trait_substs, | ||
214 | })); | ||
215 | Some(substs) | ||
216 | } | ||
217 | }; | ||
210 | 218 | ||
211 | self.write_assoc_resolution(id, item); | 219 | self.write_assoc_resolution(id, item); |
212 | Some((def, substs)) | 220 | Some((def, substs)) |
221 | }, | ||
222 | ) | ||
213 | } | 223 | } |
214 | 224 | ||
215 | fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> { | 225 | fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> { |
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index eb69344f6..8c3d32d09 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -166,37 +166,78 @@ pub(crate) fn lookup_method( | |||
166 | name: &Name, | 166 | name: &Name, |
167 | resolver: &Resolver, | 167 | resolver: &Resolver, |
168 | ) -> Option<(Ty, Function)> { | 168 | ) -> Option<(Ty, Function)> { |
169 | iterate_method_candidates(ty, db, resolver, Some(name), |ty, f| Some((ty.clone(), f))) | 169 | iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| match f |
170 | { | ||
171 | AssocItem::Function(f) => Some((ty.clone(), f)), | ||
172 | _ => None, | ||
173 | }) | ||
174 | } | ||
175 | |||
176 | /// Whether we're looking up a dotted method call (like `v.len()`) or a path | ||
177 | /// (like `Vec::new`). | ||
178 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
179 | pub enum LookupMode { | ||
180 | /// Looking up a method call like `v.len()`: We only consider candidates | ||
181 | /// that have a `self` parameter, and do autoderef. | ||
182 | MethodCall, | ||
183 | /// Looking up a path like `Vec::new` or `Vec::default`: We consider all | ||
184 | /// candidates including associated constants, but don't do autoderef. | ||
185 | Path, | ||
170 | } | 186 | } |
171 | 187 | ||
172 | // This would be nicer if it just returned an iterator, but that runs into | 188 | // This would be nicer if it just returned an iterator, but that runs into |
173 | // lifetime problems, because we need to borrow temp `CrateImplBlocks`. | 189 | // lifetime problems, because we need to borrow temp `CrateImplBlocks`. |
190 | // FIXME add a context type here? | ||
174 | pub(crate) fn iterate_method_candidates<T>( | 191 | pub(crate) fn iterate_method_candidates<T>( |
175 | ty: &Canonical<Ty>, | 192 | ty: &Canonical<Ty>, |
176 | db: &impl HirDatabase, | 193 | db: &impl HirDatabase, |
177 | resolver: &Resolver, | 194 | resolver: &Resolver, |
178 | name: Option<&Name>, | 195 | name: Option<&Name>, |
179 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 196 | mode: LookupMode, |
197 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
180 | ) -> Option<T> { | 198 | ) -> Option<T> { |
181 | // For method calls, rust first does any number of autoderef, and then one | 199 | let krate = resolver.krate()?; |
182 | // autoref (i.e. when the method takes &self or &mut self). We just ignore | 200 | match mode { |
183 | // the autoref currently -- when we find a method matching the given name, | 201 | LookupMode::MethodCall => { |
184 | // we assume it fits. | 202 | // For method calls, rust first does any number of autoderef, and then one |
203 | // autoref (i.e. when the method takes &self or &mut self). We just ignore | ||
204 | // the autoref currently -- when we find a method matching the given name, | ||
205 | // we assume it fits. | ||
185 | 206 | ||
186 | // Also note that when we've got a receiver like &S, even if the method we | 207 | // Also note that when we've got a receiver like &S, even if the method we |
187 | // find in the end takes &self, we still do the autoderef step (just as | 208 | // find in the end takes &self, we still do the autoderef step (just as |
188 | // rustc does an autoderef and then autoref again). | 209 | // rustc does an autoderef and then autoref again). |
189 | 210 | ||
190 | let krate = resolver.krate()?; | 211 | for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { |
191 | for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { | 212 | if let Some(result) = |
192 | if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback) | 213 | iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback) |
193 | { | 214 | { |
194 | return Some(result); | 215 | return Some(result); |
216 | } | ||
217 | if let Some(result) = iterate_trait_method_candidates( | ||
218 | &derefed_ty, | ||
219 | db, | ||
220 | resolver, | ||
221 | name, | ||
222 | mode, | ||
223 | &mut callback, | ||
224 | ) { | ||
225 | return Some(result); | ||
226 | } | ||
227 | } | ||
195 | } | 228 | } |
196 | if let Some(result) = | 229 | LookupMode::Path => { |
197 | iterate_trait_method_candidates(&derefed_ty, db, resolver, name, &mut callback) | 230 | // No autoderef for path lookups |
198 | { | 231 | if let Some(result) = |
199 | return Some(result); | 232 | iterate_inherent_methods(&ty, db, name, mode, krate, &mut callback) |
233 | { | ||
234 | return Some(result); | ||
235 | } | ||
236 | if let Some(result) = | ||
237 | iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback) | ||
238 | { | ||
239 | return Some(result); | ||
240 | } | ||
200 | } | 241 | } |
201 | } | 242 | } |
202 | None | 243 | None |
@@ -207,7 +248,8 @@ fn iterate_trait_method_candidates<T>( | |||
207 | db: &impl HirDatabase, | 248 | db: &impl HirDatabase, |
208 | resolver: &Resolver, | 249 | resolver: &Resolver, |
209 | name: Option<&Name>, | 250 | name: Option<&Name>, |
210 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 251 | mode: LookupMode, |
252 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
211 | ) -> Option<T> { | 253 | ) -> Option<T> { |
212 | let krate = resolver.krate()?; | 254 | let krate = resolver.krate()?; |
213 | // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) | 255 | // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) |
@@ -231,22 +273,20 @@ fn iterate_trait_method_candidates<T>( | |||
231 | // trait, but if we find out it doesn't, we'll skip the rest of the | 273 | // trait, but if we find out it doesn't, we'll skip the rest of the |
232 | // iteration | 274 | // iteration |
233 | let mut known_implemented = inherently_implemented; | 275 | let mut known_implemented = inherently_implemented; |
234 | for item in data.items() { | 276 | for &item in data.items() { |
235 | if let AssocItem::Function(m) = *item { | 277 | if !is_valid_candidate(db, name, mode, item) { |
236 | let data = m.data(db); | 278 | continue; |
237 | if name.map_or(true, |name| data.name() == name) && data.has_self_param() { | 279 | } |
238 | if !known_implemented { | 280 | if !known_implemented { |
239 | let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); | 281 | let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); |
240 | if db.trait_solve(krate, goal).is_none() { | 282 | if db.trait_solve(krate, goal).is_none() { |
241 | continue 'traits; | 283 | continue 'traits; |
242 | } | ||
243 | } | ||
244 | known_implemented = true; | ||
245 | if let Some(result) = callback(&ty.value, m) { | ||
246 | return Some(result); | ||
247 | } | ||
248 | } | 284 | } |
249 | } | 285 | } |
286 | known_implemented = true; | ||
287 | if let Some(result) = callback(&ty.value, item) { | ||
288 | return Some(result); | ||
289 | } | ||
250 | } | 290 | } |
251 | } | 291 | } |
252 | None | 292 | None |
@@ -256,21 +296,20 @@ fn iterate_inherent_methods<T>( | |||
256 | ty: &Canonical<Ty>, | 296 | ty: &Canonical<Ty>, |
257 | db: &impl HirDatabase, | 297 | db: &impl HirDatabase, |
258 | name: Option<&Name>, | 298 | name: Option<&Name>, |
299 | mode: LookupMode, | ||
259 | krate: Crate, | 300 | krate: Crate, |
260 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 301 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, |
261 | ) -> Option<T> { | 302 | ) -> Option<T> { |
262 | for krate in def_crates(db, krate, &ty.value)? { | 303 | for krate in def_crates(db, krate, &ty.value)? { |
263 | let impls = db.impls_in_crate(krate); | 304 | let impls = db.impls_in_crate(krate); |
264 | 305 | ||
265 | for impl_block in impls.lookup_impl_blocks(&ty.value) { | 306 | for impl_block in impls.lookup_impl_blocks(&ty.value) { |
266 | for item in impl_block.items(db) { | 307 | for item in impl_block.items(db) { |
267 | if let AssocItem::Function(f) = item { | 308 | if !is_valid_candidate(db, name, mode, item) { |
268 | let data = f.data(db); | 309 | continue; |
269 | if name.map_or(true, |name| data.name() == name) && data.has_self_param() { | 310 | } |
270 | if let Some(result) = callback(&ty.value, f) { | 311 | if let Some(result) = callback(&ty.value, item) { |
271 | return Some(result); | 312 | return Some(result); |
272 | } | ||
273 | } | ||
274 | } | 313 | } |
275 | } | 314 | } |
276 | } | 315 | } |
@@ -278,6 +317,26 @@ fn iterate_inherent_methods<T>( | |||
278 | None | 317 | None |
279 | } | 318 | } |
280 | 319 | ||
320 | fn is_valid_candidate( | ||
321 | db: &impl HirDatabase, | ||
322 | name: Option<&Name>, | ||
323 | mode: LookupMode, | ||
324 | item: AssocItem, | ||
325 | ) -> bool { | ||
326 | match item { | ||
327 | AssocItem::Function(m) => { | ||
328 | let data = m.data(db); | ||
329 | name.map_or(true, |name| data.name() == name) | ||
330 | && (data.has_self_param() || mode == LookupMode::Path) | ||
331 | } | ||
332 | AssocItem::Const(c) => { | ||
333 | name.map_or(true, |name| Some(name) == c.name(db).as_ref()) | ||
334 | && (mode == LookupMode::Path) | ||
335 | } | ||
336 | _ => false, | ||
337 | } | ||
338 | } | ||
339 | |||
281 | pub(crate) fn implements_trait( | 340 | pub(crate) fn implements_trait( |
282 | ty: &Canonical<Ty>, | 341 | ty: &Canonical<Ty>, |
283 | db: &impl HirDatabase, | 342 | db: &impl HirDatabase, |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index c12326643..bfef48b16 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -1841,8 +1841,8 @@ fn test() { | |||
1841 | [243; 254) 'Struct::FOO': u32 | 1841 | [243; 254) 'Struct::FOO': u32 |
1842 | [264; 265) 'y': u32 | 1842 | [264; 265) 'y': u32 |
1843 | [268; 277) 'Enum::BAR': u32 | 1843 | [268; 277) 'Enum::BAR': u32 |
1844 | [287; 288) 'z': {unknown} | 1844 | [287; 288) 'z': u32 |
1845 | [291; 304) 'TraitTest::ID': {unknown} | 1845 | [291; 304) 'TraitTest::ID': u32 |
1846 | "### | 1846 | "### |
1847 | ); | 1847 | ); |
1848 | } | 1848 | } |
@@ -2782,9 +2782,9 @@ fn test() { | |||
2782 | [97; 99) 's1': S | 2782 | [97; 99) 's1': S |
2783 | [105; 121) 'Defaul...efault': fn default<S>() -> Self | 2783 | [105; 121) 'Defaul...efault': fn default<S>() -> Self |
2784 | [105; 123) 'Defaul...ault()': S | 2784 | [105; 123) 'Defaul...ault()': S |
2785 | [133; 135) 's2': {unknown} | 2785 | [133; 135) 's2': S |
2786 | [138; 148) 'S::default': {unknown} | 2786 | [138; 148) 'S::default': fn default<S>() -> Self |
2787 | [138; 150) 'S::default()': {unknown} | 2787 | [138; 150) 'S::default()': S |
2788 | [160; 162) 's3': S | 2788 | [160; 162) 's3': S |
2789 | [165; 188) '<S as ...efault': fn default<S>() -> Self | 2789 | [165; 188) '<S as ...efault': fn default<S>() -> Self |
2790 | [165; 190) '<S as ...ault()': S | 2790 | [165; 190) '<S as ...ault()': S |
@@ -2793,6 +2793,153 @@ fn test() { | |||
2793 | } | 2793 | } |
2794 | 2794 | ||
2795 | #[test] | 2795 | #[test] |
2796 | fn infer_trait_assoc_method_generics_1() { | ||
2797 | assert_snapshot!( | ||
2798 | infer(r#" | ||
2799 | trait Trait<T> { | ||
2800 | fn make() -> T; | ||
2801 | } | ||
2802 | struct S; | ||
2803 | impl Trait<u32> for S {} | ||
2804 | struct G<T>; | ||
2805 | impl<T> Trait<T> for G<T> {} | ||
2806 | fn test() { | ||
2807 | let a = S::make(); | ||
2808 | let b = G::<u64>::make(); | ||
2809 | let c: f64 = G::make(); | ||
2810 | } | ||
2811 | "#), | ||
2812 | @r###" | ||
2813 | [127; 211) '{ ...e(); }': () | ||
2814 | [137; 138) 'a': u32 | ||
2815 | [141; 148) 'S::make': fn make<S, u32>() -> T | ||
2816 | [141; 150) 'S::make()': u32 | ||
2817 | [160; 161) 'b': u64 | ||
2818 | [164; 178) 'G::<u64>::make': fn make<G<u64>, u64>() -> T | ||
2819 | [164; 180) 'G::<u6...make()': u64 | ||
2820 | [190; 191) 'c': f64 | ||
2821 | [199; 206) 'G::make': fn make<G<f64>, f64>() -> T | ||
2822 | [199; 208) 'G::make()': f64 | ||
2823 | "### | ||
2824 | ); | ||
2825 | } | ||
2826 | |||
2827 | #[test] | ||
2828 | fn infer_trait_assoc_method_generics_2() { | ||
2829 | assert_snapshot!( | ||
2830 | infer(r#" | ||
2831 | trait Trait<T> { | ||
2832 | fn make<U>() -> (T, U); | ||
2833 | } | ||
2834 | struct S; | ||
2835 | impl Trait<u32> for S {} | ||
2836 | struct G<T>; | ||
2837 | impl<T> Trait<T> for G<T> {} | ||
2838 | fn test() { | ||
2839 | let a = S::make::<i64>(); | ||
2840 | let b: (_, i64) = S::make(); | ||
2841 | let c = G::<u32>::make::<i64>(); | ||
2842 | let d: (u32, _) = G::make::<i64>(); | ||
2843 | let e: (u32, i64) = G::make(); | ||
2844 | } | ||
2845 | "#), | ||
2846 | @r###" | ||
2847 | [135; 313) '{ ...e(); }': () | ||
2848 | [145; 146) 'a': (u32, i64) | ||
2849 | [149; 163) 'S::make::<i64>': fn make<S, u32, i64>() -> (T, U) | ||
2850 | [149; 165) 'S::mak...i64>()': (u32, i64) | ||
2851 | [175; 176) 'b': (u32, i64) | ||
2852 | [189; 196) 'S::make': fn make<S, u32, i64>() -> (T, U) | ||
2853 | [189; 198) 'S::make()': (u32, i64) | ||
2854 | [208; 209) 'c': (u32, i64) | ||
2855 | [212; 233) 'G::<u3...:<i64>': fn make<G<u32>, u32, i64>() -> (T, U) | ||
2856 | [212; 235) 'G::<u3...i64>()': (u32, i64) | ||
2857 | [245; 246) 'd': (u32, i64) | ||
2858 | [259; 273) 'G::make::<i64>': fn make<G<u32>, u32, i64>() -> (T, U) | ||
2859 | [259; 275) 'G::mak...i64>()': (u32, i64) | ||
2860 | [285; 286) 'e': (u32, i64) | ||
2861 | [301; 308) 'G::make': fn make<G<u32>, u32, i64>() -> (T, U) | ||
2862 | [301; 310) 'G::make()': (u32, i64) | ||
2863 | "### | ||
2864 | ); | ||
2865 | } | ||
2866 | |||
2867 | #[test] | ||
2868 | fn infer_trait_assoc_method_generics_3() { | ||
2869 | assert_snapshot!( | ||
2870 | infer(r#" | ||
2871 | trait Trait<T> { | ||
2872 | fn make() -> (Self, T); | ||
2873 | } | ||
2874 | struct S<T>; | ||
2875 | impl Trait<i64> for S<i32> {} | ||
2876 | fn test() { | ||
2877 | let a = S::make(); | ||
2878 | } | ||
2879 | "#), | ||
2880 | @r###" | ||
2881 | [101; 127) '{ ...e(); }': () | ||
2882 | [111; 112) 'a': (S<i32>, i64) | ||
2883 | [115; 122) 'S::make': fn make<S<i32>, i64>() -> (Self, T) | ||
2884 | [115; 124) 'S::make()': (S<i32>, i64) | ||
2885 | "### | ||
2886 | ); | ||
2887 | } | ||
2888 | |||
2889 | #[test] | ||
2890 | fn infer_trait_assoc_method_generics_4() { | ||
2891 | assert_snapshot!( | ||
2892 | infer(r#" | ||
2893 | trait Trait<T> { | ||
2894 | fn make() -> (Self, T); | ||
2895 | } | ||
2896 | struct S<T>; | ||
2897 | impl Trait<i64> for S<u64> {} | ||
2898 | impl Trait<i32> for S<u32> {} | ||
2899 | fn test() { | ||
2900 | let a: (S<u64>, _) = S::make(); | ||
2901 | let b: (_, i32) = S::make(); | ||
2902 | } | ||
2903 | "#), | ||
2904 | @r###" | ||
2905 | [131; 203) '{ ...e(); }': () | ||
2906 | [141; 142) 'a': (S<u64>, i64) | ||
2907 | [158; 165) 'S::make': fn make<S<u64>, i64>() -> (Self, T) | ||
2908 | [158; 167) 'S::make()': (S<u64>, i64) | ||
2909 | [177; 178) 'b': (S<u32>, i32) | ||
2910 | [191; 198) 'S::make': fn make<S<u32>, i32>() -> (Self, T) | ||
2911 | [191; 200) 'S::make()': (S<u32>, i32) | ||
2912 | "### | ||
2913 | ); | ||
2914 | } | ||
2915 | |||
2916 | #[test] | ||
2917 | fn infer_trait_assoc_method_generics_5() { | ||
2918 | assert_snapshot!( | ||
2919 | infer(r#" | ||
2920 | trait Trait<T> { | ||
2921 | fn make<U>() -> (Self, T, U); | ||
2922 | } | ||
2923 | struct S<T>; | ||
2924 | impl Trait<i64> for S<u64> {} | ||
2925 | fn test() { | ||
2926 | let a = <S as Trait<i64>>::make::<u8>(); | ||
2927 | let b: (S<u64>, _, _) = Trait::<i64>::make::<u8>(); | ||
2928 | } | ||
2929 | "#), | ||
2930 | @r###" | ||
2931 | [107; 211) '{ ...>(); }': () | ||
2932 | [117; 118) 'a': (S<u64>, i64, u8) | ||
2933 | [121; 150) '<S as ...::<u8>': fn make<S<u64>, i64, u8>() -> (Self, T, U) | ||
2934 | [121; 152) '<S as ...<u8>()': (S<u64>, i64, u8) | ||
2935 | [162; 163) 'b': (S<u64>, i64, u8) | ||
2936 | [182; 206) 'Trait:...::<u8>': fn make<S<u64>, i64, u8>() -> (Self, T, U) | ||
2937 | [182; 208) 'Trait:...<u8>()': (S<u64>, i64, u8) | ||
2938 | "### | ||
2939 | ); | ||
2940 | } | ||
2941 | |||
2942 | #[test] | ||
2796 | fn infer_from_bound_1() { | 2943 | fn infer_from_bound_1() { |
2797 | assert_snapshot!( | 2944 | assert_snapshot!( |
2798 | infer(r#" | 2945 | infer(r#" |
@@ -3303,6 +3450,22 @@ fn test() { S.foo()<|>; } | |||
3303 | assert_eq!(t, "u128"); | 3450 | assert_eq!(t, "u128"); |
3304 | } | 3451 | } |
3305 | 3452 | ||
3453 | #[ignore] | ||
3454 | #[test] | ||
3455 | fn method_resolution_by_value_before_autoref() { | ||
3456 | let t = type_at( | ||
3457 | r#" | ||
3458 | //- /main.rs | ||
3459 | trait Clone { fn clone(&self) -> Self; } | ||
3460 | struct S; | ||
3461 | impl Clone for S {} | ||
3462 | impl Clone for &S {} | ||
3463 | fn test() { (S.clone(), (&S).clone(), (&&S).clone())<|>; } | ||
3464 | "#, | ||
3465 | ); | ||
3466 | assert_eq!(t, "(S, S, &S)"); | ||
3467 | } | ||
3468 | |||
3306 | #[test] | 3469 | #[test] |
3307 | fn method_resolution_trait_before_autoderef() { | 3470 | fn method_resolution_trait_before_autoderef() { |
3308 | let t = type_at( | 3471 | let t = type_at( |
diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index a58fdc036..9ac9768af 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs | |||
@@ -50,23 +50,46 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
50 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), | 50 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), |
51 | _ => unreachable!(), | 51 | _ => unreachable!(), |
52 | }; | 52 | }; |
53 | ctx.analyzer.iterate_path_candidates(ctx.db, ty.clone(), None, |_ty, item| { | ||
54 | match item { | ||
55 | hir::AssocItem::Function(func) => { | ||
56 | let data = func.data(ctx.db); | ||
57 | if !data.has_self_param() { | ||
58 | acc.add_function(ctx, func); | ||
59 | } | ||
60 | } | ||
61 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
62 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
63 | } | ||
64 | None::<()> | ||
65 | }); | ||
66 | // Iterate assoc types separately | ||
67 | // FIXME: complete T::AssocType | ||
53 | let krate = ctx.module.map(|m| m.krate()); | 68 | let krate = ctx.module.map(|m| m.krate()); |
54 | if let Some(krate) = krate { | 69 | if let Some(krate) = krate { |
55 | ty.iterate_impl_items(ctx.db, krate, |item| { | 70 | ty.iterate_impl_items(ctx.db, krate, |item| { |
56 | match item { | 71 | match item { |
57 | hir::AssocItem::Function(func) => { | 72 | hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} |
58 | let data = func.data(ctx.db); | ||
59 | if !data.has_self_param() { | ||
60 | acc.add_function(ctx, func); | ||
61 | } | ||
62 | } | ||
63 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
64 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | 73 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), |
65 | } | 74 | } |
66 | None::<()> | 75 | None::<()> |
67 | }); | 76 | }); |
68 | } | 77 | } |
69 | } | 78 | } |
79 | hir::ModuleDef::Trait(t) => { | ||
80 | for item in t.items(ctx.db) { | ||
81 | match item { | ||
82 | hir::AssocItem::Function(func) => { | ||
83 | let data = func.data(ctx.db); | ||
84 | if !data.has_self_param() { | ||
85 | acc.add_function(ctx, func); | ||
86 | } | ||
87 | } | ||
88 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
89 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
90 | } | ||
91 | } | ||
92 | } | ||
70 | _ => {} | 93 | _ => {} |
71 | }; | 94 | }; |
72 | } | 95 | } |
@@ -559,6 +582,111 @@ mod tests { | |||
559 | } | 582 | } |
560 | 583 | ||
561 | #[test] | 584 | #[test] |
585 | fn completes_trait_associated_method_1() { | ||
586 | assert_debug_snapshot!( | ||
587 | do_reference_completion( | ||
588 | " | ||
589 | //- /lib.rs | ||
590 | trait Trait { | ||
591 | /// A trait method | ||
592 | fn m(); | ||
593 | } | ||
594 | |||
595 | fn foo() { let _ = Trait::<|> } | ||
596 | " | ||
597 | ), | ||
598 | @r###" | ||
599 | [ | ||
600 | CompletionItem { | ||
601 | label: "m()", | ||
602 | source_range: [73; 73), | ||
603 | delete: [73; 73), | ||
604 | insert: "m()$0", | ||
605 | kind: Function, | ||
606 | lookup: "m", | ||
607 | detail: "fn m()", | ||
608 | documentation: Documentation( | ||
609 | "A trait method", | ||
610 | ), | ||
611 | }, | ||
612 | ] | ||
613 | "### | ||
614 | ); | ||
615 | } | ||
616 | |||
617 | #[test] | ||
618 | fn completes_trait_associated_method_2() { | ||
619 | assert_debug_snapshot!( | ||
620 | do_reference_completion( | ||
621 | " | ||
622 | //- /lib.rs | ||
623 | trait Trait { | ||
624 | /// A trait method | ||
625 | fn m(); | ||
626 | } | ||
627 | |||
628 | struct S; | ||
629 | impl Trait for S {} | ||
630 | |||
631 | fn foo() { let _ = S::<|> } | ||
632 | " | ||
633 | ), | ||
634 | @r###" | ||
635 | [ | ||
636 | CompletionItem { | ||
637 | label: "m()", | ||
638 | source_range: [99; 99), | ||
639 | delete: [99; 99), | ||
640 | insert: "m()$0", | ||
641 | kind: Function, | ||
642 | lookup: "m", | ||
643 | detail: "fn m()", | ||
644 | documentation: Documentation( | ||
645 | "A trait method", | ||
646 | ), | ||
647 | }, | ||
648 | ] | ||
649 | "### | ||
650 | ); | ||
651 | } | ||
652 | |||
653 | #[test] | ||
654 | fn completes_trait_associated_method_3() { | ||
655 | assert_debug_snapshot!( | ||
656 | do_reference_completion( | ||
657 | " | ||
658 | //- /lib.rs | ||
659 | trait Trait { | ||
660 | /// A trait method | ||
661 | fn m(); | ||
662 | } | ||
663 | |||
664 | struct S; | ||
665 | impl Trait for S {} | ||
666 | |||
667 | fn foo() { let _ = <S as Trait>::<|> } | ||
668 | " | ||
669 | ), | ||
670 | @r###" | ||
671 | [ | ||
672 | CompletionItem { | ||
673 | label: "m()", | ||
674 | source_range: [110; 110), | ||
675 | delete: [110; 110), | ||
676 | insert: "m()$0", | ||
677 | kind: Function, | ||
678 | lookup: "m", | ||
679 | detail: "fn m()", | ||
680 | documentation: Documentation( | ||
681 | "A trait method", | ||
682 | ), | ||
683 | }, | ||
684 | ] | ||
685 | "### | ||
686 | ); | ||
687 | } | ||
688 | |||
689 | #[test] | ||
562 | fn completes_type_alias() { | 690 | fn completes_type_alias() { |
563 | assert_debug_snapshot!( | 691 | assert_debug_snapshot!( |
564 | do_reference_completion( | 692 | do_reference_completion( |
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 1f3fa6c57..c1ce54bea 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -316,6 +316,25 @@ mod tests { | |||
316 | } | 316 | } |
317 | 317 | ||
318 | #[test] | 318 | #[test] |
319 | fn goto_definition_works_for_macros_in_use_tree() { | ||
320 | check_goto( | ||
321 | " | ||
322 | //- /lib.rs | ||
323 | use foo::foo<|>; | ||
324 | |||
325 | //- /foo/lib.rs | ||
326 | #[macro_export] | ||
327 | macro_rules! foo { | ||
328 | () => { | ||
329 | {} | ||
330 | }; | ||
331 | } | ||
332 | ", | ||
333 | "foo MACRO_CALL FileId(2) [0; 66) [29; 32)", | ||
334 | ); | ||
335 | } | ||
336 | |||
337 | #[test] | ||
319 | fn goto_definition_works_for_methods() { | 338 | fn goto_definition_works_for_methods() { |
320 | covers!(goto_definition_works_for_methods); | 339 | covers!(goto_definition_works_for_methods); |
321 | check_goto( | 340 | check_goto( |
@@ -371,6 +390,61 @@ mod tests { | |||
371 | "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", | 390 | "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", |
372 | ); | 391 | ); |
373 | } | 392 | } |
393 | |||
394 | #[test] | ||
395 | fn goto_definition_works_for_ufcs_inherent_methods() { | ||
396 | check_goto( | ||
397 | " | ||
398 | //- /lib.rs | ||
399 | struct Foo; | ||
400 | impl Foo { | ||
401 | fn frobnicate() { } | ||
402 | } | ||
403 | |||
404 | fn bar(foo: &Foo) { | ||
405 | Foo::frobnicate<|>(); | ||
406 | } | ||
407 | ", | ||
408 | "frobnicate FN_DEF FileId(1) [27; 47) [30; 40)", | ||
409 | ); | ||
410 | } | ||
411 | |||
412 | #[test] | ||
413 | fn goto_definition_works_for_ufcs_trait_methods_through_traits() { | ||
414 | check_goto( | ||
415 | " | ||
416 | //- /lib.rs | ||
417 | trait Foo { | ||
418 | fn frobnicate(); | ||
419 | } | ||
420 | |||
421 | fn bar() { | ||
422 | Foo::frobnicate<|>(); | ||
423 | } | ||
424 | ", | ||
425 | "frobnicate FN_DEF FileId(1) [16; 32) [19; 29)", | ||
426 | ); | ||
427 | } | ||
428 | |||
429 | #[test] | ||
430 | fn goto_definition_works_for_ufcs_trait_methods_through_self() { | ||
431 | check_goto( | ||
432 | " | ||
433 | //- /lib.rs | ||
434 | struct Foo; | ||
435 | trait Trait { | ||
436 | fn frobnicate(); | ||
437 | } | ||
438 | impl Trait for Foo {} | ||
439 | |||
440 | fn bar() { | ||
441 | Foo::frobnicate<|>(); | ||
442 | } | ||
443 | ", | ||
444 | "frobnicate FN_DEF FileId(1) [30; 46) [33; 43)", | ||
445 | ); | ||
446 | } | ||
447 | |||
374 | #[test] | 448 | #[test] |
375 | fn goto_definition_on_self() { | 449 | fn goto_definition_on_self() { |
376 | check_goto( | 450 | check_goto( |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index a12da5be2..1ec9881b9 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -113,6 +113,20 @@ fn test_doc_comment_of_items() { | |||
113 | } | 113 | } |
114 | 114 | ||
115 | #[test] | 115 | #[test] |
116 | fn test_doc_comment_of_statics() { | ||
117 | let file = SourceFile::parse( | ||
118 | r#" | ||
119 | /// Number of levels | ||
120 | static LEVELS: i32 = 0; | ||
121 | "#, | ||
122 | ) | ||
123 | .ok() | ||
124 | .unwrap(); | ||
125 | let st = file.syntax().descendants().find_map(StaticDef::cast).unwrap(); | ||
126 | assert_eq!("Number of levels", st.doc_comment_text().unwrap()); | ||
127 | } | ||
128 | |||
129 | #[test] | ||
116 | fn test_doc_comment_preserves_indents() { | 130 | fn test_doc_comment_preserves_indents() { |
117 | let file = SourceFile::parse( | 131 | let file = SourceFile::parse( |
118 | r#" | 132 | r#" |
diff --git a/crates/ra_syntax/src/parsing/text_tree_sink.rs b/crates/ra_syntax/src/parsing/text_tree_sink.rs index 142164316..c36756d6c 100644 --- a/crates/ra_syntax/src/parsing/text_tree_sink.rs +++ b/crates/ra_syntax/src/parsing/text_tree_sink.rs | |||
@@ -147,7 +147,7 @@ fn n_attached_trivias<'a>( | |||
147 | ) -> usize { | 147 | ) -> usize { |
148 | match kind { | 148 | match kind { |
149 | MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF | 149 | MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF |
150 | | TRAIT_DEF | MODULE | RECORD_FIELD_DEF => { | 150 | | TRAIT_DEF | MODULE | RECORD_FIELD_DEF | STATIC_DEF => { |
151 | let mut res = 0; | 151 | let mut res = 0; |
152 | for (i, (kind, text)) in trivias.enumerate() { | 152 | for (i, (kind, text)) in trivias.enumerate() { |
153 | match kind { | 153 | match kind { |