diff --git a/src/clang.rs b/src/clang.rs index 00716a1bd5..1cf4dd0d0d 100644 --- a/src/clang.rs +++ b/src/clang.rs @@ -12,6 +12,40 @@ use std::hash::Hasher; use std::os::raw::{c_char, c_int, c_longlong, c_uint, c_ulong, c_ulonglong}; use std::{mem, ptr, slice}; +/// Type representing a clang attribute. +/// +/// Values of this type can be used to check for different attributes using the `has_attrs` +/// function. +pub struct Attribute { + name: &'static [u8], + kind: Option, + token_kind: CXTokenKind, +} + +impl Attribute { + /// A `warn_unused_result` attribute. + pub const MUST_USE: Self = Self { + name: b"warn_unused_result", + // FIXME(emilio): clang-sys doesn't expose `CXCursor_WarnUnusedResultAttr` (from clang 9). + kind: Some(440), + token_kind: CXToken_Identifier, + }; + + /// A `_Noreturn` attribute. + pub const NO_RETURN: Self = Self { + name: b"_Noreturn", + kind: None, + token_kind: CXToken_Keyword, + }; + + /// A `[[noreturn]]` attribute. + pub const NO_RETURN_CPP: Self = Self { + name: b"noreturn", + kind: None, + token_kind: CXToken_Identifier, + }; +} + /// A cursor into the Clang AST, pointing to an AST node. /// /// We call the AST node pointed to by the cursor the cursor's "referent". @@ -638,35 +672,41 @@ impl Cursor { } } - /// Whether this cursor has the `warn_unused_result` attribute. - pub fn has_warn_unused_result_attr(&self) -> bool { - // FIXME(emilio): clang-sys doesn't expose this (from clang 9). - const CXCursor_WarnUnusedResultAttr: CXCursorKind = 440; - self.has_attr("warn_unused_result", Some(CXCursor_WarnUnusedResultAttr)) - } + /// Does this cursor have the given attributes? + pub fn has_attrs( + &self, + attrs: &[Attribute; N], + ) -> [bool; N] { + let mut found_attrs = [false; N]; + let mut found_count = 0; - /// Does this cursor have the given attribute? - /// - /// `name` is checked against unexposed attributes. - fn has_attr(&self, name: &str, clang_kind: Option) -> bool { - let mut found_attr = false; self.visit(|cur| { let kind = cur.kind(); - found_attr = clang_kind.map_or(false, |k| k == kind) || - (kind == CXCursor_UnexposedAttr && - cur.tokens().iter().any(|t| { - t.kind == CXToken_Identifier && - t.spelling() == name.as_bytes() - })); - - if found_attr { - CXChildVisit_Break - } else { - CXChildVisit_Continue + for (idx, attr) in attrs.iter().enumerate() { + let found_attr = &mut found_attrs[idx]; + if !*found_attr { + // `attr.name` and` attr.token_kind` are checked against unexposed attributes only. + if attr.kind.map_or(false, |k| k == kind) || + (kind == CXCursor_UnexposedAttr && + cur.tokens().iter().any(|t| { + t.kind == attr.token_kind && + t.spelling() == attr.name + })) + { + *found_attr = true; + found_count += 1; + + if found_count == N { + return CXChildVisit_Break; + } + } + } } + + CXChildVisit_Continue }); - found_attr + found_attrs } /// Given that this cursor's referent is a `typedef`, get the `Type` that is diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 77e9ba950d..ecfbb6ad43 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -4704,6 +4704,10 @@ pub mod utils { ctx: &BindgenContext, sig: &FunctionSig, ) -> proc_macro2::TokenStream { + if sig.is_divergent() { + return quote! { -> ! }; + } + let return_item = ctx.resolve_item(sig.return_type()); if let TypeKind::Void = *return_item.kind().expect_type().kind() { quote! {} diff --git a/src/ir/function.rs b/src/ir/function.rs index e8e2c2dfed..48ad73e8f0 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -6,7 +6,7 @@ use super::dot::DotAttributes; use super::item::Item; use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::TypeKind; -use crate::clang; +use crate::clang::{self, Attribute}; use crate::parse::{ ClangItemParser, ClangSubItemParser, ParseError, ParseResult, }; @@ -228,6 +228,7 @@ pub struct FunctionSig { /// Whether this function is variadic. is_variadic: bool, + is_divergent: bool, /// Whether this function's return value must be used. must_use: bool, @@ -358,6 +359,7 @@ impl FunctionSig { return_type: TypeId, argument_types: Vec<(Option, TypeId)>, is_variadic: bool, + is_divergent: bool, must_use: bool, abi: Abi, ) -> Self { @@ -365,6 +367,7 @@ impl FunctionSig { return_type, argument_types, is_variadic, + is_divergent, must_use, abi, } @@ -447,8 +450,23 @@ impl FunctionSig { } }; - let must_use = ctx.options().enable_function_attribute_detection && - cursor.has_warn_unused_result_attr(); + let (must_use, mut is_divergent) = + if ctx.options().enable_function_attribute_detection { + let [must_use, no_return, no_return_cpp] = cursor.has_attrs(&[ + Attribute::MUST_USE, + Attribute::NO_RETURN, + Attribute::NO_RETURN_CPP, + ]); + (must_use, no_return || no_return_cpp) + } else { + Default::default() + }; + + // This looks easy to break but the clang parser keeps the type spelling clean even if + // other attributes are added. + is_divergent = + is_divergent || ty.spelling().contains("__attribute__((noreturn))"); + let is_method = kind == CXCursor_CXXMethod; let is_constructor = kind == CXCursor_Constructor; let is_destructor = kind == CXCursor_Destructor; @@ -528,7 +546,14 @@ impl FunctionSig { warn!("Unknown calling convention: {:?}", call_conv); } - Ok(Self::new(ret, args, ty.is_variadic(), must_use, abi)) + Ok(Self::new( + ret, + args, + ty.is_variadic(), + is_divergent, + must_use, + abi, + )) } /// Get this function signature's return type. @@ -575,6 +600,10 @@ impl FunctionSig { matches!(self.abi, Abi::C | Abi::Unknown(..)) } + + pub(crate) fn is_divergent(&self) -> bool { + self.is_divergent + } } impl ClangSubItemParser for Function { diff --git a/tests/expectations/tests/noreturn.rs b/tests/expectations/tests/noreturn.rs new file mode 100644 index 0000000000..1945749518 --- /dev/null +++ b/tests/expectations/tests/noreturn.rs @@ -0,0 +1,19 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +extern "C" { + #[link_name = "\u{1}_Z1fv"] + pub fn f() -> !; +} +extern "C" { + #[link_name = "\u{1}_Z1gv"] + pub fn g() -> !; +} +extern "C" { + #[link_name = "\u{1}_Z1hv"] + pub fn h() -> !; +} diff --git a/tests/headers/noreturn.hpp b/tests/headers/noreturn.hpp new file mode 100644 index 0000000000..4ce1e11e3f --- /dev/null +++ b/tests/headers/noreturn.hpp @@ -0,0 +1,4 @@ +// bindgen-flags: --enable-function-attribute-detection -- -std=c++11 +_Noreturn void f(void); +__attribute__((noreturn)) void g(void); +[[noreturn]] void h(void);