]> git.lizzy.rs Git - rust.git/blob - src/librustc/metadata/creader.rs
libsyntax: Update view_item_use/import to reflect actual usage
[rust.git] / src / librustc / metadata / creader.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
12 //! Validates all used crates and extern libraries and loads their metadata
13
14 use core::prelude::*;
15
16 use metadata::cstore;
17 use metadata::common::*;
18 use metadata::decoder;
19 use metadata::filesearch::FileSearch;
20 use metadata::loader;
21
22 use core::either;
23 use core::option;
24 use core::vec;
25 use syntax::attr;
26 use syntax::codemap::{span, dummy_sp};
27 use syntax::diagnostic::span_handler;
28 use syntax::parse::token::ident_interner;
29 use syntax::print::pprust;
30 use syntax::visit;
31 use syntax::{ast, ast_util};
32 use std::oldmap::HashMap;
33
34 // Traverses an AST, reading all the information about use'd crates and extern
35 // libraries necessary for later resolving, typechecking, linking, etc.
36 pub fn read_crates(diag: span_handler,
37                    crate: ast::crate,
38                    cstore: @mut cstore::CStore,
39                    filesearch: FileSearch,
40                    os: loader::os,
41                    statik: bool,
42                    intr: @ident_interner) {
43     let e = @mut Env {
44         diag: diag,
45         filesearch: filesearch,
46         cstore: cstore,
47         os: os,
48         statik: statik,
49         crate_cache: @mut ~[],
50         next_crate_num: 1,
51         intr: intr
52     };
53     let v =
54         visit::mk_simple_visitor(@visit::SimpleVisitor {
55             visit_view_item: |a| visit_view_item(e, a),
56             visit_item: |a| visit_item(e, a),
57             .. *visit::default_simple_visitor()});
58     visit::visit_crate(crate, (), v);
59     dump_crates(e.crate_cache);
60     warn_if_multiple_versions(e, diag, e.crate_cache);
61 }
62
63 type cache_entry = {
64     cnum: int,
65     span: span,
66     hash: ~str,
67     metas: @~[@ast::meta_item]
68 };
69
70 fn dump_crates(+crate_cache: @mut ~[cache_entry]) {
71     debug!("resolved crates:");
72     for crate_cache.each |entry| {
73         debug!("cnum: %?", entry.cnum);
74         debug!("span: %?", entry.span);
75         debug!("hash: %?", entry.hash);
76     }
77 }
78
79 fn warn_if_multiple_versions(e: @mut Env,
80                              diag: span_handler,
81                              crate_cache: @mut ~[cache_entry]) {
82     use either::*;
83
84     if crate_cache.len() != 0u {
85         let name = loader::crate_name_from_metas(
86             /*bad*/copy *crate_cache.last().metas);
87         let (matches, non_matches) =
88             partition(crate_cache.map_to_vec(|&entry| {
89                 let othername = loader::crate_name_from_metas(
90                     copy *entry.metas);
91                 if name == othername {
92                     Left(entry)
93                 } else {
94                     Right(entry)
95                 }
96             }));
97
98         assert !matches.is_empty();
99
100         if matches.len() != 1u {
101             diag.handler().warn(
102                 fmt!("using multiple versions of crate `%s`", name));
103             for matches.each |match_| {
104                 diag.span_note(match_.span, ~"used here");
105                 let attrs = ~[
106                     attr::mk_attr(attr::mk_list_item(
107                         ~"link", /*bad*/copy *match_.metas))
108                 ];
109                 loader::note_linkage_attrs(e.intr, diag, attrs);
110             }
111         }
112
113         warn_if_multiple_versions(e, diag, @mut non_matches);
114     }
115 }
116
117 struct Env {
118     diag: span_handler,
119     filesearch: FileSearch,
120     cstore: @mut cstore::CStore,
121     os: loader::os,
122     statik: bool,
123     crate_cache: @mut ~[cache_entry],
124     next_crate_num: ast::crate_num,
125     intr: @ident_interner
126 }
127
128 fn visit_view_item(e: @mut Env, i: @ast::view_item) {
129     match /*bad*/copy i.node {
130       ast::view_item_extern_mod(ident, meta_items, id) => {
131         debug!("resolving extern mod stmt. ident: %?, meta: %?",
132                ident, meta_items);
133         let cnum = resolve_crate(e, ident, meta_items, ~"", i.span);
134         cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum);
135       }
136       _ => ()
137     }
138 }
139
140 fn visit_item(e: @mut Env, i: @ast::item) {
141     match /*bad*/copy i.node {
142       ast::item_foreign_mod(fm) => {
143         match attr::foreign_abi(i.attrs) {
144           either::Right(abi) => {
145             if abi != ast::foreign_abi_cdecl &&
146                abi != ast::foreign_abi_stdcall { return; }
147           }
148           either::Left(ref msg) => e.diag.span_fatal(i.span, (*msg))
149         }
150
151         let cstore = e.cstore;
152         let mut already_added = false;
153         let link_args = attr::find_attrs_by_name(i.attrs, "link_args");
154
155         match fm.sort {
156           ast::named => {
157             let foreign_name =
158                match attr::first_attr_value_str_by_name(i.attrs,
159                                                         ~"link_name") {
160                  Some(ref nn) => {
161                    if (*nn) == ~"" {
162                       e.diag.span_fatal(
163                           i.span,
164                           ~"empty #[link_name] not allowed; use #[nolink].");
165                    }
166                    (/*bad*/copy *nn)
167                  }
168                 None => /*bad*/copy *e.intr.get(i.ident)
169             };
170             if attr::find_attrs_by_name(i.attrs, ~"nolink").is_empty() {
171                 already_added =
172                     !cstore::add_used_library(cstore, copy foreign_name);
173             }
174             if !link_args.is_empty() && already_added {
175                 e.diag.span_fatal(i.span, ~"library '" + foreign_name +
176                            ~"' already added: can't specify link_args.");
177             }
178           }
179           ast::anonymous => { /* do nothing */ }
180         }
181
182         for link_args.each |a| {
183             match attr::get_meta_item_value_str(attr::attr_meta(*a)) {
184               Some(ref linkarg) => {
185                 cstore::add_used_link_args(cstore, (/*bad*/copy *linkarg));
186               }
187               None => {/* fallthrough */ }
188             }
189         }
190       }
191       _ => { }
192     }
193 }
194
195 fn metas_with(+ident: ~str, +key: ~str, +metas: ~[@ast::meta_item])
196     -> ~[@ast::meta_item] {
197     let name_items = attr::find_meta_items_by_name(metas, key);
198     if name_items.is_empty() {
199         vec::append_one(metas, attr::mk_name_value_item_str(key, ident))
200     } else {
201         metas
202     }
203 }
204
205 fn metas_with_ident(+ident: ~str, +metas: ~[@ast::meta_item])
206     -> ~[@ast::meta_item] {
207     metas_with(ident, ~"name", metas)
208 }
209
210 fn existing_match(e: @mut Env, metas: ~[@ast::meta_item], hash: ~str)
211                -> Option<int> {
212     for e.crate_cache.each |c| {
213         if loader::metadata_matches(*c.metas, metas)
214             && (hash.is_empty() || c.hash == hash) {
215             return Some(c.cnum);
216         }
217     }
218     return None;
219 }
220
221 fn resolve_crate(e: @mut Env,
222                  ident: ast::ident,
223                  +metas: ~[@ast::meta_item],
224                  +hash: ~str,
225                  span: span)
226               -> ast::crate_num {
227     let metas = metas_with_ident(/*bad*/copy *e.intr.get(ident), metas);
228
229     match existing_match(e, metas, hash) {
230       None => {
231         let load_ctxt: loader::ctxt = {
232             diag: e.diag,
233             filesearch: e.filesearch,
234             span: span,
235             ident: ident,
236             metas: metas,
237             hash: hash,
238             os: e.os,
239             static: e.statik,
240             intr: e.intr
241         };
242         let cinfo = loader::load_library_crate(load_ctxt);
243
244         let cfilename = Path(cinfo.ident);
245         let cdata = cinfo.data;
246
247         let attrs = decoder::get_crate_attributes(cdata);
248         let linkage_metas = attr::find_linkage_metas(attrs);
249         let hash = decoder::get_crate_hash(cdata);
250
251         // Claim this crate number and cache it
252         let cnum = e.next_crate_num;
253         e.crate_cache.push({cnum: cnum, span: span,
254                             hash: hash, metas: @linkage_metas});
255         e.next_crate_num += 1;
256
257         // Now resolve the crates referenced by this crate
258         let cnum_map = resolve_crate_deps(e, cdata);
259
260         let cname =
261             match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
262                                                          ~"name") {
263               option::Some(ref v) => (/*bad*/copy *v),
264               option::None => /*bad*/copy *e.intr.get(ident)
265             };
266         let cmeta = @{name: cname, data: cdata,
267                       cnum_map: cnum_map, cnum: cnum};
268
269         let cstore = e.cstore;
270         cstore::set_crate_data(cstore, cnum, cmeta);
271         cstore::add_used_crate_file(cstore, &cfilename);
272         return cnum;
273       }
274       Some(cnum) => {
275         return cnum;
276       }
277     }
278 }
279
280 // Go through the crate metadata and load any crates that it references
281 fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
282     debug!("resolving deps of external crate");
283     // The map from crate numbers in the crate we're resolving to local crate
284     // numbers
285     let cnum_map = HashMap();
286     for decoder::get_crate_deps(e.intr, cdata).each |dep| {
287         let extrn_cnum = dep.cnum;
288         let cname = dep.name;
289         let cmetas = metas_with(/*bad*/copy dep.vers, ~"vers", ~[]);
290         debug!("resolving dep crate %s ver: %s hash: %s",
291                *e.intr.get(dep.name), dep.vers, dep.hash);
292         match existing_match(e, metas_with_ident(copy *e.intr.get(cname),
293                                                  copy cmetas),
294                              dep.hash) {
295           Some(local_cnum) => {
296             debug!("already have it");
297             // We've already seen this crate
298             cnum_map.insert(extrn_cnum, local_cnum);
299           }
300           None => {
301             debug!("need to load it");
302             // This is a new one so we've got to load it
303             // FIXME (#2404): Need better error reporting than just a bogus
304             // span.
305             let fake_span = dummy_sp();
306             let local_cnum = resolve_crate(e, cname, cmetas,
307                                            /*bad*/copy dep.hash, fake_span);
308             cnum_map.insert(extrn_cnum, local_cnum);
309           }
310         }
311     }
312     return cnum_map;
313 }
314
315 // Local Variables:
316 // mode: rust
317 // fill-column: 78;
318 // indent-tabs-mode: nil
319 // c-basic-offset: 4
320 // buffer-file-coding-system: utf-8-unix
321 // End: