]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
d9579d0f | 11 | //! Give useful errors and suggestions to users when an item can't be |
85aaf69f SL |
12 | //! found or is otherwise invalid. |
13 | ||
14 | use CrateCtxt; | |
15 | ||
c30ab7b3 | 16 | use check::FnCtxt; |
54a0048b SL |
17 | use rustc::hir::map as hir_map; |
18 | use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable}; | |
54a0048b | 19 | use hir::def::Def; |
9e0c209e | 20 | use hir::def_id::{CRATE_DEF_INDEX, DefId}; |
62682a34 | 21 | use middle::lang_items::FnOnceTraitLangItem; |
54a0048b | 22 | use rustc::traits::{Obligation, SelectionContext}; |
c30ab7b3 | 23 | use util::nodemap::FnvHashSet; |
85aaf69f | 24 | |
e9174d1e | 25 | use syntax::ast; |
3157f602 XL |
26 | use errors::DiagnosticBuilder; |
27 | use syntax_pos::Span; | |
28 | ||
54a0048b SL |
29 | use rustc::hir::print as pprust; |
30 | use rustc::hir; | |
31 | use rustc::hir::Expr_; | |
85aaf69f SL |
32 | |
33 | use std::cell; | |
34 | use std::cmp::Ordering; | |
35 | ||
a7813a04 | 36 | use super::{MethodError, NoMatchData, CandidateSource}; |
d9579d0f | 37 | use super::probe::Mode; |
85aaf69f | 38 | |
a7813a04 XL |
39 | impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { |
40 | fn is_fn_ty(&self, ty: &Ty<'tcx>, span: Span) -> bool { | |
41 | let tcx = self.tcx; | |
42 | match ty.sty { | |
43 | // Not all of these (e.g. unsafe fns) implement FnOnce | |
44 | // so we look for these beforehand | |
c30ab7b3 SL |
45 | ty::TyClosure(..) | |
46 | ty::TyFnDef(..) | | |
47 | ty::TyFnPtr(_) => true, | |
a7813a04 XL |
48 | // If it's not a simple function, look for things which implement FnOnce |
49 | _ => { | |
3157f602 XL |
50 | let fn_once = match tcx.lang_items.require(FnOnceTraitLangItem) { |
51 | Ok(fn_once) => fn_once, | |
c30ab7b3 | 52 | Err(..) => return false, |
3157f602 | 53 | }; |
a7813a04 | 54 | |
c30ab7b3 SL |
55 | self.autoderef(span, ty).any(|(ty, _)| { |
56 | self.probe(|_| { | |
57 | let fn_once_substs = tcx.mk_substs_trait(ty, &[self.next_ty_var()]); | |
58 | let trait_ref = ty::TraitRef::new(fn_once, fn_once_substs); | |
59 | let poly_trait_ref = trait_ref.to_poly_trait_ref(); | |
60 | let obligation = | |
61 | Obligation::misc(span, self.body_id, poly_trait_ref.to_predicate()); | |
62 | SelectionContext::new(self).evaluate_obligation(&obligation) | |
63 | }) | |
64 | }) | |
54a0048b SL |
65 | } |
66 | } | |
67 | } | |
3157f602 | 68 | |
a7813a04 XL |
69 | pub fn report_method_error(&self, |
70 | span: Span, | |
71 | rcvr_ty: Ty<'tcx>, | |
72 | item_name: ast::Name, | |
73 | rcvr_expr: Option<&hir::Expr>, | |
c30ab7b3 | 74 | error: MethodError<'tcx>) { |
a7813a04 XL |
75 | // avoid suggestions when we don't know what's going on. |
76 | if rcvr_ty.references_error() { | |
c30ab7b3 | 77 | return; |
a7813a04 | 78 | } |
85aaf69f | 79 | |
c30ab7b3 | 80 | let report_candidates = |err: &mut DiagnosticBuilder, mut sources: Vec<CandidateSource>| { |
a7813a04 XL |
81 | |
82 | sources.sort(); | |
83 | sources.dedup(); | |
84 | // Dynamic limit to avoid hiding just one candidate, which is silly. | |
85 | let limit = if sources.len() == 5 { 5 } else { 4 }; | |
86 | ||
87 | for (idx, source) in sources.iter().take(limit).enumerate() { | |
88 | match *source { | |
89 | CandidateSource::ImplSource(impl_did) => { | |
90 | // Provide the best span we can. Use the item, if local to crate, else | |
91 | // the impl, if local to crate (item may be defaulted), else nothing. | |
9e0c209e | 92 | let item = self.impl_or_trait_item(impl_did, item_name) |
a7813a04 | 93 | .or_else(|| { |
c30ab7b3 SL |
94 | self.impl_or_trait_item(self.tcx |
95 | .impl_trait_ref(impl_did) | |
96 | .unwrap() | |
97 | .def_id, | |
98 | ||
99 | item_name) | |
100 | }) | |
101 | .unwrap(); | |
102 | let note_span = self.tcx | |
103 | .map | |
104 | .span_if_local(item.def_id()) | |
105 | .or_else(|| self.tcx.map.span_if_local(impl_did)); | |
a7813a04 XL |
106 | |
107 | let impl_ty = self.impl_self_ty(span, impl_did).ty; | |
108 | ||
109 | let insertion = match self.tcx.impl_trait_ref(impl_did) { | |
110 | None => format!(""), | |
111 | Some(trait_ref) => { | |
112 | format!(" of the trait `{}`", | |
113 | self.tcx.item_path_str(trait_ref.def_id)) | |
114 | } | |
115 | }; | |
116 | ||
117 | let note_str = format!("candidate #{} is defined in an impl{} \ | |
118 | for the type `{}`", | |
119 | idx + 1, | |
120 | insertion, | |
121 | impl_ty); | |
122 | if let Some(note_span) = note_span { | |
123 | // We have a span pointing to the method. Show note with snippet. | |
124 | err.span_note(note_span, ¬e_str); | |
125 | } else { | |
126 | err.note(¬e_str); | |
127 | } | |
128 | } | |
129 | CandidateSource::TraitSource(trait_did) => { | |
9e0c209e | 130 | let item = self.impl_or_trait_item(trait_did, item_name).unwrap(); |
a7813a04 | 131 | let item_span = self.tcx.map.def_id_span(item.def_id(), span); |
c30ab7b3 SL |
132 | span_note!(err, |
133 | item_span, | |
a7813a04 XL |
134 | "candidate #{} is defined in the trait `{}`", |
135 | idx + 1, | |
136 | self.tcx.item_path_str(trait_did)); | |
9cc50fc6 | 137 | } |
54a0048b SL |
138 | } |
139 | } | |
a7813a04 XL |
140 | if sources.len() > limit { |
141 | err.note(&format!("and {} others", sources.len() - limit)); | |
142 | } | |
143 | }; | |
62682a34 | 144 | |
a7813a04 XL |
145 | match error { |
146 | MethodError::NoMatch(NoMatchData { static_candidates: static_sources, | |
147 | unsatisfied_predicates, | |
148 | out_of_scope_traits, | |
c30ab7b3 SL |
149 | mode, |
150 | .. }) => { | |
a7813a04 XL |
151 | let tcx = self.tcx; |
152 | ||
c30ab7b3 SL |
153 | let mut err = self.type_error_struct(span, |
154 | |actual| { | |
155 | format!("no {} named `{}` found for type `{}` in the current scope", | |
156 | if mode == Mode::MethodCall { | |
157 | "method" | |
158 | } else { | |
159 | "associated item" | |
160 | }, | |
161 | item_name, | |
162 | actual) | |
163 | }, | |
164 | rcvr_ty); | |
5bcae85e SL |
165 | |
166 | // If the method name is the name of a field with a function or closure type, | |
167 | // give a helping note that it has to be called as (x.f)(...). | |
168 | if let Some(expr) = rcvr_expr { | |
169 | for (ty, _) in self.autoderef(span, rcvr_ty) { | |
9e0c209e SL |
170 | match ty.sty { |
171 | ty::TyAdt(def, substs) if !def.is_enum() => { | |
c30ab7b3 SL |
172 | if let Some(field) = def.struct_variant() |
173 | .find_field_named(item_name) { | |
9e0c209e SL |
174 | let snippet = tcx.sess.codemap().span_to_snippet(expr.span); |
175 | let expr_string = match snippet { | |
176 | Ok(expr_string) => expr_string, | |
c30ab7b3 SL |
177 | _ => "s".into(), // Default to a generic placeholder for the |
178 | // expression when we can't generate a | |
179 | // string snippet | |
9e0c209e SL |
180 | }; |
181 | ||
182 | let field_ty = field.ty(tcx, substs); | |
183 | ||
184 | if self.is_fn_ty(&field_ty, span) { | |
c30ab7b3 SL |
185 | err.span_note(span, |
186 | &format!("use `({0}.{1})(...)` if you \ | |
187 | meant to call the function \ | |
188 | stored in the `{1}` field", | |
189 | expr_string, | |
190 | item_name)); | |
9e0c209e | 191 | } else { |
c30ab7b3 SL |
192 | err.span_note(span, |
193 | &format!("did you mean to write `{0}.{1}`?", | |
194 | expr_string, | |
195 | item_name)); | |
9e0c209e SL |
196 | } |
197 | break; | |
5bcae85e | 198 | } |
5bcae85e | 199 | } |
9e0c209e | 200 | _ => {} |
a7813a04 | 201 | } |
54a0048b SL |
202 | } |
203 | } | |
92a42be0 | 204 | |
a7813a04 XL |
205 | if self.is_fn_ty(&rcvr_ty, span) { |
206 | macro_rules! report_function { | |
207 | ($span:expr, $name:expr) => { | |
208 | err.note(&format!("{} is a function, perhaps you wish to call it", | |
209 | $name)); | |
210 | } | |
54a0048b | 211 | } |
a7813a04 XL |
212 | |
213 | if let Some(expr) = rcvr_expr { | |
c30ab7b3 | 214 | if let Ok(expr_string) = tcx.sess.codemap().span_to_snippet(expr.span) { |
a7813a04 | 215 | report_function!(expr.span, expr_string); |
c30ab7b3 | 216 | } else if let Expr_::ExprPath(_, path) = expr.node.clone() { |
a7813a04 XL |
217 | if let Some(segment) = path.segments.last() { |
218 | report_function!(expr.span, segment.name); | |
219 | } | |
62682a34 SL |
220 | } |
221 | } | |
c34b1796 | 222 | } |
85aaf69f | 223 | |
a7813a04 | 224 | if !static_sources.is_empty() { |
c30ab7b3 SL |
225 | err.note("found the following associated functions; to be used as methods, \ |
226 | functions must have a `self` parameter"); | |
85aaf69f | 227 | |
a7813a04 XL |
228 | report_candidates(&mut err, static_sources); |
229 | } | |
85aaf69f | 230 | |
a7813a04 XL |
231 | if !unsatisfied_predicates.is_empty() { |
232 | let bound_list = unsatisfied_predicates.iter() | |
c30ab7b3 | 233 | .map(|p| format!("`{} : {}`", p.self_ty(), p)) |
a7813a04 XL |
234 | .collect::<Vec<_>>() |
235 | .join(", "); | |
c30ab7b3 SL |
236 | err.note(&format!("the method `{}` exists but the following trait bounds \ |
237 | were not satisfied: {}", | |
238 | item_name, | |
239 | bound_list)); | |
a7813a04 | 240 | } |
62682a34 | 241 | |
c30ab7b3 SL |
242 | self.suggest_traits_to_import(&mut err, |
243 | span, | |
244 | rcvr_ty, | |
245 | item_name, | |
246 | rcvr_expr, | |
247 | out_of_scope_traits); | |
a7813a04 XL |
248 | err.emit(); |
249 | } | |
85aaf69f | 250 | |
a7813a04 | 251 | MethodError::Ambiguity(sources) => { |
c30ab7b3 SL |
252 | let mut err = struct_span_err!(self.sess(), |
253 | span, | |
254 | E0034, | |
a7813a04 | 255 | "multiple applicable items in scope"); |
9e0c209e | 256 | err.span_label(span, &format!("multiple `{}` found", item_name)); |
85aaf69f | 257 | |
a7813a04 XL |
258 | report_candidates(&mut err, sources); |
259 | err.emit(); | |
260 | } | |
85aaf69f | 261 | |
a7813a04 XL |
262 | MethodError::ClosureAmbiguity(trait_def_id) => { |
263 | let msg = format!("the `{}` method from the `{}` trait cannot be explicitly \ | |
264 | invoked on this closure as we have not yet inferred what \ | |
265 | kind of closure it is", | |
c30ab7b3 SL |
266 | item_name, |
267 | self.tcx.item_path_str(trait_def_id)); | |
a7813a04 XL |
268 | let msg = if let Some(callee) = rcvr_expr { |
269 | format!("{}; use overloaded call notation instead (e.g., `{}()`)", | |
c30ab7b3 SL |
270 | msg, |
271 | pprust::expr_to_string(callee)) | |
a7813a04 XL |
272 | } else { |
273 | msg | |
274 | }; | |
275 | self.sess().span_err(span, &msg); | |
276 | } | |
54a0048b | 277 | |
a7813a04 XL |
278 | MethodError::PrivateMatch(def) => { |
279 | let msg = format!("{} `{}` is private", def.kind_name(), item_name); | |
280 | self.tcx.sess.span_err(span, &msg); | |
281 | } | |
54a0048b | 282 | } |
85aaf69f SL |
283 | } |
284 | ||
a7813a04 XL |
285 | fn suggest_traits_to_import(&self, |
286 | err: &mut DiagnosticBuilder, | |
287 | span: Span, | |
288 | rcvr_ty: Ty<'tcx>, | |
289 | item_name: ast::Name, | |
290 | rcvr_expr: Option<&hir::Expr>, | |
c30ab7b3 | 291 | valid_out_of_scope_traits: Vec<DefId>) { |
a7813a04 XL |
292 | if !valid_out_of_scope_traits.is_empty() { |
293 | let mut candidates = valid_out_of_scope_traits; | |
294 | candidates.sort(); | |
295 | candidates.dedup(); | |
c30ab7b3 SL |
296 | let msg = format!("items from traits can only be used if the trait is in scope; the \ |
297 | following {traits_are} implemented but not in scope, perhaps add \ | |
298 | a `use` for {one_of_them}:", | |
299 | traits_are = if candidates.len() == 1 { | |
300 | "trait is" | |
301 | } else { | |
302 | "traits are" | |
303 | }, | |
304 | one_of_them = if candidates.len() == 1 { | |
305 | "it" | |
306 | } else { | |
307 | "one of them" | |
308 | }); | |
a7813a04 XL |
309 | |
310 | err.help(&msg[..]); | |
311 | ||
312 | let limit = if candidates.len() == 5 { 5 } else { 4 }; | |
313 | for (i, trait_did) in candidates.iter().take(limit).enumerate() { | |
314 | err.help(&format!("candidate #{}: `use {}`", | |
315 | i + 1, | |
316 | self.tcx.item_path_str(*trait_did))); | |
317 | } | |
318 | if candidates.len() > limit { | |
319 | err.note(&format!("and {} others", candidates.len() - limit)); | |
85aaf69f | 320 | } |
c30ab7b3 | 321 | return; |
85aaf69f | 322 | } |
85aaf69f | 323 | |
a7813a04 XL |
324 | let type_is_local = self.type_derefs_to_local(span, rcvr_ty, rcvr_expr); |
325 | ||
326 | // there's no implemented traits, so lets suggest some traits to | |
327 | // implement, by finding ones that have the item name, and are | |
328 | // legal to implement. | |
329 | let mut candidates = all_traits(self.ccx) | |
330 | .filter(|info| { | |
331 | // we approximate the coherence rules to only suggest | |
332 | // traits that are legal to implement by requiring that | |
333 | // either the type or trait is local. Multidispatch means | |
334 | // this isn't perfect (that is, there are cases when | |
335 | // implementing a trait would be legal but is rejected | |
336 | // here). | |
c30ab7b3 SL |
337 | (type_is_local || info.def_id.is_local()) && |
338 | self.impl_or_trait_item(info.def_id, item_name).is_some() | |
a7813a04 XL |
339 | }) |
340 | .collect::<Vec<_>>(); | |
341 | ||
342 | if !candidates.is_empty() { | |
343 | // sort from most relevant to least relevant | |
344 | candidates.sort_by(|a, b| a.cmp(b).reverse()); | |
345 | candidates.dedup(); | |
346 | ||
347 | // FIXME #21673 this help message could be tuned to the case | |
348 | // of a type parameter: suggest adding a trait bound rather | |
349 | // than implementing. | |
c30ab7b3 SL |
350 | let msg = format!("items from traits can only be used if the trait is implemented \ |
351 | and in scope; the following {traits_define} an item `{name}`, \ | |
352 | perhaps you need to implement {one_of_them}:", | |
353 | traits_define = if candidates.len() == 1 { | |
354 | "trait defines" | |
355 | } else { | |
356 | "traits define" | |
357 | }, | |
358 | one_of_them = if candidates.len() == 1 { | |
359 | "it" | |
360 | } else { | |
361 | "one of them" | |
362 | }, | |
363 | name = item_name); | |
a7813a04 XL |
364 | |
365 | err.help(&msg[..]); | |
366 | ||
367 | for (i, trait_info) in candidates.iter().enumerate() { | |
368 | err.help(&format!("candidate #{}: `{}`", | |
369 | i + 1, | |
370 | self.tcx.item_path_str(trait_info.def_id))); | |
371 | } | |
85aaf69f | 372 | } |
85aaf69f SL |
373 | } |
374 | ||
a7813a04 XL |
375 | /// Checks whether there is a local type somewhere in the chain of |
376 | /// autoderefs of `rcvr_ty`. | |
377 | fn type_derefs_to_local(&self, | |
378 | span: Span, | |
379 | rcvr_ty: Ty<'tcx>, | |
c30ab7b3 SL |
380 | rcvr_expr: Option<&hir::Expr>) |
381 | -> bool { | |
a7813a04 XL |
382 | fn is_local(ty: Ty) -> bool { |
383 | match ty.sty { | |
9e0c209e | 384 | ty::TyAdt(def, _) => def.did.is_local(), |
a7813a04 | 385 | |
9e0c209e | 386 | ty::TyTrait(ref tr) => tr.principal.def_id().is_local(), |
a7813a04 XL |
387 | |
388 | ty::TyParam(_) => true, | |
389 | ||
390 | // everything else (primitive types etc.) is effectively | |
391 | // non-local (there are "edge" cases, e.g. (LocalType,), but | |
392 | // the noise from these sort of types is usually just really | |
393 | // annoying, rather than any sort of help). | |
c30ab7b3 | 394 | _ => false, |
a7813a04 | 395 | } |
85aaf69f | 396 | } |
85aaf69f | 397 | |
a7813a04 XL |
398 | // This occurs for UFCS desugaring of `T::method`, where there is no |
399 | // receiver expression for the method call, and thus no autoderef. | |
400 | if rcvr_expr.is_none() { | |
401 | return is_local(self.resolve_type_vars_with_obligations(rcvr_ty)); | |
c34b1796 | 402 | } |
c34b1796 | 403 | |
3157f602 | 404 | self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty)) |
c34b1796 | 405 | } |
85aaf69f SL |
406 | } |
407 | ||
a7813a04 XL |
408 | pub type AllTraitsVec = Vec<TraitInfo>; |
409 | ||
c34b1796 | 410 | #[derive(Copy, Clone)] |
85aaf69f | 411 | pub struct TraitInfo { |
e9174d1e | 412 | pub def_id: DefId, |
85aaf69f SL |
413 | } |
414 | ||
415 | impl TraitInfo { | |
e9174d1e | 416 | fn new(def_id: DefId) -> TraitInfo { |
c30ab7b3 | 417 | TraitInfo { def_id: def_id } |
85aaf69f SL |
418 | } |
419 | } | |
420 | impl PartialEq for TraitInfo { | |
421 | fn eq(&self, other: &TraitInfo) -> bool { | |
422 | self.cmp(other) == Ordering::Equal | |
423 | } | |
424 | } | |
425 | impl Eq for TraitInfo {} | |
426 | impl PartialOrd for TraitInfo { | |
c30ab7b3 SL |
427 | fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { |
428 | Some(self.cmp(other)) | |
429 | } | |
85aaf69f SL |
430 | } |
431 | impl Ord for TraitInfo { | |
432 | fn cmp(&self, other: &TraitInfo) -> Ordering { | |
b039eaaf SL |
433 | // local crates are more important than remote ones (local: |
434 | // cnum == 0), and otherwise we throw in the defid for totality | |
85aaf69f | 435 | |
b039eaaf SL |
436 | let lhs = (other.def_id.krate, other.def_id); |
437 | let rhs = (self.def_id.krate, self.def_id); | |
85aaf69f SL |
438 | lhs.cmp(&rhs) |
439 | } | |
440 | } | |
441 | ||
442 | /// Retrieve all traits in this crate and any dependent crates. | |
443 | pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> { | |
444 | if ccx.all_traits.borrow().is_none() { | |
54a0048b | 445 | use rustc::hir::intravisit; |
85aaf69f SL |
446 | |
447 | let mut traits = vec![]; | |
448 | ||
449 | // Crate-local: | |
450 | // | |
451 | // meh. | |
c30ab7b3 | 452 | struct Visitor<'a, 'tcx: 'a> { |
b039eaaf | 453 | map: &'a hir_map::Map<'tcx>, |
85aaf69f SL |
454 | traits: &'a mut AllTraitsVec, |
455 | } | |
92a42be0 | 456 | impl<'v, 'a, 'tcx> intravisit::Visitor<'v> for Visitor<'a, 'tcx> { |
e9174d1e | 457 | fn visit_item(&mut self, i: &'v hir::Item) { |
85aaf69f | 458 | match i.node { |
e9174d1e | 459 | hir::ItemTrait(..) => { |
b039eaaf SL |
460 | let def_id = self.map.local_def_id(i.id); |
461 | self.traits.push(TraitInfo::new(def_id)); | |
85aaf69f SL |
462 | } |
463 | _ => {} | |
464 | } | |
85aaf69f SL |
465 | } |
466 | } | |
92a42be0 | 467 | ccx.tcx.map.krate().visit_all_items(&mut Visitor { |
b039eaaf | 468 | map: &ccx.tcx.map, |
c30ab7b3 | 469 | traits: &mut traits, |
92a42be0 | 470 | }); |
85aaf69f SL |
471 | |
472 | // Cross-crate: | |
b039eaaf | 473 | let mut external_mods = FnvHashSet(); |
9e0c209e SL |
474 | fn handle_external_def(ccx: &CrateCtxt, |
475 | traits: &mut AllTraitsVec, | |
b039eaaf | 476 | external_mods: &mut FnvHashSet<DefId>, |
c30ab7b3 SL |
477 | def: Def) { |
478 | let def_id = def.def_id(); | |
479 | match def { | |
480 | Def::Trait(..) => { | |
9e0c209e | 481 | traits.push(TraitInfo::new(def_id)); |
85aaf69f | 482 | } |
c30ab7b3 | 483 | Def::Mod(..) => { |
9e0c209e | 484 | if !external_mods.insert(def_id) { |
b039eaaf SL |
485 | return; |
486 | } | |
9e0c209e | 487 | for child in ccx.tcx.sess.cstore.item_children(def_id) { |
c30ab7b3 | 488 | handle_external_def(ccx, traits, external_mods, child.def) |
92a42be0 | 489 | } |
85aaf69f SL |
490 | } |
491 | _ => {} | |
492 | } | |
493 | } | |
92a42be0 | 494 | for cnum in ccx.tcx.sess.cstore.crates() { |
c30ab7b3 | 495 | let def_id = DefId { |
9e0c209e | 496 | krate: cnum, |
c30ab7b3 SL |
497 | index: CRATE_DEF_INDEX, |
498 | }; | |
499 | handle_external_def(ccx, &mut traits, &mut external_mods, Def::Mod(def_id)); | |
92a42be0 | 500 | } |
85aaf69f SL |
501 | |
502 | *ccx.all_traits.borrow_mut() = Some(traits); | |
503 | } | |
504 | ||
505 | let borrow = ccx.all_traits.borrow(); | |
506 | assert!(borrow.is_some()); | |
507 | AllTraits { | |
508 | borrow: borrow, | |
c30ab7b3 | 509 | idx: 0, |
85aaf69f SL |
510 | } |
511 | } | |
512 | ||
513 | pub struct AllTraits<'a> { | |
bd371182 | 514 | borrow: cell::Ref<'a, Option<AllTraitsVec>>, |
c30ab7b3 | 515 | idx: usize, |
85aaf69f SL |
516 | } |
517 | ||
518 | impl<'a> Iterator for AllTraits<'a> { | |
519 | type Item = TraitInfo; | |
520 | ||
521 | fn next(&mut self) -> Option<TraitInfo> { | |
522 | let AllTraits { ref borrow, ref mut idx } = *self; | |
523 | // ugh. | |
524 | borrow.as_ref().unwrap().get(*idx).map(|info| { | |
525 | *idx += 1; | |
526 | *info | |
527 | }) | |
528 | } | |
529 | } |