diff --git a/bindgen-tests/tests/expectations/tests/source-order-recursive.rs b/bindgen-tests/tests/expectations/tests/source-order-recursive.rs new file mode 100644 index 0000000000..46cdf5062d --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/source-order-recursive.rs @@ -0,0 +1,67 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct foo {} +#[test] +fn bindgen_test_layout_foo() { + assert_eq!( + ::std::mem::size_of::(), + 0usize, + concat!("Size of: ", stringify!(foo)), + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(foo)), + ); +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct bar { + pub field: foo, +} +#[test] +fn bindgen_test_layout_bar() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 0usize, + concat!("Size of: ", stringify!(bar)), + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(bar)), + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).field) as usize - ptr as usize }, + 0usize, + concat!("Offset of field: ", stringify!(bar), "::", stringify!(field)), + ); +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct baz { + pub field: bar, +} +#[test] +fn bindgen_test_layout_baz() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 0usize, + concat!("Size of: ", stringify!(baz)), + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(baz)), + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).field) as usize - ptr as usize }, + 0usize, + concat!("Offset of field: ", stringify!(baz), "::", stringify!(field)), + ); +} diff --git a/bindgen-tests/tests/headers/source-order-recursive.h b/bindgen-tests/tests/headers/source-order-recursive.h new file mode 100644 index 0000000000..6d589a7f7c --- /dev/null +++ b/bindgen-tests/tests/headers/source-order-recursive.h @@ -0,0 +1,14 @@ +// bindgen-flags: -- -Itests/headers -Itests/headers/source-order-recursive + +#ifndef A_H +#define A_H + +struct foo {}; + +#include "source-order-recursive-2.h" + +struct baz { + struct bar field; +}; + +#endif diff --git a/bindgen-tests/tests/headers/source-order-recursive/source-order-recursive-2.h b/bindgen-tests/tests/headers/source-order-recursive/source-order-recursive-2.h new file mode 100644 index 0000000000..01ff1890b1 --- /dev/null +++ b/bindgen-tests/tests/headers/source-order-recursive/source-order-recursive-2.h @@ -0,0 +1,5 @@ +#include "source-order-recursive.h" + +struct bar { + struct foo field; +}; diff --git a/bindgen/clang.rs b/bindgen/clang.rs index 534ad8c777..7bbda53932 100644 --- a/bindgen/clang.rs +++ b/bindgen/clang.rs @@ -7,13 +7,14 @@ use crate::ir::context::{BindgenContext, IncludeLocation}; use clang_sys::*; use std::cmp; - use std::convert::TryInto; use std::ffi::{CStr, CString}; use std::fmt; use std::hash::Hash; use std::hash::Hasher; use std::os::raw::{c_char, c_int, c_longlong, c_uint, c_ulong, c_ulonglong}; +use std::path::Path; +use std::path::PathBuf; use std::{mem, ptr, slice}; /// Type representing a clang attribute. @@ -395,8 +396,9 @@ impl Cursor { offset: offset.try_into().unwrap(), } } else { + let file_name = cxstring_into_string(clang_getFileName(file)); SourceLocation::File { - file_name: cxstring_into_string(clang_getFileName(file)), + file_path: absolutize_path(file_name), line: line.try_into().unwrap(), column: column.try_into().unwrap(), offset: offset.try_into().unwrap(), @@ -534,8 +536,12 @@ impl Cursor { let mut children = self.collect_children(); for child in &children { if child.kind() == CXCursor_InclusionDirective { - if let Some(included_file) = child.get_included_file_name() { - ctx.add_include(included_file, child.location()); + if let Some(included_file_name) = child.get_included_file_name() + { + ctx.add_include( + absolutize_path(included_file_name), + child.location(), + ); } } } @@ -1574,7 +1580,7 @@ pub(crate) enum SourceLocation { /// Location in a source file. File { /// Name of the source file. - file_name: String, + file_path: PathBuf, /// Line in the source file. line: usize, /// Column in the source file. @@ -1584,6 +1590,18 @@ pub(crate) enum SourceLocation { }, } +fn absolutize_path>(path: P) -> PathBuf { + let path = path.as_ref(); + + if path.is_relative() { + std::env::current_dir() + .expect("Cannot retrieve current directory") + .join(path) + } else { + path.to_owned() + } +} + impl SourceLocation { /// Locations of built-in items provided by the compiler (which don't have a source file), /// are sorted first. Remaining locations are sorted by their position in the source file. @@ -1608,26 +1626,26 @@ impl SourceLocation { } ( SourceLocation::File { - file_name, offset, .. + file_path, offset, .. }, SourceLocation::File { - file_name: other_file_name, + file_path: other_file_path, offset: other_offset, .. }, ) => { - if file_name == other_file_name { + if file_path == other_file_path { return offset.cmp(other_offset); } // If `file` is transitively included via `ancestor_file`, // find the offset of the include directive in `ancestor_file`. - let offset_in_ancestor = |file: &str, ancestor_file: &str| { + let offset_in_ancestor = |file: &Path, ancestor_file: &Path| { let mut file = file; while file != ancestor_file { let include_location = ctx.include_location(file); file = if let IncludeLocation::File { - file_name: file, + file_path: file, offset, .. } = include_location @@ -1646,20 +1664,20 @@ impl SourceLocation { }; if let Some(offset) = - offset_in_ancestor(file_name, other_file_name) + offset_in_ancestor(file_path, other_file_path) { return offset.cmp(other_offset); } if let Some(other_offset) = - offset_in_ancestor(other_file_name, file_name) + offset_in_ancestor(other_file_path, file_path) { return offset.cmp(other_offset); } // If the source files are siblings, compare their include locations. - let parent = ctx.include_location(file_name); - let other_parent = ctx.include_location(other_file_name); + let parent = ctx.include_location(file_path); + let other_parent = ctx.include_location(other_file_path); parent.cmp_by_source_order(other_parent, ctx) } } @@ -1671,11 +1689,11 @@ impl fmt::Display for SourceLocation { match self { Self::Builtin { .. } => "built-in".fmt(f), Self::File { - file_name, + file_path, line, column, .. - } => write!(f, "{}:{}:{}", file_name, line, column), + } => write!(f, "{}:{}:{}", file_path.display(), line, column), } } } @@ -1906,6 +1924,15 @@ impl TranslationUnit { } } + /// Get the source file path of this translation unit. + pub(crate) fn path(&self) -> PathBuf { + let file_name = unsafe { + cxstring_into_string(clang_getTranslationUnitSpelling(self.x)) + }; + + absolutize_path(file_name) + } + /// Is this the null translation unit? pub(crate) fn is_null(&self) -> bool { self.x.is_null() diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index f203daaa53..30b075da8b 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4354,17 +4354,17 @@ fn unsupported_abi_diagnostic( ); if let Some(crate::clang::SourceLocation::File { - file_name, + file_path, line, column, .. }) = location.cloned() { - if let Ok(Some(source)) = get_line(&file_name, line) { + if let Ok(Some(source)) = get_line(&file_path, line) { let mut slice = Slice::default(); slice .with_source(source) - .with_location(file_name, line, column); + .with_location(file_path, line, column); diag.add_slice(slice); } } @@ -4395,17 +4395,17 @@ fn variadic_fn_diagnostic( .add_annotation("No code will be generated for this function.", Level::Note); if let Some(crate::clang::SourceLocation::File { - file_name, + file_path, line, column, .. }) = location.cloned() { - if let Ok(Some(source)) = get_line(&file_name, line) { + if let Ok(Some(source)) = get_line(&file_path, line) { let mut slice = Slice::default(); slice .with_source(source) - .with_location(file_name, line, column); + .with_location(file_path, line, column); diag.add_slice(slice); } } diff --git a/bindgen/diagnostics.rs b/bindgen/diagnostics.rs index f765afe970..0cb53a3736 100644 --- a/bindgen/diagnostics.rs +++ b/bindgen/diagnostics.rs @@ -2,8 +2,8 @@ //! //! The entry point of this module is the [`Diagnostic`] type. -use std::fmt::Write; use std::io::{self, BufRead, BufReader}; +use std::path::Path; use std::{borrow::Cow, fs::File}; use annotate_snippets::{ @@ -162,25 +162,24 @@ impl<'a> Slice<'a> { } /// Set the file, line and column. - pub(crate) fn with_location( + pub(crate) fn with_location>( &mut self, - mut name: String, + path: P, line: usize, col: usize, ) -> &mut Self { - write!(name, ":{}:{}", line, col) - .expect("Writing to a string cannot fail"); - self.filename = Some(name); + self.filename = + Some(format!("{}:{}:{}", path.as_ref().display(), line, col)); self.line = Some(line); self } } -pub(crate) fn get_line( - filename: &str, +pub(crate) fn get_line>( + file_path: P, line: usize, ) -> io::Result> { - let file = BufReader::new(File::open(filename)?); + let file = BufReader::new(File::open(file_path.as_ref())?); if let Some(line) = file.lines().nth(line.wrapping_sub(1)) { return line.map(Some); } diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index f61b654ed1..74d51b058b 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -30,6 +30,8 @@ use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::collections::{BTreeSet, HashMap as StdHashMap}; use std::iter::IntoIterator; +use std::path::Path; +use std::path::PathBuf; use std::{cmp, mem}; /// An identifier for some kind of IR item. @@ -318,7 +320,7 @@ pub(crate) enum IncludeLocation { /// Include location of a header included with an `#include` directive. File { /// Source file name of the include location. - file_name: String, + file_path: PathBuf, /// Line of the include location. line: usize, /// Column of the include location. @@ -364,11 +366,11 @@ impl IncludeLocation { ( IncludeLocation::Cli { .. } | IncludeLocation::Main, IncludeLocation::File { - file_name: other_file_name, + file_path: other_file_path, .. }, ) => { - let other_parent = ctx.include_location(other_file_name); + let other_parent = ctx.include_location(other_file_path); self.cmp_by_source_order(other_parent, ctx) } ( @@ -378,26 +380,26 @@ impl IncludeLocation { // If both include locations are in files, compare them as `SourceLocation`s. ( IncludeLocation::File { - file_name, + file_path, line, column, offset, }, IncludeLocation::File { - file_name: other_file_name, + file_path: other_file_path, line: other_line, column: other_column, offset: other_offset, }, ) => SourceLocation::File { - file_name: file_name.clone(), + file_path: file_path.clone(), line: *line, column: *column, offset: *offset, } .cmp_by_source_order( &SourceLocation::File { - file_name: other_file_name.clone(), + file_path: other_file_path.clone(), line: *other_line, column: *other_column, offset: *other_offset, @@ -465,7 +467,7 @@ pub(crate) struct BindgenContext { /// /// The key is the included file, the value is a pair of the source file and /// the position of the `#include` directive in the source file. - includes: StdHashMap, + includes: StdHashMap, /// A set of all the included filenames. deps: BTreeSet, @@ -749,39 +751,48 @@ If you encounter an error missing from this list, please file an issue or a PR!" /// Add the location of where `included_file` is included. pub(crate) fn add_include( &mut self, - included_file: String, + included_file_path: PathBuf, source_location: SourceLocation, ) { - let include_location = match source_location { - // Include location is a built-in, so it must have been included via CLI arguments. - SourceLocation::Builtin { offset } => { - IncludeLocation::Cli { offset } + if self.includes.contains_key(&included_file_path) { + return; + } + + let include_location = { + // Recursively including the main header file doesn't count. + if self.translation_unit.path() == included_file_path { + IncludeLocation::Main + } else { + match source_location { + // Include location is a built-in, so it must have been included via CLI arguments. + SourceLocation::Builtin { offset } => { + IncludeLocation::Cli { offset } + } + // Header was included with an `#include` directive. + SourceLocation::File { + file_path, + line, + column, + offset, + } => IncludeLocation::File { + file_path, + line, + column, + offset, + }, + } } - // Header was included with an `#include` directive. - SourceLocation::File { - file_name, - line, - column, - offset, - } => IncludeLocation::File { - file_name, - line, - column, - offset, - }, }; - self.includes - .entry(included_file) - .or_insert(include_location); + self.includes.insert(included_file_path, include_location); } - /// Get the location of the first `#include` directive for the `included_file`. - pub(crate) fn include_location( + /// Get the location of the first `#include` directive for the `included_file_path`. + pub(crate) fn include_location>( &self, - included_file: &str, + included_file_path: P, ) -> &IncludeLocation { - self.includes.get(included_file).unwrap_or( + self.includes.get(included_file_path.as_ref()).unwrap_or( // Header was not included anywhere, so it must be the main header. &IncludeLocation::Main, ) @@ -2482,13 +2493,13 @@ If you encounter an error missing from this list, please file an issue or a PR!" // are always included. if !self.options().allowlisted_files.is_empty() { if let Some(SourceLocation::File { - file_name, .. + file_path, .. }) = item.location() { if self .options() .allowlisted_files - .matches(file_name) + .matches(file_path.display().to_string()) { return true; } diff --git a/bindgen/ir/item.rs b/bindgen/ir/item.rs index 52df62c046..8bf8db7e10 100644 --- a/bindgen/ir/item.rs +++ b/bindgen/ir/item.rs @@ -647,9 +647,13 @@ impl Item { } if !ctx.options().blocklisted_files.is_empty() { - if let Some(SourceLocation::File { file_name, .. }) = &self.location + if let Some(SourceLocation::File { file_path, .. }) = &self.location { - if ctx.options().blocklisted_files.matches(file_name) { + if ctx + .options() + .blocklisted_files + .matches(file_path.display().to_string()) + { return true; } } diff --git a/bindgen/ir/var.rs b/bindgen/ir/var.rs index 6bbe4a9acc..011a83e034 100644 --- a/bindgen/ir/var.rs +++ b/bindgen/ir/var.rs @@ -471,16 +471,16 @@ fn duplicated_macro_diagnostic( let mut source = Cow::from(macro_name); if let crate::clang::SourceLocation::File { - file_name, + file_path, line, column, .. } = location { - if let Ok(Some(code)) = get_line(&file_name, line) { + if let Ok(Some(code)) = get_line(&file_path, line) { source = code.into(); } - slice.with_location(file_name, line, column); + slice.with_location(file_path, line, column); } slice.with_source(source);