Repository: rust-lang/rust-bindgen (rust)

Issue:
Inadequate recognition of inline namespaces
The following is minimized from Xcode's C++ standard library implementation.

```cpp
// namespace.h

#pragma once
#define BEGIN_NAMESPACE namespace repro { inline namespace __1 {
#define END_NAMESPACE } }
```

```cpp
// main.cc

#include "namespace.h"

BEGIN_NAMESPACE

class duration {};

END_NAMESPACE
```

```console
$ bindgen main.cc --enable-cxx-namespaces
/* automatically generated by rust-bindgen 0.70.1 */

#[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
pub mod root {
    #[allow(unused_imports)]
    use self::super::root;
    pub mod repro {
        #[allow(unused_imports)]
        use self::super::super::root;
        pub mod __1 {
            #[allow(unused_imports)]
            use self::super::super::super::root;
            #[repr(C)]
            #[derive(Debug, Copy, Clone)]
            pub struct duration {
                pub _address: u8,
            }
            #[allow(clippy::unnecessary_operation, clippy::identity_op)]
            const _: () = {
                ["Size of duration"][::std::mem::size_of::<duration>() - 1usize];
                ["Alignment of duration"][::std::mem::align_of::<duration>() - 1usize];
            };
        }
    }
}
```

Bindgen is incorrectly generating `root::repro::__1::duration` instead of `root::repro::duration`. If the 2 defines are placed into main.cc instead of namespace.h, the behavior is different.

```console
#[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
pub mod root {
    #[allow(unused_imports)]
    use self::super::root;
    pub mod repro {
        #[allow(unused_imports)]
        use self::super::super::root;
        #[repr(C)]
        #[derive(Debug, Copy, Clone)]
        pub struct duration {
            pub _address: u8,
        }
        #[allow(clippy::unnecessary_operation, clippy::identity_op)]
        const _: () = {
            ["Size of duration"][::std::mem::size_of::<duration>() - 1usize];
            ["Alignment of duration"][::std::mem::align_of::<duration>() - 1usize];
        };
    }
}
```

As of current master, the logic by which bindgen decides whether a namespace is inline is here:

https://github.com/rust-lang/rust-bindgen/blob/af26991da8161f4d401d0d0532a05079449e7379/bindgen/ir/context.rs#L2267-L2286

It should be using https://docs.rs/clang-sys/1.8.1/clang_sys/fn.clang_Cursor_isInlineNamespace.html instead, which I confirmed fixes this bug.

Pull request description:
Recognize inline namespaces using clang's dedicated API for that
Fixes #2950.

Patch:
diff --git a/.github/workflows/bindgen.yml b/.github/workflows/bindgen.yml
index b54d6fdc..d1a11712 100644
--- a/.github/workflows/bindgen.yml
+++ b/.github/workflows/bindgen.yml
@@ -196,8 +196,8 @@ jobs:
       - name: Install libtinfo
         if: matrix.os == 'ubuntu-latest'
         run: |
-          sudo apt-get update
-          sudo apt-get install libtinfo5
+          wget https://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb
+          sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb
       - name: Run all the tests
         env:
           GITHUB_ACTIONS_OS: ${{matrix.os}}
diff --git a/bindgen/clang.rs b/bindgen/clang.rs
index e585fb31..47f07a38 100644
--- a/bindgen/clang.rs
+++ b/bindgen/clang.rs
@@ -970,6 +970,11 @@ impl Cursor {
             })
         }
     }
+
+    /// Is this cursor's referent a namespace that is inline?
+    pub(crate) fn is_inline_namespace(&self) -> bool {
+        unsafe { clang_Cursor_isInlineNamespace(self.x) != 0 }
+    }
 }
 
 /// A struct that owns the tokenizer result from a given cursor.
diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs
index 75f6a1ec..d32bb144 100644
--- a/bindgen/ir/context.rs
+++ b/bindgen/ir/context.rs
@@ -2328,6 +2328,10 @@ If you encounter an error missing from this list, please file an issue or a PR!"
             }
         }
 
+        if cursor.is_inline_namespace() {
+            kind = ModuleKind::Inline;
+        }
+
         (module_name, kind)
     }
 


Test patch:
diff --git a/bindgen-tests/tests/expectations/tests/inline_namespace_macro.rs b/bindgen-tests/tests/expectations/tests/inline_namespace_macro.rs
new file mode 100644
index 00000000..99ca607d
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/inline_namespace_macro.rs
@@ -0,0 +1,20 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
+pub mod root {
+    #[allow(unused_imports)]
+    use self::super::root;
+    pub mod repro {
+        #[allow(unused_imports)]
+        use self::super::super::root;
+        #[repr(C)]
+        #[derive(Debug, Default, Copy, Clone)]
+        pub struct duration {
+            pub _address: u8,
+        }
+        #[allow(clippy::unnecessary_operation, clippy::identity_op)]
+        const _: () = {
+            ["Size of duration"][::std::mem::size_of::<duration>() - 1usize];
+            ["Alignment of duration"][::std::mem::align_of::<duration>() - 1usize];
+        };
+    }
+}
diff --git a/bindgen-tests/tests/headers/inline_namespace_macro.hpp b/bindgen-tests/tests/headers/inline_namespace_macro.hpp
new file mode 100644
index 00000000..c7cf5caf
--- /dev/null
+++ b/bindgen-tests/tests/headers/inline_namespace_macro.hpp
@@ -0,0 +1,9 @@
+// bindgen-flags: --enable-cxx-namespaces -- -std=c++11
+
+#include "namespace/nsdefine.h"
+
+BEGIN_NAMESPACE
+
+class duration {};
+
+END_NAMESPACE
diff --git a/bindgen-tests/tests/headers/namespace/nsdefine.h b/bindgen-tests/tests/headers/namespace/nsdefine.h
new file mode 100644
index 00000000..6504fa9f
--- /dev/null
+++ b/bindgen-tests/tests/headers/namespace/nsdefine.h
@@ -0,0 +1,4 @@
+#pragma once
+
+#define BEGIN_NAMESPACE namespace repro { inline namespace __1 {
+#define END_NAMESPACE } }