]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/tystr_pass.rs
rustdoc: Remove a now invalid test
[rust.git] / src / librustdoc / tystr_pass.rs
1 // Copyright 2012 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
11 //! Pulls type information out of the AST and attaches it to the document
12
13 use astsrv;
14 use doc::ItemUtils;
15 use doc;
16 use extract::to_str;
17 use extract;
18 use fold::Fold;
19 use fold;
20 use pass::Pass;
21
22 use syntax::ast;
23 use syntax::print::pprust;
24 use syntax::ast_map;
25
26 pub fn mk_pass() -> Pass {
27     Pass {
28         name: ~"tystr",
29         f: run
30     }
31 }
32
33 pub fn run(
34     srv: astsrv::Srv,
35     doc: doc::Doc
36 ) -> doc::Doc {
37     let fold = Fold {
38         ctxt: srv.clone(),
39         fold_fn: fold_fn,
40         fold_const: fold_const,
41         fold_enum: fold_enum,
42         fold_trait: fold_trait,
43         fold_impl: fold_impl,
44         fold_type: fold_type,
45         fold_struct: fold_struct,
46         .. fold::default_any_fold(srv)
47     };
48     (fold.fold_doc)(&fold, doc)
49 }
50
51 fn fold_fn(
52     fold: &fold::Fold<astsrv::Srv>,
53     doc: doc::FnDoc
54 ) -> doc::FnDoc {
55
56     let srv = fold.ctxt.clone();
57
58     doc::SimpleItemDoc {
59         sig: get_fn_sig(srv, doc.id()),
60         .. doc
61     }
62 }
63
64 fn get_fn_sig(srv: astsrv::Srv, fn_id: doc::AstId) -> Option<~str> {
65     do astsrv::exec(srv) |ctxt| {
66         match *ctxt.ast_map.get(&fn_id) {
67             ast_map::node_item(@ast::item {
68                 ident: ident,
69                 node: ast::item_fn(ref decl, purity, _, ref tys, _), _
70             }, _) |
71             ast_map::node_foreign_item(@ast::foreign_item {
72                 ident: ident,
73                 node: ast::foreign_item_fn(ref decl, purity, ref tys), _
74             }, _, _, _) => {
75                 Some(pprust::fun_to_str(decl, purity, ident, None, tys,
76                                         extract::interner()))
77             }
78             _ => fail!(~"get_fn_sig: fn_id not bound to a fn item")
79         }
80     }
81 }
82
83 fn fold_const(
84     fold: &fold::Fold<astsrv::Srv>,
85     doc: doc::ConstDoc
86 ) -> doc::ConstDoc {
87     let srv = fold.ctxt.clone();
88
89     doc::SimpleItemDoc {
90         sig: Some({
91             let doc = copy doc;
92             do astsrv::exec(srv) |ctxt| {
93                 match *ctxt.ast_map.get(&doc.id()) {
94                     ast_map::node_item(@ast::item {
95                         node: ast::item_const(ty, _), _
96                     }, _) => {
97                         pprust::ty_to_str(ty, extract::interner())
98                     }
99                     _ => fail!(~"fold_const: id not bound to a const item")
100                 }
101             }}),
102         .. doc
103     }
104 }
105
106 fn fold_enum(
107     fold: &fold::Fold<astsrv::Srv>,
108     doc: doc::EnumDoc
109 ) -> doc::EnumDoc {
110     let doc_id = doc.id();
111     let srv = fold.ctxt.clone();
112
113     doc::EnumDoc {
114         variants: do vec::map(doc.variants) |variant| {
115             let sig = {
116                 let variant = copy *variant;
117                 do astsrv::exec(srv.clone()) |ctxt| {
118                     match *ctxt.ast_map.get(&doc_id) {
119                         ast_map::node_item(@ast::item {
120                             node: ast::item_enum(ref enum_definition, _), _
121                         }, _) => {
122                             let ast_variant =
123                                 do vec::find(enum_definition.variants) |v| {
124                                 to_str(v.node.name) == variant.name
125                             }.get();
126
127                             pprust::variant_to_str(
128                                 ast_variant, extract::interner())
129                         }
130                         _ => fail!(~"enum variant not bound to an enum item")
131                     }
132                 }
133             };
134
135             doc::VariantDoc {
136                 sig: Some(sig),
137                 .. copy *variant
138             }
139         },
140         .. doc
141     }
142 }
143
144 fn fold_trait(
145     fold: &fold::Fold<astsrv::Srv>,
146     doc: doc::TraitDoc
147 ) -> doc::TraitDoc {
148     doc::TraitDoc {
149         methods: merge_methods(fold.ctxt.clone(), doc.id(), copy doc.methods),
150         .. doc
151     }
152 }
153
154 fn merge_methods(
155     srv: astsrv::Srv,
156     item_id: doc::AstId,
157     docs: ~[doc::MethodDoc]
158 ) -> ~[doc::MethodDoc] {
159     do vec::map(docs) |doc| {
160         doc::MethodDoc {
161             sig: get_method_sig(srv.clone(), item_id, copy doc.name),
162             .. copy *doc
163         }
164     }
165 }
166
167 fn get_method_sig(
168     srv: astsrv::Srv,
169     item_id: doc::AstId,
170     method_name: ~str
171 ) -> Option<~str> {
172     do astsrv::exec(srv) |ctxt| {
173         match *ctxt.ast_map.get(&item_id) {
174             ast_map::node_item(@ast::item {
175                 node: ast::item_trait(_, _, ref methods), _
176             }, _) => {
177                 match vec::find(*methods, |method| {
178                     match copy *method {
179                         ast::required(ty_m) => to_str(ty_m.ident) == method_name,
180                         ast::provided(m) => to_str(m.ident) == method_name,
181                     }
182                 }) {
183                     Some(method) => {
184                         match method {
185                             ast::required(ty_m) => {
186                                 Some(pprust::fun_to_str(
187                                     &ty_m.decl,
188                                     ty_m.purity,
189                                     ty_m.ident,
190                                     Some(ty_m.self_ty.node),
191                                     &ty_m.generics,
192                                     extract::interner()
193                                 ))
194                             }
195                             ast::provided(m) => {
196                                 Some(pprust::fun_to_str(
197                                     &m.decl,
198                                     m.purity,
199                                     m.ident,
200                                     Some(m.self_ty.node),
201                                     &m.generics,
202                                     extract::interner()
203                                 ))
204                             }
205                         }
206                     }
207                     _ => fail!(~"method not found")
208                 }
209             }
210             ast_map::node_item(@ast::item {
211                 node: ast::item_impl(_, _, _, ref methods), _
212             }, _) => {
213                 match vec::find(*methods, |method| {
214                     to_str(method.ident) == method_name
215                 }) {
216                     Some(method) => {
217                         Some(pprust::fun_to_str(
218                             &method.decl,
219                             method.purity,
220                             method.ident,
221                             Some(method.self_ty.node),
222                             &method.generics,
223                             extract::interner()
224                         ))
225                     }
226                     None => fail!(~"method not found")
227                 }
228             }
229             _ => fail!(~"get_method_sig: item ID not bound to trait or impl")
230         }
231     }
232 }
233
234 fn fold_impl(
235     fold: &fold::Fold<astsrv::Srv>,
236     doc: doc::ImplDoc
237 ) -> doc::ImplDoc {
238
239     let srv = fold.ctxt.clone();
240
241     let (bounds, trait_types, self_ty) = {
242         let doc = copy doc;
243         do astsrv::exec(srv) |ctxt| {
244             match *ctxt.ast_map.get(&doc.id()) {
245                 ast_map::node_item(@ast::item {
246                     node: ast::item_impl(ref generics, opt_trait_type, self_ty, _), _
247                 }, _) => {
248                     let bounds = pprust::generics_to_str(generics, extract::interner());
249                     let bounds = if bounds.is_empty() { None } else { Some(bounds) };
250                     let trait_types = opt_trait_type.map_default(~[], |p| {
251                         ~[pprust::path_to_str(p.path, extract::interner())]
252                     });
253                     (bounds,
254                      trait_types,
255                      Some(pprust::ty_to_str(
256                          self_ty, extract::interner())))
257                 }
258                 _ => fail!(~"expected impl")
259             }
260         }
261     };
262
263     doc::ImplDoc {
264         bounds_str: bounds,
265         trait_types: trait_types,
266         self_ty: self_ty,
267         methods: merge_methods(fold.ctxt.clone(), doc.id(), copy doc.methods),
268         .. doc
269     }
270 }
271
272 fn fold_type(
273     fold: &fold::Fold<astsrv::Srv>,
274     doc: doc::TyDoc
275 ) -> doc::TyDoc {
276
277     let srv = fold.ctxt.clone();
278
279     doc::SimpleItemDoc {
280         sig: {
281             let doc = copy doc;
282             do astsrv::exec(srv) |ctxt| {
283                 match *ctxt.ast_map.get(&doc.id()) {
284                     ast_map::node_item(@ast::item {
285                         ident: ident,
286                         node: ast::item_ty(ty, ref params), _
287                     }, _) => {
288                         Some(fmt!(
289                             "type %s%s = %s",
290                             to_str(ident),
291                             pprust::generics_to_str(params,
292                                                     extract::interner()),
293                             pprust::ty_to_str(ty,
294                                               extract::interner())
295                         ))
296                     }
297                     _ => fail!(~"expected type")
298                 }
299             }
300         },
301         .. doc
302     }
303 }
304
305 fn fold_struct(
306     fold: &fold::Fold<astsrv::Srv>,
307     doc: doc::StructDoc
308 ) -> doc::StructDoc {
309     let srv = fold.ctxt.clone();
310
311     doc::StructDoc {
312         sig: {
313             let doc = copy doc;
314             do astsrv::exec(srv) |ctxt| {
315                 match *ctxt.ast_map.get(&doc.id()) {
316                     ast_map::node_item(item, _) => {
317                         let item = strip_struct_extra_stuff(item);
318                         Some(pprust::item_to_str(item,
319                                                  extract::interner()))
320                     }
321                     _ => fail!(~"not an item")
322                 }
323             }
324         },
325         .. doc
326     }
327 }
328
329 /// Removes various things from the struct item definition that
330 /// shouldn't be displayed in the struct signature. Probably there
331 /// should be a simple pprust::struct_to_str function that does
332 /// what I actually want
333 fn strip_struct_extra_stuff(item: @ast::item) -> @ast::item {
334     let node = match copy item.node {
335         ast::item_struct(def, tys) => ast::item_struct(def, tys),
336         _ => fail!(~"not a struct")
337     };
338
339     @ast::item {
340         attrs: ~[], // Remove the attributes
341         node: node,
342         .. copy *item
343     }
344 }
345
346 #[cfg(test)]
347 mod test {
348     use astsrv;
349     use doc;
350     use extract;
351     use tystr_pass::run;
352
353     fn mk_doc(source: ~str) -> doc::Doc {
354         do astsrv::from_str(copy source) |srv| {
355             let doc = extract::from_srv(srv.clone(), ~"");
356             run(srv.clone(), doc)
357         }
358     }
359
360     #[test]
361     fn should_add_fn_sig() {
362         let doc = mk_doc(~"fn a<T>() -> int { }");
363         assert!(doc.cratemod().fns()[0].sig == Some(~"fn a<T>() -> int"));
364     }
365
366     #[test]
367     fn should_add_foreign_fn_sig() {
368         let doc = mk_doc(~"extern mod a { fn a<T>() -> int; }");
369         assert!(doc.cratemod().nmods()[0].fns[0].sig ==
370                 Some(~"fn a<T>() -> int"));
371     }
372
373     #[test]
374     fn should_add_const_types() {
375         let doc = mk_doc(~"static a: bool = true;");
376         assert!(doc.cratemod().consts()[0].sig == Some(~"bool"));
377     }
378
379     #[test]
380     fn should_add_variant_sigs() {
381         let doc = mk_doc(~"enum a { b(int) }");
382         assert!(doc.cratemod().enums()[0].variants[0].sig ==
383                 Some(~"b(int)"));
384     }
385
386     #[test]
387     fn should_add_trait_method_sigs() {
388         let doc = mk_doc(~"trait i { fn a<T>(&mut self) -> int; }");
389         assert!(doc.cratemod().traits()[0].methods[0].sig
390                 == Some(~"fn a<T>(&mut self) -> int"));
391     }
392
393     #[test]
394     fn should_add_impl_bounds() {
395         let doc = mk_doc(~"impl<T, U: Copy, V: Copy + Clone> Option<T, U, V> { }");
396         assert!(doc.cratemod().impls()[0].bounds_str == Some(~"<T, U: Copy, V: Copy + Clone>"));
397     }
398
399     #[test]
400     fn should_add_impl_trait_types() {
401         let doc = mk_doc(~"impl j for int { fn a<T>() { } }");
402         assert!(doc.cratemod().impls()[0].trait_types[0] == ~"j");
403     }
404
405     #[test]
406     fn should_not_add_impl_trait_types_if_none() {
407         let doc = mk_doc(~"impl int { fn a() { } }");
408         assert!(vec::len(doc.cratemod().impls()[0].trait_types) == 0);
409     }
410
411     #[test]
412     fn should_add_impl_self_ty() {
413         let doc = mk_doc(~"impl int { fn a() { } }");
414         assert!(doc.cratemod().impls()[0].self_ty == Some(~"int"));
415     }
416
417     #[test]
418     fn should_add_impl_method_sigs() {
419         let doc = mk_doc(~"impl int { fn a<T>(&self) -> int { fail!() } }");
420         assert!(doc.cratemod().impls()[0].methods[0].sig
421                 == Some(~"fn a<T>(&self) -> int"));
422     }
423
424     #[test]
425     fn should_add_type_signatures() {
426         let doc = mk_doc(~"type t<T> = int;");
427         assert!(doc.cratemod().types()[0].sig == Some(~"type t<T> = int"));
428     }
429
430     #[test]
431     fn should_add_struct_defs() {
432         let doc = mk_doc(~"struct S { field: () }");
433         assert!((&doc.cratemod().structs()[0].sig).get().contains(
434             "struct S {"));
435     }
436
437     #[test]
438     fn should_not_serialize_struct_attrs() {
439         // All we care about are the fields
440         let doc = mk_doc(~"#[wut] struct S { field: () }");
441         assert!(!(&doc.cratemod().structs()[0].sig).get().contains("wut"));
442     }
443 }