]> git.lizzy.rs Git - rust.git/blob - src/librustc_metadata/native_libs.rs
Rollup merge of #58199 - clintfred:partial-move-err-msg, r=estebank
[rust.git] / src / librustc_metadata / native_libs.rs
1 use rustc::hir::itemlikevisit::ItemLikeVisitor;
2 use rustc::hir;
3 use rustc::middle::cstore::{self, NativeLibrary};
4 use rustc::session::Session;
5 use rustc::ty::TyCtxt;
6 use rustc::util::nodemap::FxHashSet;
7 use rustc_target::spec::abi::Abi;
8 use syntax::attr;
9 use syntax::source_map::Span;
10 use syntax::feature_gate::{self, GateIssue};
11 use syntax::symbol::Symbol;
12 use syntax::{span_err, struct_span_err};
13
14 pub fn collect<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec<NativeLibrary> {
15     let mut collector = Collector {
16         tcx,
17         libs: Vec::new(),
18     };
19     tcx.hir().krate().visit_all_item_likes(&mut collector);
20     collector.process_command_line();
21     return collector.libs
22 }
23
24 pub fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool {
25     match lib.cfg {
26         Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None),
27         None => true,
28     }
29 }
30
31 struct Collector<'a, 'tcx: 'a> {
32     tcx: TyCtxt<'a, 'tcx, 'tcx>,
33     libs: Vec<NativeLibrary>,
34 }
35
36 impl<'a, 'tcx> ItemLikeVisitor<'tcx> for Collector<'a, 'tcx> {
37     fn visit_item(&mut self, it: &'tcx hir::Item) {
38         let fm = match it.node {
39             hir::ItemKind::ForeignMod(ref fm) => fm,
40             _ => return,
41         };
42
43         if fm.abi == Abi::Rust ||
44             fm.abi == Abi::RustIntrinsic ||
45             fm.abi == Abi::PlatformIntrinsic {
46             return
47         }
48
49         // Process all of the #[link(..)]-style arguments
50         for m in it.attrs.iter().filter(|a| a.check_name("link")) {
51             let items = match m.meta_item_list() {
52                 Some(item) => item,
53                 None => continue,
54             };
55             let mut lib = NativeLibrary {
56                 name: None,
57                 kind: cstore::NativeUnknown,
58                 cfg: None,
59                 foreign_module: Some(self.tcx.hir().local_def_id(it.id)),
60                 wasm_import_module: None,
61             };
62             let mut kind_specified = false;
63
64             for item in items.iter() {
65                 if item.check_name("kind") {
66                     kind_specified = true;
67                     let kind = match item.value_str() {
68                         Some(name) => name,
69                         None => continue, // skip like historical compilers
70                     };
71                     lib.kind = match &kind.as_str()[..] {
72                         "static" => cstore::NativeStatic,
73                         "static-nobundle" => cstore::NativeStaticNobundle,
74                         "dylib" => cstore::NativeUnknown,
75                         "framework" => cstore::NativeFramework,
76                         k => {
77                             struct_span_err!(self.tcx.sess, m.span, E0458,
78                                       "unknown kind: `{}`", k)
79                                 .span_label(item.span, "unknown kind").emit();
80                             cstore::NativeUnknown
81                         }
82                     };
83                 } else if item.check_name("name") {
84                     lib.name = item.value_str();
85                 } else if item.check_name("cfg") {
86                     let cfg = match item.meta_item_list() {
87                         Some(list) => list,
88                         None => continue, // skip like historical compilers
89                     };
90                     if cfg.is_empty() {
91                         self.tcx.sess.span_err(
92                             item.span(),
93                             "`cfg()` must have an argument",
94                         );
95                     } else if let cfg @ Some(..) = cfg[0].meta_item() {
96                         lib.cfg = cfg.cloned();
97                     } else {
98                         self.tcx.sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
99                     }
100                 } else if item.check_name("wasm_import_module") {
101                     match item.value_str() {
102                         Some(s) => lib.wasm_import_module = Some(s),
103                         None => {
104                             let msg = "must be of the form #[link(wasm_import_module = \"...\")]";
105                             self.tcx.sess.span_err(item.span(), msg);
106                         }
107                     }
108                 } else {
109                     // currently, like past compilers, ignore unknown
110                     // directives here.
111                 }
112             }
113
114             // In general we require #[link(name = "...")] but we allow
115             // #[link(wasm_import_module = "...")] without the `name`.
116             let requires_name = kind_specified || lib.wasm_import_module.is_none();
117             if lib.name.is_none() && requires_name {
118                 struct_span_err!(self.tcx.sess, m.span, E0459,
119                                  "#[link(...)] specified without \
120                                   `name = \"foo\"`")
121                     .span_label(m.span, "missing `name` argument")
122                     .emit();
123             }
124             self.register_native_lib(Some(m.span), lib);
125         }
126     }
127
128     fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem) {}
129     fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem) {}
130 }
131
132 impl<'a, 'tcx> Collector<'a, 'tcx> {
133     fn register_native_lib(&mut self, span: Option<Span>, lib: NativeLibrary) {
134         if lib.name.as_ref().map(|s| s.as_str().is_empty()).unwrap_or(false) {
135             match span {
136                 Some(span) => {
137                     struct_span_err!(self.tcx.sess, span, E0454,
138                                      "#[link(name = \"\")] given with empty name")
139                         .span_label(span, "empty name given")
140                         .emit();
141                 }
142                 None => {
143                     self.tcx.sess.err("empty library name given via `-l`");
144                 }
145             }
146             return
147         }
148         let is_osx = self.tcx.sess.target.target.options.is_like_osx;
149         if lib.kind == cstore::NativeFramework && !is_osx {
150             let msg = "native frameworks are only available on macOS targets";
151             match span {
152                 Some(span) => span_err!(self.tcx.sess, span, E0455, "{}", msg),
153                 None => self.tcx.sess.err(msg),
154             }
155         }
156         if lib.cfg.is_some() && !self.tcx.features().link_cfg {
157             feature_gate::emit_feature_err(&self.tcx.sess.parse_sess,
158                                            "link_cfg",
159                                            span.unwrap(),
160                                            GateIssue::Language,
161                                            "is feature gated");
162         }
163         if lib.kind == cstore::NativeStaticNobundle &&
164            !self.tcx.features().static_nobundle {
165             feature_gate::emit_feature_err(&self.tcx.sess.parse_sess,
166                                            "static_nobundle",
167                                            span.unwrap_or_else(|| syntax_pos::DUMMY_SP),
168                                            GateIssue::Language,
169                                            "kind=\"static-nobundle\" is feature gated");
170         }
171         self.libs.push(lib);
172     }
173
174     // Process libs passed on the command line
175     fn process_command_line(&mut self) {
176         // First, check for errors
177         let mut renames = FxHashSet::default();
178         for &(ref name, ref new_name, _) in &self.tcx.sess.opts.libs {
179             if let &Some(ref new_name) = new_name {
180                 let any_duplicate = self.libs
181                     .iter()
182                     .filter_map(|lib| lib.name.as_ref())
183                     .any(|n| n == name);
184                 if new_name.is_empty() {
185                     self.tcx.sess.err(
186                         &format!("an empty renaming target was specified for library `{}`",name));
187                 } else if !any_duplicate {
188                     self.tcx.sess.err(&format!("renaming of the library `{}` was specified, \
189                                                 however this crate contains no #[link(...)] \
190                                                 attributes referencing this library.", name));
191                 } else if renames.contains(name) {
192                     self.tcx.sess.err(&format!("multiple renamings were \
193                                                 specified for library `{}` .",
194                                                name));
195                 } else {
196                     renames.insert(name);
197                 }
198             }
199         }
200
201         // Update kind and, optionally, the name of all native libraries
202         // (there may be more than one) with the specified name.
203         for &(ref name, ref new_name, kind) in &self.tcx.sess.opts.libs {
204             let mut found = false;
205             for lib in self.libs.iter_mut() {
206                 let lib_name = match lib.name {
207                     Some(n) => n,
208                     None => continue,
209                 };
210                 if lib_name == name as &str {
211                     let mut changed = false;
212                     if let Some(k) = kind {
213                         lib.kind = k;
214                         changed = true;
215                     }
216                     if let &Some(ref new_name) = new_name {
217                         lib.name = Some(Symbol::intern(new_name));
218                         changed = true;
219                     }
220                     if !changed {
221                         let msg = format!("redundant linker flag specified for \
222                                            library `{}`", name);
223                         self.tcx.sess.warn(&msg);
224                     }
225
226                     found = true;
227                 }
228             }
229             if !found {
230                 // Add if not found
231                 let new_name = new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
232                 let lib = NativeLibrary {
233                     name: Some(Symbol::intern(new_name.unwrap_or(name))),
234                     kind: if let Some(k) = kind { k } else { cstore::NativeUnknown },
235                     cfg: None,
236                     foreign_module: None,
237                     wasm_import_module: None,
238                 };
239                 self.register_native_lib(None, lib);
240             }
241         }
242     }
243 }