summaryrefslogtreecommitdiff
path: root/libbindgen/src
diff options
context:
space:
mode:
Diffstat (limited to 'libbindgen/src')
-rw-r--r--libbindgen/src/codegen/mod.rs3
-rw-r--r--libbindgen/src/ir/context.rs67
-rw-r--r--libbindgen/src/ir/item.rs17
-rw-r--r--libbindgen/src/ir/module.rs19
-rw-r--r--libbindgen/src/lib.rs39
5 files changed, 125 insertions, 20 deletions
diff --git a/libbindgen/src/codegen/mod.rs b/libbindgen/src/codegen/mod.rs
index d12c3d2a..f8352cc8 100644
--- a/libbindgen/src/codegen/mod.rs
+++ b/libbindgen/src/codegen/mod.rs
@@ -329,7 +329,8 @@ impl CodeGenerator for Module {
}
};
- if !ctx.options().enable_cxx_namespaces {
+ if !ctx.options().enable_cxx_namespaces ||
+ (self.is_inline() && !ctx.options().conservative_inline_namespaces) {
codegen_self(result, &mut false);
return;
}
diff --git a/libbindgen/src/ir/context.rs b/libbindgen/src/ir/context.rs
index 2d877920..c3de2ca8 100644
--- a/libbindgen/src/ir/context.rs
+++ b/libbindgen/src/ir/context.rs
@@ -14,7 +14,7 @@ use super::derive::{CanDeriveCopy, CanDeriveDebug};
use super::int::IntKind;
use super::item::{Item, ItemCanonicalPath};
use super::item_kind::ItemKind;
-use super::module::Module;
+use super::module::{Module, ModuleKind};
use super::ty::{FloatKind, Type, TypeKind};
use super::type_collector::{ItemSet, TypeCollector};
use syntax::ast::Ident;
@@ -545,7 +545,7 @@ impl<'ctx> BindgenContext<'ctx> {
}
fn build_root_module(id: ItemId) -> Item {
- let module = Module::new(Some("root".into()));
+ let module = Module::new(Some("root".into()), ModuleKind::Normal);
Item::new(id, None, None, id, ItemKind::Module(module))
}
@@ -955,31 +955,64 @@ impl<'ctx> BindgenContext<'ctx> {
&self.options
}
+ /// Tokenizes a namespace cursor in order to get the name and kind of the
+ /// namespace,
+ fn tokenize_namespace(&self,
+ cursor: &clang::Cursor)
+ -> (Option<String>, ModuleKind) {
+ assert_eq!(cursor.kind(), ::clang_sys::CXCursor_Namespace,
+ "Be a nice person");
+ let tokens = match self.translation_unit.tokens(&cursor) {
+ Some(tokens) => tokens,
+ None => return (None, ModuleKind::Normal),
+ };
+
+ let mut iter = tokens.iter();
+ let mut kind = ModuleKind::Normal;
+ let mut found_namespace_keyword = false;
+ let mut module_name = None;
+ while let Some(token) = iter.next() {
+ match &*token.spelling {
+ "inline" => {
+ assert!(!found_namespace_keyword);
+ assert!(kind != ModuleKind::Inline);
+ kind = ModuleKind::Inline;
+ }
+ "namespace" => {
+ found_namespace_keyword = true;
+ }
+ "{" => {
+ assert!(found_namespace_keyword);
+ break;
+ }
+ name if found_namespace_keyword => {
+ module_name = Some(name.to_owned());
+ break;
+ }
+ _ => {
+ panic!("Unknown token while processing namespace: {:?}",
+ token);
+ }
+ }
+ };
+
+ (module_name, kind)
+ }
+
/// Given a CXCursor_Namespace cursor, return the item id of the
/// corresponding module, or create one on the fly.
pub fn module(&mut self, cursor: clang::Cursor) -> ItemId {
use clang_sys::*;
- assert!(cursor.kind() == CXCursor_Namespace, "Be a nice person");
+ assert_eq!(cursor.kind(), CXCursor_Namespace, "Be a nice person");
let cursor = cursor.canonical();
if let Some(id) = self.modules.get(&cursor) {
return *id;
}
- let module_id = self.next_item_id();
- let module_name = self.translation_unit
- .tokens(&cursor)
- .and_then(|tokens| {
- if tokens.len() <= 1 {
- None
- } else {
- match &*tokens[1].spelling {
- "{" => None,
- s => Some(s.to_owned()),
- }
- }
- });
+ let (module_name, kind) = self.tokenize_namespace(&cursor);
- let module = Module::new(module_name);
+ let module_id = self.next_item_id();
+ let module = Module::new(module_name, kind);
let module = Item::new(module_id,
None,
None,
diff --git a/libbindgen/src/ir/item.rs b/libbindgen/src/ir/item.rs
index e9960166..3810bc2f 100644
--- a/libbindgen/src/ir/item.rs
+++ b/libbindgen/src/ir/item.rs
@@ -853,6 +853,15 @@ impl Item {
format!("id_{}", self.id().as_usize())
}
+ /// Get a reference to this item's `Module`, or `None` if this is not a
+ /// `Module` item.
+ pub fn as_module(&self) -> Option<&Module> {
+ match self.kind {
+ ItemKind::Module(ref module) => Some(module),
+ _ => None,
+ }
+ }
+
/// Get a mutable reference to this item's `Module`, or `None` if this is
/// not a `Module` item.
pub fn as_module_mut(&mut self) -> Option<&mut Module> {
@@ -1304,7 +1313,13 @@ impl ItemCanonicalPath for Item {
let mut path: Vec<_> = target.ancestors(ctx)
.chain(iter::once(ctx.root_module()))
.map(|id| ctx.resolve_item(id))
- .filter(|item| item.is_module() || item.id() == target.id())
+ .filter(|item| {
+ item.id() == target.id() ||
+ item.as_module().map_or(false, |module| {
+ !module.is_inline() ||
+ ctx.options().conservative_inline_namespaces
+ })
+ })
.map(|item| {
ctx.resolve_item(item.name_target(ctx))
.name(ctx)
diff --git a/libbindgen/src/ir/module.rs b/libbindgen/src/ir/module.rs
index 5fae3932..002fe36e 100644
--- a/libbindgen/src/ir/module.rs
+++ b/libbindgen/src/ir/module.rs
@@ -5,20 +5,32 @@ use parse::{ClangSubItemParser, ParseError, ParseResult};
use parse_one;
use super::context::{BindgenContext, ItemId};
+/// Whether this module is inline or not.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum ModuleKind {
+ /// This module is not inline.
+ Normal,
+ /// This module is inline, as in `inline namespace foo {}`.
+ Inline,
+}
+
/// A module, as in, a C++ namespace.
#[derive(Clone, Debug)]
pub struct Module {
/// The name of the module, or none if it's anonymous.
name: Option<String>,
+ /// The kind of module this is.
+ kind: ModuleKind,
/// The children of this module, just here for convenience.
children_ids: Vec<ItemId>,
}
impl Module {
/// Construct a new `Module`.
- pub fn new(name: Option<String>) -> Self {
+ pub fn new(name: Option<String>, kind: ModuleKind) -> Self {
Module {
name: name,
+ kind: kind,
children_ids: vec![],
}
}
@@ -37,6 +49,11 @@ impl Module {
pub fn children(&self) -> &[ItemId] {
&self.children_ids
}
+
+ /// Whether this namespace is inline.
+ pub fn is_inline(&self) -> bool {
+ self.kind == ModuleKind::Inline
+ }
}
impl ClangSubItemParser for Module {
diff --git a/libbindgen/src/lib.rs b/libbindgen/src/lib.rs
index 1834b3f2..60b750de 100644
--- a/libbindgen/src/lib.rs
+++ b/libbindgen/src/lib.rs
@@ -305,6 +305,39 @@ impl Builder {
self
}
+ /// Treat inline namespaces conservatively.
+ ///
+ /// This is tricky, because in C++ is technically legal to override an item
+ /// defined in an inline namespace:
+ ///
+ /// ```cpp
+ /// inline namespace foo {
+ /// using Bar = int;
+ /// }
+ /// using Bar = long;
+ /// ```
+ ///
+ /// Even though referencing `Bar` is a compiler error.
+ ///
+ /// We want to support this (arguably esoteric) use case, but we don't want
+ /// to make the rest of bindgen users pay an usability penalty for that.
+ ///
+ /// To support this, we need to keep all the inline namespaces around, but
+ /// then bindgen usage is a bit more difficult, because you cannot
+ /// reference, e.g., `std::string` (you'd need to use the proper inline
+ /// namespace).
+ ///
+ /// We could complicate a lot of the logic to detect name collisions, and if
+ /// not detected generate a `pub use inline_ns::*` or something like that.
+ ///
+ /// That's probably something we can do if we see this option is needed in a
+ /// lot of cases, to improve it's usability, but my guess is that this is
+ /// not going to be too useful.
+ pub fn conservative_inline_namespaces(mut self) -> Builder {
+ self.options.conservative_inline_namespaces = true;
+ self
+ }
+
/// Ignore functions.
pub fn ignore_functions(mut self) -> Builder {
self.options.codegen_config.functions = false;
@@ -448,6 +481,11 @@ pub struct BindgenOptions {
/// Which kind of items should we generate? By default, we'll generate all
/// of them.
pub codegen_config: CodegenConfig,
+
+ /// Whether to treat inline namespaces conservatively.
+ ///
+ /// See the builder method description for more details.
+ pub conservative_inline_namespaces: bool,
}
impl BindgenOptions {
@@ -489,6 +527,7 @@ impl Default for BindgenOptions {
dummy_uses: None,
type_chooser: None,
codegen_config: CodegenConfig::all(),
+ conservative_inline_namespaces: false,
}
}
}