1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use std::collections::hash_map::Entry;
6use std::collections::{HashMap, HashSet};
7use std::fs::File;
8use std::io::Read;
9use std::path::{Path as FilePath, PathBuf as FilePathBuf};
10
11use syn::ext::IdentExt;
12
13use crate::bindgen::bitflags;
14use crate::bindgen::cargo::{Cargo, PackageRef};
15use crate::bindgen::config::{Config, ParseConfig};
16use crate::bindgen::error::Error;
17use crate::bindgen::ir::{
18 AnnotationSet, AnnotationValue, Cfg, Constant, Documentation, Enum, Function, GenericParam,
19 GenericParams, ItemMap, OpaqueItem, Path, Static, Struct, Type, Typedef, Union,
20};
21use crate::bindgen::utilities::{SynAbiHelpers, SynAttributeHelpers, SynItemHelpers};
22
23const STD_CRATES: &[&str] = &[
24 "std",
25 "std_unicode",
26 "alloc",
27 "collections",
28 "core",
29 "proc_macro",
30];
31
32type ParseResult = Result<Parse, Error>;
33
34/// Parses a single rust source file, not following `mod` or `extern crate`.
35pub fn parse_src(src_file: &FilePath, config: &Config) -> ParseResult {
36 let mod_name = src_file.file_stem().unwrap().to_str().unwrap();
37 let mut config = config.clone();
38 config.parse = ParseConfig {
39 parse_deps: true,
40 ..ParseConfig::default()
41 };
42
43 let mut context = Parser {
44 binding_crate_name: mod_name.to_owned(),
45 config: &config,
46 lib: None,
47 parsed_crates: HashSet::new(),
48 cache_src: HashMap::new(),
49 cache_expanded_crate: HashMap::new(),
50 cfg_stack: Vec::new(),
51 out: Parse::new(),
52 };
53
54 let pkg_ref = PackageRef {
55 name: mod_name.to_owned(),
56 version: None,
57 };
58
59 context.parse_mod(&pkg_ref, src_file, 0)?;
60 context.out.source_files = context.cache_src.keys().map(|k| k.to_owned()).collect();
61 Ok(context.out)
62}
63
64/// Recursively parses a rust library starting at the root crate's directory.
65///
66/// Inside a crate, `mod` and `extern crate` declarations are followed
67/// and parsed. To find an external crate, the parser uses the `cargo metadata`
68/// command to find the location of dependencies.
69pub(crate) fn parse_lib(lib: Cargo, config: &Config) -> ParseResult {
70 let mut context: Parser<'_> = Parser {
71 binding_crate_name: lib.binding_crate_name().to_owned(),
72 config,
73 lib: Some(lib),
74 parsed_crates: HashSet::new(),
75 cache_src: HashMap::new(),
76 cache_expanded_crate: HashMap::new(),
77 cfg_stack: Vec::new(),
78 out: Parse::new(),
79 };
80
81 let binding_crate: PackageRef = context.lib.as_ref().unwrap().binding_crate_ref();
82 context.parse_crate(&binding_crate)?;
83 context.out.source_files = context.cache_src.keys().map(|k: &PathBuf| k.to_owned()).collect();
84 Ok(context.out)
85}
86
87#[derive(Debug, Clone)]
88struct Parser<'a> {
89 binding_crate_name: String,
90 lib: Option<Cargo>,
91 config: &'a Config,
92
93 parsed_crates: HashSet<String>,
94 cache_src: HashMap<FilePathBuf, Vec<syn::Item>>,
95 cache_expanded_crate: HashMap<String, Vec<syn::Item>>,
96
97 cfg_stack: Vec<Cfg>,
98
99 out: Parse,
100}
101
102impl<'a> Parser<'a> {
103 fn should_parse_dependency(&self, pkg_name: &str) -> bool {
104 if self.parsed_crates.contains(pkg_name) {
105 return false;
106 }
107
108 if !self.config.parse.parse_deps {
109 return false;
110 }
111
112 // Skip any whitelist or blacklist for expand
113 if self
114 .config
115 .parse
116 .expand
117 .crates
118 .iter()
119 .any(|name| name == pkg_name)
120 {
121 return true;
122 }
123
124 // If we have a whitelist, check it
125 if let Some(ref include) = self.config.parse.include {
126 if !include.iter().any(|name| name == pkg_name) {
127 debug!("Excluding crate {}", pkg_name);
128 return false;
129 }
130 }
131
132 // Check the blacklist
133 !STD_CRATES.contains(&pkg_name)
134 && !self
135 .config
136 .parse
137 .exclude
138 .iter()
139 .any(|name| name == pkg_name)
140 }
141
142 fn parse_crate(&mut self, pkg: &PackageRef) -> Result<(), Error> {
143 assert!(self.lib.is_some());
144 debug!("Parsing crate {}", pkg.name);
145 self.parsed_crates.insert(pkg.name.clone());
146
147 // Check if we should use cargo expand for this crate
148 if self.config.parse.expand.crates.contains(&pkg.name) {
149 self.parse_expand_crate(pkg)?;
150 } else {
151 // Parse the crate before the dependencies otherwise the same-named idents we
152 // want to generate bindings for would be replaced by the ones provided
153 // by the first dependency containing it.
154 let crate_src = self.lib.as_ref().unwrap().find_crate_src(pkg);
155
156 match crate_src {
157 Some(crate_src) => self.parse_mod(pkg, crate_src.as_path(), 0)?,
158 None => {
159 // This should be an error, but is common enough to just elicit a warning
160 warn!(
161 "Parsing crate `{}`: can't find lib.rs with `cargo metadata`. \
162 The crate may be available only on a particular platform, \
163 so consider setting `fetch_all_dependencies` in your cbindgen configuration.",
164 pkg.name
165 );
166 }
167 }
168 }
169
170 for (dep_pkg, cfg) in self.lib.as_ref().unwrap().dependencies(pkg) {
171 if !self.should_parse_dependency(&dep_pkg.name) {
172 continue;
173 }
174
175 if let Some(ref cfg) = cfg {
176 self.cfg_stack.push(cfg.clone());
177 }
178
179 self.parse_crate(&dep_pkg)?;
180
181 if cfg.is_some() {
182 self.cfg_stack.pop();
183 }
184 }
185
186 Ok(())
187 }
188
189 fn parse_expand_crate(&mut self, pkg: &PackageRef) -> Result<(), Error> {
190 assert!(self.lib.is_some());
191
192 let mod_items = {
193 if !self.cache_expanded_crate.contains_key(&pkg.name) {
194 let s = self
195 .lib
196 .as_ref()
197 .unwrap()
198 .expand_crate(
199 pkg,
200 self.config.parse.expand.all_features,
201 self.config.parse.expand.default_features,
202 &self.config.parse.expand.features,
203 self.config.parse.expand.profile,
204 )
205 .map_err(|x| Error::CargoExpand(pkg.name.clone(), x))?;
206 let i = syn::parse_file(&s).map_err(|x| Error::ParseSyntaxError {
207 crate_name: pkg.name.clone(),
208 src_path: "".to_owned(),
209 error: x,
210 })?;
211 self.cache_expanded_crate.insert(pkg.name.clone(), i.items);
212 }
213
214 self.cache_expanded_crate.get(&pkg.name).unwrap().clone()
215 };
216
217 self.process_mod(
218 pkg, None, None, &mod_items, 0, /* is_mod_rs = */ true,
219 /* is_inline = */ false,
220 )
221 }
222
223 fn parse_mod(
224 &mut self,
225 pkg: &PackageRef,
226 mod_path: &FilePath,
227 depth: usize,
228 ) -> Result<(), Error> {
229 let mod_items = match self.cache_src.entry(mod_path.to_path_buf()) {
230 Entry::Vacant(vacant_entry) => {
231 let mut s = String::new();
232 let mut f = File::open(mod_path).map_err(|_| Error::ParseCannotOpenFile {
233 crate_name: pkg.name.clone(),
234 src_path: mod_path.to_str().unwrap().to_owned(),
235 })?;
236
237 f.read_to_string(&mut s)
238 .map_err(|_| Error::ParseCannotOpenFile {
239 crate_name: pkg.name.clone(),
240 src_path: mod_path.to_str().unwrap().to_owned(),
241 })?;
242
243 let i = syn::parse_file(&s).map_err(|x| Error::ParseSyntaxError {
244 crate_name: pkg.name.clone(),
245 src_path: mod_path.to_string_lossy().into(),
246 error: x,
247 })?;
248
249 vacant_entry.insert(i.items).clone()
250 }
251 Entry::Occupied(occupied_entry) => occupied_entry.get().clone(),
252 };
253
254 // Compute module directory according to Rust 2018 rules
255 let submod_dir_2018;
256
257 let mod_dir = mod_path.parent().unwrap();
258
259 let is_mod_rs = depth == 0 || mod_path.ends_with("mod.rs");
260 let submod_dir = if is_mod_rs {
261 mod_dir
262 } else {
263 submod_dir_2018 = mod_path
264 .parent()
265 .unwrap()
266 .join(mod_path.file_stem().unwrap());
267 &submod_dir_2018
268 };
269
270 self.process_mod(
271 pkg,
272 Some(mod_dir),
273 Some(submod_dir),
274 &mod_items,
275 depth,
276 /* is_inline = */ false,
277 is_mod_rs,
278 )
279 }
280
281 /// `mod_dir` is the path to the current directory of the module. It may be
282 /// `None` for pre-expanded modules.
283 ///
284 /// `submod_dir` is the path to search submodules in by default, which might
285 /// be different for rust 2018 for example.
286 #[allow(clippy::too_many_arguments)]
287 fn process_mod(
288 &mut self,
289 pkg: &PackageRef,
290 mod_dir: Option<&FilePath>,
291 submod_dir: Option<&FilePath>,
292 items: &[syn::Item],
293 depth: usize,
294 is_inline: bool,
295 is_in_mod_rs: bool,
296 ) -> Result<(), Error> {
297 debug_assert_eq!(mod_dir.is_some(), submod_dir.is_some());
298 // We process the items first then the nested modules.
299 let nested_modules = self.out.load_syn_crate_mod(
300 self.config,
301 &self.binding_crate_name,
302 &pkg.name,
303 Cfg::join(&self.cfg_stack).as_ref(),
304 items,
305 );
306
307 for item in nested_modules {
308 let next_mod_name = item.ident.unraw().to_string();
309 let cfg = Cfg::load(&item.attrs);
310 if let Some(ref cfg) = cfg {
311 self.cfg_stack.push(cfg.clone());
312 }
313
314 if let Some((_, ref inline_items)) = item.content {
315 // TODO(emilio): This should use #[path] attribute if present,
316 // rather than next_mod_name.
317 let next_submod_dir = submod_dir.map(|dir| dir.join(&next_mod_name));
318 let next_mod_dir = mod_dir.map(|dir| dir.join(&next_mod_name));
319 self.process_mod(
320 pkg,
321 next_mod_dir.as_deref(),
322 next_submod_dir.as_deref(),
323 inline_items,
324 depth,
325 /* is_inline = */ true,
326 is_in_mod_rs,
327 )?;
328 } else if let Some(mod_dir) = mod_dir {
329 let submod_dir = submod_dir.unwrap();
330 let next_mod_path1 = submod_dir.join(next_mod_name.clone() + ".rs");
331 let next_mod_path2 = submod_dir.join(next_mod_name.clone()).join("mod.rs");
332
333 if next_mod_path1.exists() {
334 self.parse_mod(pkg, next_mod_path1.as_path(), depth + 1)?;
335 } else if next_mod_path2.exists() {
336 self.parse_mod(pkg, next_mod_path2.as_path(), depth + 1)?;
337 } else {
338 // Last chance to find a module path
339 let mut path_attr_found = false;
340 for attr in &item.attrs {
341 if let Ok(syn::Meta::NameValue(syn::MetaNameValue { path, lit, .. })) =
342 attr.parse_meta()
343 {
344 match lit {
345 syn::Lit::Str(ref path_lit) if path.is_ident("path") => {
346 path_attr_found = true;
347 // https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute
348 //
349 // For path attributes on modules not inside inline module blocks, the file path
350 // is relative to the directory the source file is located.
351 //
352 // For path attributes inside inline module blocks, the relative location of the
353 // file path depends on the kind of source file the path attribute is located
354 // in. "mod-rs" source files are root modules (such as lib.rs or main.rs) and
355 // modules with files named mod.rs. "non-mod-rs" source files are all other
356 // module files.
357 //
358 // Paths for path attributes inside inline module blocks in a mod-rs file are
359 // relative to the directory of the mod-rs file including the inline module
360 // components as directories. For non-mod-rs files, it is the same except the
361 // path starts with a directory with the name of the non-mod-rs module.
362 //
363 let base = if is_inline && !is_in_mod_rs {
364 submod_dir
365 } else {
366 mod_dir
367 };
368 self.parse_mod(pkg, &base.join(path_lit.value()), depth + 1)?;
369 break;
370 }
371 _ => (),
372 }
373 }
374 }
375
376 // This should be an error, but it's common enough to
377 // just elicit a warning
378 if !path_attr_found {
379 warn!(
380 "Parsing crate `{}`: can't find mod {}`.",
381 pkg.name, next_mod_name
382 );
383 }
384 }
385 } else {
386 warn!(
387 "Parsing expanded crate `{}`: can't find mod {}`.",
388 pkg.name, next_mod_name
389 );
390 }
391
392 if cfg.is_some() {
393 self.cfg_stack.pop();
394 }
395 }
396
397 Ok(())
398 }
399}
400
401#[derive(Debug, Clone)]
402pub struct Parse {
403 pub constants: ItemMap<Constant>,
404 pub globals: ItemMap<Static>,
405 pub enums: ItemMap<Enum>,
406 pub structs: ItemMap<Struct>,
407 pub unions: ItemMap<Union>,
408 pub opaque_items: ItemMap<OpaqueItem>,
409 pub typedefs: ItemMap<Typedef>,
410 pub functions: Vec<Function>,
411 pub source_files: Vec<FilePathBuf>,
412}
413
414impl Parse {
415 pub fn new() -> Parse {
416 Parse {
417 constants: ItemMap::default(),
418 globals: ItemMap::default(),
419 enums: ItemMap::default(),
420 structs: ItemMap::default(),
421 unions: ItemMap::default(),
422 opaque_items: ItemMap::default(),
423 typedefs: ItemMap::default(),
424 functions: Vec::new(),
425 source_files: Vec::new(),
426 }
427 }
428
429 pub fn add_std_types(&mut self) {
430 let mut add_opaque = |path: &str, generic_params: Vec<&str>| {
431 let path = Path::new(path);
432 let generic_params: Vec<_> = generic_params
433 .into_iter()
434 .map(GenericParam::new_type_param)
435 .collect();
436 self.opaque_items.try_insert(OpaqueItem::new(
437 path,
438 GenericParams(generic_params),
439 None,
440 AnnotationSet::new(),
441 Documentation::none(),
442 ))
443 };
444
445 add_opaque("String", vec![]);
446 add_opaque("Box", vec!["T"]);
447 add_opaque("RefCell", vec!["T"]);
448 add_opaque("Rc", vec!["T"]);
449 add_opaque("Arc", vec!["T"]);
450 add_opaque("Result", vec!["T", "E"]);
451 add_opaque("Option", vec!["T"]);
452 add_opaque("NonNull", vec!["T"]);
453 add_opaque("Vec", vec!["T"]);
454 add_opaque("HashMap", vec!["K", "V", "Hasher"]);
455 add_opaque("BTreeMap", vec!["K", "V"]);
456 add_opaque("HashSet", vec!["T"]);
457 add_opaque("BTreeSet", vec!["T"]);
458 add_opaque("LinkedList", vec!["T"]);
459 add_opaque("VecDeque", vec!["T"]);
460 add_opaque("ManuallyDrop", vec!["T"]);
461 add_opaque("MaybeUninit", vec!["T"]);
462 }
463
464 pub fn extend_with(&mut self, other: &Parse) {
465 self.constants.extend_with(&other.constants);
466 self.globals.extend_with(&other.globals);
467 self.enums.extend_with(&other.enums);
468 self.structs.extend_with(&other.structs);
469 self.unions.extend_with(&other.unions);
470 self.opaque_items.extend_with(&other.opaque_items);
471 self.typedefs.extend_with(&other.typedefs);
472 self.functions.extend_from_slice(&other.functions);
473 self.source_files.extend_from_slice(&other.source_files);
474 }
475
476 fn load_syn_crate_mod<'a>(
477 &mut self,
478 config: &Config,
479 binding_crate_name: &str,
480 crate_name: &str,
481 mod_cfg: Option<&Cfg>,
482 items: &'a [syn::Item],
483 ) -> Vec<&'a syn::ItemMod> {
484 let mut impls_with_assoc_consts = Vec::new();
485 let mut nested_modules = Vec::new();
486
487 for item in items {
488 if item.should_skip_parsing() {
489 continue;
490 }
491 match item {
492 syn::Item::ForeignMod(ref item) => {
493 self.load_syn_foreign_mod(
494 config,
495 binding_crate_name,
496 crate_name,
497 mod_cfg,
498 item,
499 );
500 }
501 syn::Item::Fn(ref item) => {
502 self.load_syn_fn(config, binding_crate_name, crate_name, mod_cfg, item);
503 }
504 syn::Item::Const(ref item) => {
505 self.load_syn_const(config, binding_crate_name, crate_name, mod_cfg, item);
506 }
507 syn::Item::Static(ref item) => {
508 self.load_syn_static(config, binding_crate_name, crate_name, mod_cfg, item);
509 }
510 syn::Item::Struct(ref item) => {
511 self.load_syn_struct(config, crate_name, mod_cfg, item);
512 }
513 syn::Item::Union(ref item) => {
514 self.load_syn_union(config, crate_name, mod_cfg, item);
515 }
516 syn::Item::Enum(ref item) => {
517 self.load_syn_enum(config, crate_name, mod_cfg, item);
518 }
519 syn::Item::Type(ref item) => {
520 self.load_syn_ty(crate_name, mod_cfg, item);
521 }
522 syn::Item::Impl(ref item_impl) => {
523 let has_assoc_const = item_impl
524 .items
525 .iter()
526 .any(|item| matches!(item, syn::ImplItem::Const(_)));
527 if has_assoc_const {
528 impls_with_assoc_consts.push(item_impl);
529 }
530
531 if let syn::Type::Path(ref path) = *item_impl.self_ty {
532 if let Some(type_name) = path.path.get_ident() {
533 for method in item_impl.items.iter().filter_map(|item| match item {
534 syn::ImplItem::Method(method) => Some(method),
535 _ => None,
536 }) {
537 self.load_syn_method(
538 config,
539 binding_crate_name,
540 crate_name,
541 mod_cfg,
542 &Path::new(type_name.unraw().to_string()),
543 method,
544 )
545 }
546 }
547 }
548 }
549 syn::Item::Macro(ref item) => {
550 self.load_builtin_macro(config, crate_name, mod_cfg, item);
551 }
552 syn::Item::Mod(ref item) => {
553 nested_modules.push(item);
554 }
555 _ => {}
556 }
557 }
558
559 for item_impl in impls_with_assoc_consts {
560 self.load_syn_assoc_consts_from_impl(crate_name, mod_cfg, item_impl)
561 }
562
563 nested_modules
564 }
565
566 fn load_syn_assoc_consts_from_impl(
567 &mut self,
568 crate_name: &str,
569 mod_cfg: Option<&Cfg>,
570 item_impl: &syn::ItemImpl,
571 ) {
572 let associated_constants = item_impl.items.iter().filter_map(|item| match item {
573 syn::ImplItem::Const(ref associated_constant) => Some(associated_constant),
574 _ => None,
575 });
576 self.load_syn_assoc_consts(
577 crate_name,
578 mod_cfg,
579 &item_impl.self_ty,
580 associated_constants,
581 );
582 }
583
584 /// Enters a `extern "C" { }` declaration and loads function declarations.
585 fn load_syn_foreign_mod(
586 &mut self,
587 config: &Config,
588 binding_crate_name: &str,
589 crate_name: &str,
590 mod_cfg: Option<&Cfg>,
591 item: &syn::ItemForeignMod,
592 ) {
593 if !item.abi.is_c() {
594 info!("Skip {} - (extern block must be extern C).", crate_name);
595 return;
596 }
597
598 for foreign_item in &item.items {
599 if let syn::ForeignItem::Fn(ref function) = *foreign_item {
600 if !config
601 .parse
602 .should_generate_top_level_item(crate_name, binding_crate_name)
603 {
604 info!(
605 "Skip {}::{} - (fn's outside of the binding crate are not used).",
606 crate_name, &function.sig.ident
607 );
608 return;
609 }
610 let path = Path::new(function.sig.ident.unraw().to_string());
611 match Function::load(path, None, &function.sig, true, &function.attrs, mod_cfg) {
612 Ok(func) => {
613 info!("Take {}::{}.", crate_name, &function.sig.ident);
614
615 self.functions.push(func);
616 }
617 Err(msg) => {
618 error!(
619 "Cannot use fn {}::{} ({}).",
620 crate_name, &function.sig.ident, msg
621 );
622 }
623 }
624 }
625 }
626 }
627
628 /// Loads a `fn` declaration inside an `impl` block, if the type is a simple identifier
629 fn load_syn_method(
630 &mut self,
631 config: &Config,
632 binding_crate_name: &str,
633 crate_name: &str,
634 mod_cfg: Option<&Cfg>,
635 self_type: &Path,
636 item: &syn::ImplItemMethod,
637 ) {
638 self.load_fn_declaration(
639 config,
640 binding_crate_name,
641 crate_name,
642 mod_cfg,
643 item,
644 Some(self_type),
645 &item.sig,
646 &item.attrs,
647 )
648 }
649
650 /// Loads a `fn` declaration
651 fn load_syn_fn(
652 &mut self,
653 config: &Config,
654 binding_crate_name: &str,
655 crate_name: &str,
656 mod_cfg: Option<&Cfg>,
657 item: &syn::ItemFn,
658 ) {
659 self.load_fn_declaration(
660 config,
661 binding_crate_name,
662 crate_name,
663 mod_cfg,
664 item,
665 None,
666 &item.sig,
667 &item.attrs,
668 );
669 }
670
671 #[allow(clippy::too_many_arguments)]
672 fn load_fn_declaration(
673 &mut self,
674 config: &Config,
675 binding_crate_name: &str,
676 crate_name: &str,
677 mod_cfg: Option<&Cfg>,
678 named_symbol: &dyn SynItemHelpers,
679 self_type: Option<&Path>,
680 sig: &syn::Signature,
681 attrs: &[syn::Attribute],
682 ) {
683 if !config
684 .parse
685 .should_generate_top_level_item(crate_name, binding_crate_name)
686 {
687 info!(
688 "Skip {}::{} - (fn's outside of the binding crate are not used).",
689 crate_name, &sig.ident
690 );
691 return;
692 }
693
694 let loggable_item_name = || {
695 let mut items = Vec::with_capacity(3);
696 items.push(crate_name.to_owned());
697 if let Some(ref self_type) = self_type {
698 items.push(self_type.to_string());
699 }
700 items.push(sig.ident.unraw().to_string());
701 items.join("::")
702 };
703
704 let is_extern_c = sig.abi.is_omitted() || sig.abi.is_c();
705 let exported_name = named_symbol.exported_name();
706
707 match (is_extern_c, exported_name) {
708 (true, Some(exported_name)) => {
709 let path = Path::new(exported_name);
710 match Function::load(path, self_type, sig, false, attrs, mod_cfg) {
711 Ok(func) => {
712 info!("Take {}.", loggable_item_name());
713 self.functions.push(func);
714 }
715 Err(msg) => {
716 error!("Cannot use fn {} ({}).", loggable_item_name(), msg);
717 }
718 }
719 }
720 (true, None) => {
721 warn!(
722 "Skipping {} - (not `no_mangle`, and has no `export_name` attribute)",
723 loggable_item_name()
724 );
725 }
726 (false, Some(_exported_name)) => {
727 warn!("Skipping {} - (not `extern \"C\"`", loggable_item_name());
728 }
729 (false, None) => {}
730 }
731 }
732
733 /// Loads associated `const` declarations
734 fn load_syn_assoc_consts<'a, I>(
735 &mut self,
736 crate_name: &str,
737 mod_cfg: Option<&Cfg>,
738 impl_ty: &syn::Type,
739 items: I,
740 ) where
741 I: IntoIterator<Item = &'a syn::ImplItemConst>,
742 {
743 let ty = match Type::load(impl_ty) {
744 Ok(ty) => ty,
745 Err(e) => {
746 warn!("Skipping associated constants for {:?}: {:?}", impl_ty, e);
747 return;
748 }
749 };
750
751 let ty = match ty {
752 Some(ty) => ty,
753 None => return,
754 };
755
756 let impl_path = match ty.get_root_path() {
757 Some(p) => p,
758 None => {
759 warn!(
760 "Couldn't find path for {:?}, skipping associated constants",
761 ty
762 );
763 return;
764 }
765 };
766
767 for item in items.into_iter() {
768 if let syn::Visibility::Public(_) = item.vis {
769 } else {
770 warn!("Skip {}::{} - (not `pub`).", crate_name, &item.ident);
771 return;
772 }
773
774 let path = Path::new(item.ident.unraw().to_string());
775 match Constant::load(
776 path,
777 mod_cfg,
778 &item.ty,
779 &item.expr,
780 &item.attrs,
781 Some(impl_path.clone()),
782 ) {
783 Ok(constant) => {
784 info!("Take {}::{}::{}.", crate_name, impl_path, &item.ident);
785 let mut any = false;
786 self.structs.for_items_mut(&impl_path, |item| {
787 any = true;
788 item.add_associated_constant(constant.clone());
789 });
790 // Handle associated constants to other item types that are
791 // not structs like enums or such as regular constants.
792 if !any && !self.constants.try_insert(constant) {
793 error!(
794 "Conflicting name for constant {}::{}::{}.",
795 crate_name, impl_path, &item.ident,
796 );
797 }
798 }
799 Err(msg) => {
800 warn!("Skip {}::{} - ({})", crate_name, &item.ident, msg);
801 }
802 }
803 }
804 }
805
806 /// Loads a `const` declaration
807 fn load_syn_const(
808 &mut self,
809 config: &Config,
810 binding_crate_name: &str,
811 crate_name: &str,
812 mod_cfg: Option<&Cfg>,
813 item: &syn::ItemConst,
814 ) {
815 if !config
816 .parse
817 .should_generate_top_level_item(crate_name, binding_crate_name)
818 {
819 info!(
820 "Skip {}::{} - (const's outside of the binding crate are not used).",
821 crate_name, &item.ident
822 );
823 return;
824 }
825
826 if let syn::Visibility::Public(_) = item.vis {
827 } else {
828 warn!("Skip {}::{} - (not `pub`).", crate_name, &item.ident);
829 return;
830 }
831
832 let path = Path::new(item.ident.unraw().to_string());
833 match Constant::load(path, mod_cfg, &item.ty, &item.expr, &item.attrs, None) {
834 Ok(constant) => {
835 info!("Take {}::{}.", crate_name, &item.ident);
836
837 let full_name = constant.path.clone();
838 if !self.constants.try_insert(constant) {
839 error!("Conflicting name for constant {}", full_name);
840 }
841 }
842 Err(msg) => {
843 warn!("Skip {}::{} - ({})", crate_name, &item.ident, msg);
844 }
845 }
846 }
847
848 /// Loads a `static` declaration
849 fn load_syn_static(
850 &mut self,
851 config: &Config,
852 binding_crate_name: &str,
853 crate_name: &str,
854 mod_cfg: Option<&Cfg>,
855 item: &syn::ItemStatic,
856 ) {
857 if !config
858 .parse
859 .should_generate_top_level_item(crate_name, binding_crate_name)
860 {
861 info!(
862 "Skip {}::{} - (static's outside of the binding crate are not used).",
863 crate_name, &item.ident
864 );
865 return;
866 }
867
868 if let Some(exported_name) = item.exported_name() {
869 let path = Path::new(exported_name);
870 match Static::load(path, item, mod_cfg) {
871 Ok(constant) => {
872 info!("Take {}::{}.", crate_name, &item.ident);
873 self.globals.try_insert(constant);
874 }
875 Err(msg) => {
876 warn!("Skip {}::{} - ({})", crate_name, &item.ident, msg);
877 }
878 }
879 } else {
880 warn!("Skip {}::{} - (not `no_mangle`).", crate_name, &item.ident);
881 }
882 }
883
884 /// Loads a `struct` declaration
885 fn load_syn_struct(
886 &mut self,
887 config: &Config,
888 crate_name: &str,
889 mod_cfg: Option<&Cfg>,
890 item: &syn::ItemStruct,
891 ) {
892 match Struct::load(&config.layout, item, mod_cfg) {
893 Ok(st) => {
894 info!("Take {}::{}.", crate_name, &item.ident);
895 self.structs.try_insert(st);
896 }
897 Err(msg) => {
898 info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg);
899 let path = Path::new(item.ident.unraw().to_string());
900 self.opaque_items.try_insert(
901 OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(),
902 );
903 }
904 }
905 }
906
907 /// Loads a `union` declaration
908 fn load_syn_union(
909 &mut self,
910 config: &Config,
911 crate_name: &str,
912 mod_cfg: Option<&Cfg>,
913 item: &syn::ItemUnion,
914 ) {
915 match Union::load(&config.layout, item, mod_cfg) {
916 Ok(st) => {
917 info!("Take {}::{}.", crate_name, &item.ident);
918
919 self.unions.try_insert(st);
920 }
921 Err(msg) => {
922 info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg);
923 let path = Path::new(item.ident.unraw().to_string());
924 self.opaque_items.try_insert(
925 OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(),
926 );
927 }
928 }
929 }
930
931 /// Loads a `enum` declaration
932 fn load_syn_enum(
933 &mut self,
934 config: &Config,
935 crate_name: &str,
936 mod_cfg: Option<&Cfg>,
937 item: &syn::ItemEnum,
938 ) {
939 match Enum::load(item, mod_cfg, config) {
940 Ok(en) => {
941 info!("Take {}::{}.", crate_name, &item.ident);
942 self.enums.try_insert(en);
943 }
944 Err(msg) => {
945 info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg);
946 let path = Path::new(item.ident.unraw().to_string());
947 self.opaque_items.try_insert(
948 OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(),
949 );
950 }
951 }
952 }
953
954 /// Loads a `type` declaration
955 fn load_syn_ty(&mut self, crate_name: &str, mod_cfg: Option<&Cfg>, item: &syn::ItemType) {
956 match Typedef::load(item, mod_cfg) {
957 Ok(st) => {
958 info!("Take {}::{}.", crate_name, &item.ident);
959
960 self.typedefs.try_insert(st);
961 }
962 Err(msg) => {
963 info!("Take {}::{} - opaque ({}).", crate_name, &item.ident, msg);
964 let path = Path::new(item.ident.unraw().to_string());
965 self.opaque_items.try_insert(
966 OpaqueItem::load(path, &item.generics, &item.attrs, mod_cfg).unwrap(),
967 );
968 }
969 }
970 }
971
972 fn load_builtin_macro(
973 &mut self,
974 config: &Config,
975 crate_name: &str,
976 mod_cfg: Option<&Cfg>,
977 item: &syn::ItemMacro,
978 ) {
979 let name = match item.mac.path.segments.last() {
980 Some(n) => n.ident.unraw().to_string(),
981 None => return,
982 };
983
984 if name != "bitflags" || !config.macro_expansion.bitflags {
985 return;
986 }
987
988 let bitflags = match bitflags::parse(item.mac.tokens.clone()) {
989 Ok(bf) => bf,
990 Err(e) => {
991 warn!("Failed to parse bitflags invocation: {:?}", e);
992 return;
993 }
994 };
995
996 let (struct_, impl_) = bitflags.expand();
997 if let Some(struct_) = struct_ {
998 self.load_syn_struct(config, crate_name, mod_cfg, &struct_);
999 }
1000 if let syn::Type::Path(ref path) = *impl_.self_ty {
1001 if let Some(type_name) = path.path.get_ident() {
1002 self.structs
1003 .for_items_mut(&Path::new(type_name.unraw().to_string()), |item| {
1004 item.annotations
1005 .add_default("internal-derive-bitflags", AnnotationValue::Bool(true));
1006 });
1007 }
1008 }
1009 self.load_syn_assoc_consts_from_impl(crate_name, mod_cfg, &impl_)
1010 }
1011}
1012