Skip to content

Commit b71bb6f

Browse files
committed
Workaround for expansion of function-like macros
This commit resolves an issue where macros that evaluate to a constant but have a function like macro in the macro body would not be properly expanded by cexpr. This adds an opt-in option to use Clang on intermediary files to evaluate the macros one by one. This is opt-in largely because of the compile time implications.
1 parent 3b5ce9c commit b71bb6f

File tree

7 files changed

+342
-71
lines changed

7 files changed

+342
-71
lines changed

bindgen-cli/options.rs

+16
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,12 @@ struct BindgenCommand {
400400
/// Wrap unsafe operations in unsafe blocks.
401401
#[arg(long)]
402402
wrap_unsafe_ops: bool,
403+
/// Enable fallback for clang macro parsing.
404+
#[arg(long)]
405+
clang_macro_fallback: bool,
406+
/// Set path for temporary files generated by fallback for clang macro parsing.
407+
#[arg(long)]
408+
clang_macro_fallback_build_dir: Option<PathBuf>,
403409
/// Derive custom traits on any kind of type. The CUSTOM value must be of the shape REGEX=DERIVE where DERIVE is a coma-separated list of derive macros.
404410
#[arg(long, value_name = "CUSTOM", value_parser = parse_custom_derive)]
405411
with_derive_custom: Vec<(Vec<String>, String)>,
@@ -554,6 +560,8 @@ where
554560
merge_extern_blocks,
555561
override_abi,
556562
wrap_unsafe_ops,
563+
clang_macro_fallback,
564+
clang_macro_fallback_build_dir,
557565
with_derive_custom,
558566
with_derive_custom_struct,
559567
with_derive_custom_enum,
@@ -1023,6 +1031,14 @@ where
10231031
builder = builder.wrap_unsafe_ops(true);
10241032
}
10251033

1034+
if clang_macro_fallback {
1035+
builder = builder.clang_macro_fallback();
1036+
}
1037+
1038+
if let Some(path) = clang_macro_fallback_build_dir {
1039+
builder = builder.clang_macro_fallback_build_dir(path);
1040+
}
1041+
10261042
#[derive(Debug)]
10271043
struct CustomDeriveCallback {
10281044
derives: Vec<String>,

bindgen-tests/tests/expectations/tests/issue-753.rs

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// bindgen-flags: --clang-macro-fallback
2+
3+
#define UINT32_C(c) c ## U
4+
5+
#define CONST UINT32_C(5)
6+
#define OTHER_CONST UINT32_C(6)
7+
#define LARGE_CONST UINT32_C(6 << 8)

bindgen/clang.rs

+96
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::cmp;
1010

1111
use std::ffi::{CStr, CString};
1212
use std::fmt;
13+
use std::fs::OpenOptions;
1314
use std::hash::Hash;
1415
use std::hash::Hasher;
1516
use std::os::raw::{c_char, c_int, c_longlong, c_uint, c_ulong, c_ulonglong};
@@ -1868,6 +1869,27 @@ impl TranslationUnit {
18681869
}
18691870
}
18701871

1872+
/// Save a translation unit to the given file.
1873+
pub(crate) fn save(&mut self, file: &str) -> Result<(), CXSaveError> {
1874+
let file = if let Ok(cstring) = CString::new(file) {
1875+
cstring
1876+
} else {
1877+
return Err(CXSaveError_Unknown);
1878+
};
1879+
let ret = unsafe {
1880+
clang_saveTranslationUnit(
1881+
self.x,
1882+
file.as_ptr(),
1883+
clang_defaultSaveOptions(self.x),
1884+
)
1885+
};
1886+
if ret != 0 {
1887+
Err(ret)
1888+
} else {
1889+
Ok(())
1890+
}
1891+
}
1892+
18711893
/// Is this the null translation unit?
18721894
pub(crate) fn is_null(&self) -> bool {
18731895
self.x.is_null()
@@ -1882,6 +1904,80 @@ impl Drop for TranslationUnit {
18821904
}
18831905
}
18841906

1907+
/// Translation unit used for macro fallback parsing
1908+
pub(crate) struct FallbackTranslationUnit {
1909+
file_path: String,
1910+
idx: Box<Index>,
1911+
tu: TranslationUnit,
1912+
}
1913+
1914+
impl fmt::Debug for FallbackTranslationUnit {
1915+
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1916+
write!(fmt, "FallbackTranslationUnit {{ }}")
1917+
}
1918+
}
1919+
1920+
impl FallbackTranslationUnit {
1921+
/// Create a new fallback translation unit
1922+
pub(crate) fn new(file: String, c_args: &[Box<str>]) -> Option<Self> {
1923+
// Create empty file
1924+
OpenOptions::new()
1925+
.write(true)
1926+
.create(true)
1927+
.truncate(true)
1928+
.open(&file)
1929+
.ok()?;
1930+
1931+
let f_index = Box::new(Index::new(true, false));
1932+
let f_translation_unit = TranslationUnit::parse(
1933+
&f_index,
1934+
&file,
1935+
c_args,
1936+
&[],
1937+
CXTranslationUnit_None,
1938+
)?;
1939+
Some(FallbackTranslationUnit {
1940+
file_path: file,
1941+
tu: f_translation_unit,
1942+
idx: f_index,
1943+
})
1944+
}
1945+
1946+
/// Get reference to underlying translation unit.
1947+
pub(crate) fn translation_unit(&self) -> &TranslationUnit {
1948+
&self.tu
1949+
}
1950+
1951+
/// Reparse a translation unit.
1952+
pub(crate) fn reparse(
1953+
&mut self,
1954+
unsaved_contents: &str,
1955+
) -> Result<(), CXErrorCode> {
1956+
let unsaved = &[UnsavedFile::new(&self.file_path, unsaved_contents)];
1957+
let mut c_unsaved: Vec<CXUnsavedFile> =
1958+
unsaved.iter().map(|f| f.x).collect();
1959+
let ret = unsafe {
1960+
clang_reparseTranslationUnit(
1961+
self.tu.x,
1962+
unsaved.len() as c_uint,
1963+
c_unsaved.as_mut_ptr(),
1964+
clang_defaultReparseOptions(self.tu.x),
1965+
)
1966+
};
1967+
if ret != 0 {
1968+
Err(ret)
1969+
} else {
1970+
Ok(())
1971+
}
1972+
}
1973+
}
1974+
1975+
impl Drop for FallbackTranslationUnit {
1976+
fn drop(&mut self) {
1977+
let _ = std::fs::remove_file(&self.file_path);
1978+
}
1979+
}
1980+
18851981
/// A diagnostic message generated while parsing a translation unit.
18861982
pub(crate) struct Diagnostic {
18871983
x: CXDiagnostic,

0 commit comments

Comments
 (0)