Skip to content

Include source code location in discovery callback #3244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
265 changes: 199 additions & 66 deletions bindgen-tests/tests/parse_callbacks/item_discovery_callback/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,57 @@ use std::rc::Rc;

use regex::Regex;

use bindgen::callbacks::{DiscoveredItem, DiscoveredItemId, ParseCallbacks};
use bindgen::callbacks::{
DiscoveredItem, DiscoveredItemId, ParseCallbacks, SourceLocation,
};
use bindgen::Builder;

#[derive(Debug, Default)]
struct ItemDiscovery(Rc<RefCell<ItemCache>>);

pub type ItemCache = HashMap<DiscoveredItemId, DiscoveredItem>;
pub type ItemCache = HashMap<DiscoveredItemId, DiscoveredInformation>;

#[derive(Debug)]
pub struct DiscoveredInformation(DiscoveredItem, Option<SourceLocation>);

impl ParseCallbacks for ItemDiscovery {
fn new_item_found(&self, _id: DiscoveredItemId, _item: DiscoveredItem) {
self.0.borrow_mut().insert(_id, _item);
fn new_item_found(
&self,
id: DiscoveredItemId,
item: DiscoveredItem,
source_location: Option<&SourceLocation>,
) {
self.0
.borrow_mut()
.insert(id, DiscoveredInformation(item, source_location.cloned()));
}
}

#[derive(Debug)]
pub struct ItemExpectations {
item: DiscoveredItem,
source_location: Option<(usize, usize, usize)>,
}

impl ItemExpectations {
fn new(
item: DiscoveredItem,
line: usize,
col: usize,
byte_offset: usize,
) -> Self {
Self {
item,
source_location: Some((line, col, byte_offset)),
}
}
}

type ExpectationMap = HashMap<DiscoveredItemId, ItemExpectations>;

fn test_item_discovery_callback(
header: &str,
expected: &HashMap<DiscoveredItemId, DiscoveredItem>,
expected: &HashMap<DiscoveredItemId, ItemExpectations>,
) {
let discovery = ItemDiscovery::default();
let info = Rc::clone(&discovery.0);
Expand All @@ -34,72 +68,117 @@ fn test_item_discovery_callback(
.generate()
.expect("TODO: panic message");

compare_item_caches(&info.borrow(), expected);
compare_item_caches(&info.borrow(), expected, header);
}

#[test]
fn test_item_discovery_callback_c() {
let expected = ItemCache::from([
let expected = ExpectationMap::from([
(
DiscoveredItemId::new(10),
DiscoveredItem::Struct {
original_name: Some("NamedStruct".to_string()),
final_name: "NamedStruct".to_string(),
},
ItemExpectations::new(
DiscoveredItem::Struct {
original_name: Some("NamedStruct".to_string()),
final_name: "NamedStruct".to_string(),
},
4,
8,
73,
),
),
(
DiscoveredItemId::new(11),
DiscoveredItem::Alias {
alias_name: "AliasOfNamedStruct".to_string(),
alias_for: DiscoveredItemId::new(10),
},
ItemExpectations::new(
DiscoveredItem::Alias {
alias_name: "AliasOfNamedStruct".to_string(),
alias_for: DiscoveredItemId::new(10),
},
7,
28,
118,
),
),
(
DiscoveredItemId::new(20),
DiscoveredItem::Union {
original_name: Some("NamedUnion".to_string()),
final_name: "NamedUnion".to_string(),
},
ItemExpectations::new(
DiscoveredItem::Union {
original_name: Some("NamedUnion".to_string()),
final_name: "NamedUnion".to_string(),
},
13,
7,
209,
),
),
(
DiscoveredItemId::new(21),
DiscoveredItem::Alias {
alias_name: "AliasOfNamedUnion".to_string(),
alias_for: DiscoveredItemId::new(20),
},
ItemExpectations::new(
DiscoveredItem::Alias {
alias_name: "AliasOfNamedUnion".to_string(),
alias_for: DiscoveredItemId::new(20),
},
16,
26,
251,
),
),
(
DiscoveredItemId::new(27),
DiscoveredItem::Alias {
alias_name: "AliasOfNamedEnum".to_string(),
alias_for: DiscoveredItemId::new(24),
},
ItemExpectations::new(
DiscoveredItem::Alias {
alias_name: "AliasOfNamedEnum".to_string(),
alias_for: DiscoveredItemId::new(24),
},
28,
24,
515,
),
),
(
DiscoveredItemId::new(24),
DiscoveredItem::Enum {
final_name: "NamedEnum".to_string(),
},
ItemExpectations::new(
DiscoveredItem::Enum {
final_name: "NamedEnum".to_string(),
},
24,
6,
466,
),
),
(
DiscoveredItemId::new(30),
DiscoveredItem::Struct {
original_name: None,
final_name: "_bindgen_ty_*".to_string(),
},
ItemExpectations::new(
DiscoveredItem::Struct {
original_name: None,
final_name: "_bindgen_ty_*".to_string(),
},
2,
38,
48,
),
),
(
DiscoveredItemId::new(40),
DiscoveredItem::Union {
original_name: None,
final_name: "_bindgen_ty_*".to_string(),
},
ItemExpectations::new(
DiscoveredItem::Union {
original_name: None,
final_name: "_bindgen_ty_*".to_string(),
},
11,
37,
186,
),
),
(
DiscoveredItemId::new(41),
DiscoveredItem::Function {
final_name: "named_function".to_string(),
},
ItemExpectations::new(
DiscoveredItem::Function {
final_name: "named_function".to_string(),
},
32,
6,
553,
),
),
]);
test_item_discovery_callback(
Expand All @@ -108,27 +187,41 @@ fn test_item_discovery_callback_c() {

#[test]
fn test_item_discovery_callback_cpp() {
let expected = ItemCache::from([
let expected = ExpectationMap::from([
(
DiscoveredItemId::new(1),
DiscoveredItem::Struct {
original_name: Some("SomeClass".to_string()),
final_name: "SomeClass".to_string(),
},
ItemExpectations::new(
DiscoveredItem::Struct {
original_name: Some("SomeClass".to_string()),
final_name: "SomeClass".to_string(),
},
3,
7,
18,
),
),
(
DiscoveredItemId::new(2),
DiscoveredItem::Method {
final_name: "named_method".to_string(),
parent: DiscoveredItemId::new(1),
},
ItemExpectations::new(
DiscoveredItem::Method {
final_name: "named_method".to_string(),
parent: DiscoveredItemId::new(1),
},
5,
10,
47,
),
),
]);
test_item_discovery_callback(
"/tests/parse_callbacks/item_discovery_callback/header_item_discovery.hpp", &expected);
}

pub fn compare_item_caches(generated: &ItemCache, expected: &ItemCache) {
fn compare_item_caches(
generated: &ItemCache,
expected: &ExpectationMap,
expected_filename: &str,
) {
// We can't use a simple Eq::eq comparison because of two reasons:
// - anonymous structs/unions will have a final name generated by bindgen which may change
// if the header file or the bindgen logic is altered
Expand All @@ -140,6 +233,7 @@ pub fn compare_item_caches(generated: &ItemCache, expected: &ItemCache) {
generated_item,
expected,
generated,
expected_filename,
)
});

Expand All @@ -151,40 +245,72 @@ pub fn compare_item_caches(generated: &ItemCache, expected: &ItemCache) {
}

fn compare_item_info(
expected_item: &DiscoveredItem,
generated_item: &DiscoveredItem,
expected: &ItemCache,
expected_item: &ItemExpectations,
generated_item: &DiscoveredInformation,
expected: &ExpectationMap,
generated: &ItemCache,
expected_filename: &str,
) -> bool {
if std::mem::discriminant(expected_item) !=
std::mem::discriminant(generated_item)
if std::mem::discriminant(&expected_item.item) !=
std::mem::discriminant(&generated_item.0)
{
return false;
}

match generated_item {
let is_a_match = match generated_item.0 {
DiscoveredItem::Struct { .. } => {
compare_struct_info(expected_item, generated_item)
compare_struct_info(&expected_item.item, &generated_item.0)
}
DiscoveredItem::Union { .. } => {
compare_union_info(expected_item, generated_item)
compare_union_info(&expected_item.item, &generated_item.0)
}
DiscoveredItem::Alias { .. } => compare_alias_info(
expected_item,
generated_item,
&expected_item.item,
&generated_item.0,
expected,
generated,
expected_filename,
),
DiscoveredItem::Enum { .. } => {
compare_enum_info(expected_item, generated_item)
compare_enum_info(&expected_item.item, &generated_item.0)
}
DiscoveredItem::Function { .. } => {
compare_function_info(expected_item, generated_item)
compare_function_info(&expected_item.item, &generated_item.0)
}
DiscoveredItem::Method { .. } => {
compare_method_info(expected_item, generated_item)
compare_method_info(&expected_item.item, &generated_item.0)
}
};

if is_a_match {
// Compare source location
assert!(
generated_item.1.is_some() ==
expected_item.source_location.is_some(),
"No source location provided when one was expected"
);
if let Some(generated_location) = generated_item.1.as_ref() {
let expected_location = expected_item.source_location.unwrap();
assert!(
generated_location
.file_name
.as_ref()
.expect("No filename provided")
.ends_with(expected_filename),
"Filename differed"
);
assert_eq!(
(
generated_location.line,
generated_location.col,
generated_location.byte_offset
),
expected_location,
"Line/col/offsets differ"
);
}
}
is_a_match
}

pub fn compare_names(expected_name: &str, generated_name: &str) -> bool {
Expand Down Expand Up @@ -288,8 +414,9 @@ pub fn compare_enum_info(
pub fn compare_alias_info(
expected_item: &DiscoveredItem,
generated_item: &DiscoveredItem,
expected: &ItemCache,
expected: &ExpectationMap,
generated: &ItemCache,
expected_filename: &str,
) -> bool {
let DiscoveredItem::Alias {
alias_name: expected_alias_name,
Expand Down Expand Up @@ -319,7 +446,13 @@ pub fn compare_alias_info(
return false;
};

compare_item_info(expected_aliased, generated_aliased, expected, generated)
compare_item_info(
expected_aliased,
generated_aliased,
expected,
generated,
expected_filename,
)
}

pub fn compare_function_info(
Expand Down
Loading