diff --git a/bindgen-tests/tests/expectations/tests/macro-redef.rs b/bindgen-tests/tests/expectations/tests/macro-redef.rs index 12e3d3d587..c59c7d37e0 100644 --- a/bindgen-tests/tests/expectations/tests/macro-redef.rs +++ b/bindgen-tests/tests/expectations/tests/macro-redef.rs @@ -1,4 +1,4 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] -pub const FOO: u32 = 4; +pub const FOO: u32 = 5; pub const BAR: u32 = 5; pub const BAZ: u32 = 6; diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index b5b6b4a000..a7a048b5f2 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -1496,6 +1496,15 @@ If you encounter an error missing from this list, please file an issue or a PR!" } } + /// Resolve the given `ItemId` into a mutable reference to an `Item`, or + /// `None` if no such item exists. + pub(crate) fn resolve_item_mut>( + &mut self, + id: Id, + ) -> Option<&mut Item> { + self.items.get_mut(id.into().0)?.as_mut() + } + /// Get the current module. pub(crate) fn current_module(&self) -> ModuleId { self.current_module diff --git a/bindgen/ir/item_kind.rs b/bindgen/ir/item_kind.rs index 9221b50579..56f8b5eacf 100644 --- a/bindgen/ir/item_kind.rs +++ b/bindgen/ir/item_kind.rs @@ -108,6 +108,15 @@ impl ItemKind { } } + /// Get a mutable reference to this `ItemKind`'s underlying `Var`, or + /// `None` if it is some other kind. + pub(crate) fn as_var_mut(&mut self) -> Option<&mut Var> { + match *self { + ItemKind::Var(ref mut v) => Some(v), + _ => None, + } + } + /// Is this a variable? pub(crate) fn is_var(&self) -> bool { self.as_var().is_some() diff --git a/bindgen/ir/var.rs b/bindgen/ir/var.rs index 9d72dcf06e..ae43d496f4 100644 --- a/bindgen/ir/var.rs +++ b/bindgen/ir/var.rs @@ -97,6 +97,13 @@ impl Var { pub fn link_name(&self) -> Option<&str> { self.link_name.as_deref() } + + /// Overwrite this variable's type and value, e.g. when a macro that + /// backs it has been redefined to a new value. + fn set_val(&mut self, ty: TypeId, val: Option) { + self.ty = ty; + self.val = val; + } } impl DotAttributes for Var { @@ -212,12 +219,6 @@ impl ClangSubItemParser for Var { // derived macros. ctx.note_parsed_macro(id.clone(), value.clone()); - if previously_defined { - let name = String::from_utf8(id).unwrap(); - duplicated_macro_diagnostic(&name, cursor.location(), ctx); - return Err(ParseError::Continue); - } - // NOTE: Unwrapping, here and above, is safe, because the // identifier of a token comes straight from clang, and we // enforce utf8 there, so we should have already panicked at @@ -264,6 +265,28 @@ impl ClangSubItemParser for Var { let ty = Item::builtin_type(type_kind, true, ctx); + if previously_defined { + let existing_id = ctx.items().find_map(|(id, item)| { + let var = item.kind().as_var()?; + (var.name() == name).then_some(id) + }); + + if let Some(existing_id) = existing_id { + duplicated_macro_diagnostic( + &name, + cursor.location(), + ctx, + ); + if let Some(existing_var) = ctx + .resolve_item_mut(existing_id) + .and_then(|item| item.kind_mut().as_var_mut()) + { + existing_var.set_val(ty, Some(val)); + } + return Ok(ParseResult::AlreadyResolved(existing_id)); + } + } + Ok(ParseResult::New( Var::new(name, None, None, ty, Some(val), true), Some(cursor), @@ -481,7 +504,7 @@ fn duplicated_macro_diagnostic( _location: clang::SourceLocation, _ctx: &BindgenContext, ) { - warn!("Duplicated macro definition: {macro_name}"); + warn!("Macro '{macro_name}' redefined; using the latest definition"); #[cfg(feature = "experimental")] // FIXME (pvdrz & amanjeev): This diagnostic message shows way too often to be actually