From d34e10fbeb5af70bbc7e22405de60f30c8570087 Mon Sep 17 00:00:00 2001 From: Porter Smith Date: Mon, 26 Nov 2018 16:59:18 -0800 Subject: [PATCH] Add #[must_use] to functions annotated with __attribute__((warn_unused_result)) --- src/clang.rs | 25 +++++++++++ src/codegen/helpers.rs | 6 +++ src/codegen/mod.rs | 10 ++++- src/features.rs | 6 +++ src/ir/function.rs | 21 ++++++--- .../tests/attribute_warn_unused_result.rs | 44 +++++++++++++++++++ .../attribute_warn_unused_result_pre_1_27.rs | 41 +++++++++++++++++ .../headers/attribute_warn_unused_result.hpp | 10 +++++ .../attribute_warn_unused_result_pre_1_27.hpp | 8 ++++ 9 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 tests/expectations/tests/attribute_warn_unused_result.rs create mode 100644 tests/expectations/tests/attribute_warn_unused_result_pre_1_27.rs create mode 100644 tests/headers/attribute_warn_unused_result.hpp create mode 100644 tests/headers/attribute_warn_unused_result_pre_1_27.hpp diff --git a/src/clang.rs b/src/clang.rs index f8f7e581cf..a31cba31e9 100644 --- a/src/clang.rs +++ b/src/clang.rs @@ -499,6 +499,31 @@ impl Cursor { } } + /// Does this cursor have the given simple attribute? + /// + /// Note that this will only work for attributes that don't have an existing libclang + /// CursorKind, e.g. pure, const, etc. + pub fn has_simple_attr(&self, attr: &str) -> bool { + let mut found_attr = false; + self.visit(|cur| { + if cur.kind() == CXCursor_UnexposedAttr { + found_attr = cur.tokens().map(|tokens| { + tokens.iter().any(|t| { + t.kind == CXToken_Identifier && t.spelling == attr + }) + }).unwrap_or(false); + + if found_attr { + return CXChildVisit_Break; + } + } + + CXChildVisit_Continue + }); + + found_attr + } + /// Given that this cursor's referent is a `typedef`, get the `Type` that is /// being aliased. pub fn typedef_type(&self) -> Option { diff --git a/src/codegen/helpers.rs b/src/codegen/helpers.rs index 343f1e30f7..02909d57c3 100644 --- a/src/codegen/helpers.rs +++ b/src/codegen/helpers.rs @@ -36,6 +36,12 @@ pub mod attributes { } } + pub fn must_use() -> quote::Tokens { + quote! { + #[must_use] + } + } + pub fn doc(comment: String) -> quote::Tokens { // Doc comments are already preprocessed into nice `///` formats by the // time they get here. Just make sure that we have newlines around it so diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 34b5b4d9ba..073745b11f 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -2154,9 +2154,13 @@ impl MethodCodegen for Method { let mut attrs = vec![]; attrs.push(attributes::inline()); + if signature.must_use() && ctx.options().rust_features().must_use_function { + attrs.push(attributes::must_use()); + } + let name = ctx.rust_ident(&name); methods.push(quote! { - #[inline] + #(#attrs)* pub unsafe fn #name ( #( #args ),* ) #ret { #block } @@ -3374,6 +3378,10 @@ impl CodeGenerator for Function { let mut attributes = vec![]; + if signature.must_use() && ctx.options().rust_features().must_use_function { + attributes.push(attributes::must_use()); + } + if let Some(comment) = item.comment(ctx) { attributes.push(attributes::doc(comment)); } diff --git a/src/features.rs b/src/features.rs index 671464ab7c..6f10f11bab 100644 --- a/src/features.rs +++ b/src/features.rs @@ -98,6 +98,8 @@ macro_rules! rust_target_base { => Stable_1_25 => 1.25; /// Rust stable 1.26 => Stable_1_26 => 1.26; + /// Rust stable 1.27 + => Stable_1_27 => 1.27; /// Nightly rust => Nightly => nightly; ); @@ -178,6 +180,10 @@ rust_feature_def!( /// [i128 / u128 support](https://doc.rust-lang.org/std/primitive.i128.html) => i128_and_u128; } + Stable_1_27 { + /// `must_use` attribute on functions ([PR](https://github.com/rust-lang/rust/pull/48925)) + => must_use_function; + } Nightly { /// `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202)) => thiscall_abi; diff --git a/src/ir/function.rs b/src/ir/function.rs index 883203e9a1..0b83a74dd1 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -221,6 +221,9 @@ pub struct FunctionSig { /// Whether this function is variadic. is_variadic: bool, + /// Whether this function's return value must be used. + must_use: bool, + /// The ABI of this function. abi: Abi, } @@ -308,14 +311,16 @@ impl FunctionSig { /// Construct a new function signature. pub fn new( return_type: TypeId, - arguments: Vec<(Option, TypeId)>, + argument_types: Vec<(Option, TypeId)>, is_variadic: bool, + must_use: bool, abi: Abi, ) -> Self { FunctionSig { - return_type: return_type, - argument_types: arguments, - is_variadic: is_variadic, + return_type, + argument_types, + is_variadic, + must_use, abi: abi, } } @@ -387,6 +392,7 @@ impl FunctionSig { } }; + let must_use = cursor.has_simple_attr("warn_unused_result"); let is_method = cursor.kind() == CXCursor_CXXMethod; let is_constructor = cursor.kind() == CXCursor_Constructor; let is_destructor = cursor.kind() == CXCursor_Destructor; @@ -458,7 +464,7 @@ impl FunctionSig { warn!("Unknown calling convention: {:?}", call_conv); } - Ok(Self::new(ret.into(), args, ty.is_variadic(), abi)) + Ok(Self::new(ret.into(), args, ty.is_variadic(), must_use, abi)) } /// Get this function signature's return type. @@ -484,6 +490,11 @@ impl FunctionSig { self.is_variadic && !self.argument_types.is_empty() } + /// Must this function's return value be used? + pub fn must_use(&self) -> bool { + self.must_use + } + /// Are function pointers with this signature able to derive Rust traits? /// Rust only supports deriving traits for function pointers with a limited /// number of parameters and a couple ABIs. diff --git a/tests/expectations/tests/attribute_warn_unused_result.rs b/tests/expectations/tests/attribute_warn_unused_result.rs new file mode 100644 index 0000000000..35ac4dd2b3 --- /dev/null +++ b/tests/expectations/tests/attribute_warn_unused_result.rs @@ -0,0 +1,44 @@ +/* automatically generated by rust-bindgen */ + +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct Foo { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_Foo() { + assert_eq!( + ::std::mem::size_of::(), + 1usize, + concat!("Size of: ", stringify!(Foo)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(Foo)) + ); +} +extern "C" { + #[must_use] + #[link_name = "\u{1}_ZN3Foo3fooEi"] + pub fn Foo_foo(this: *mut Foo, arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +impl Foo { + #[inline] + #[must_use] + pub unsafe fn foo(&mut self, arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int { + Foo_foo(self, arg1) + } +} +extern "C" { + #[must_use] + #[link_name = "\u{1}_Z3fooi"] + pub fn foo(arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} diff --git a/tests/expectations/tests/attribute_warn_unused_result_pre_1_27.rs b/tests/expectations/tests/attribute_warn_unused_result_pre_1_27.rs new file mode 100644 index 0000000000..c60b19c6b0 --- /dev/null +++ b/tests/expectations/tests/attribute_warn_unused_result_pre_1_27.rs @@ -0,0 +1,41 @@ +/* automatically generated by rust-bindgen */ + +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct Foo { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_Foo() { + assert_eq!( + ::std::mem::size_of::(), + 1usize, + concat!("Size of: ", stringify!(Foo)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(Foo)) + ); +} +extern "C" { + #[link_name = "\u{1}_ZN3Foo3fooEi"] + pub fn Foo_foo(this: *mut Foo, arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +impl Foo { + #[inline] + pub unsafe fn foo(&mut self, arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int { + Foo_foo(self, arg1) + } +} +extern "C" { + #[link_name = "\u{1}_Z3fooi"] + pub fn foo(arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} diff --git a/tests/headers/attribute_warn_unused_result.hpp b/tests/headers/attribute_warn_unused_result.hpp new file mode 100644 index 0000000000..2155030711 --- /dev/null +++ b/tests/headers/attribute_warn_unused_result.hpp @@ -0,0 +1,10 @@ +// bindgen-flags: --rust-target 1.27 + +class Foo { +public: + __attribute__((warn_unused_result)) + int foo(int); +}; + +__attribute__((warn_unused_result)) +int foo(int); diff --git a/tests/headers/attribute_warn_unused_result_pre_1_27.hpp b/tests/headers/attribute_warn_unused_result_pre_1_27.hpp new file mode 100644 index 0000000000..25127d9cd0 --- /dev/null +++ b/tests/headers/attribute_warn_unused_result_pre_1_27.hpp @@ -0,0 +1,8 @@ +class Foo { +public: + __attribute__((warn_unused_result)) + int foo(int); +}; + +__attribute__((warn_unused_result)) +int foo(int);