Skip to content

Commit d34e10f

Browse files
committed
Add #[must_use] to functions annotated with __attribute__((warn_unused_result))
1 parent 1e527e2 commit d34e10f

9 files changed

+165
-6
lines changed

src/clang.rs

+25
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,31 @@ impl Cursor {
499499
}
500500
}
501501

502+
/// Does this cursor have the given simple attribute?
503+
///
504+
/// Note that this will only work for attributes that don't have an existing libclang
505+
/// CursorKind, e.g. pure, const, etc.
506+
pub fn has_simple_attr(&self, attr: &str) -> bool {
507+
let mut found_attr = false;
508+
self.visit(|cur| {
509+
if cur.kind() == CXCursor_UnexposedAttr {
510+
found_attr = cur.tokens().map(|tokens| {
511+
tokens.iter().any(|t| {
512+
t.kind == CXToken_Identifier && t.spelling == attr
513+
})
514+
}).unwrap_or(false);
515+
516+
if found_attr {
517+
return CXChildVisit_Break;
518+
}
519+
}
520+
521+
CXChildVisit_Continue
522+
});
523+
524+
found_attr
525+
}
526+
502527
/// Given that this cursor's referent is a `typedef`, get the `Type` that is
503528
/// being aliased.
504529
pub fn typedef_type(&self) -> Option<Type> {

src/codegen/helpers.rs

+6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ pub mod attributes {
3636
}
3737
}
3838

39+
pub fn must_use() -> quote::Tokens {
40+
quote! {
41+
#[must_use]
42+
}
43+
}
44+
3945
pub fn doc(comment: String) -> quote::Tokens {
4046
// Doc comments are already preprocessed into nice `///` formats by the
4147
// time they get here. Just make sure that we have newlines around it so

src/codegen/mod.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -2154,9 +2154,13 @@ impl MethodCodegen for Method {
21542154
let mut attrs = vec![];
21552155
attrs.push(attributes::inline());
21562156

2157+
if signature.must_use() && ctx.options().rust_features().must_use_function {
2158+
attrs.push(attributes::must_use());
2159+
}
2160+
21572161
let name = ctx.rust_ident(&name);
21582162
methods.push(quote! {
2159-
#[inline]
2163+
#(#attrs)*
21602164
pub unsafe fn #name ( #( #args ),* ) #ret {
21612165
#block
21622166
}
@@ -3374,6 +3378,10 @@ impl CodeGenerator for Function {
33743378

33753379
let mut attributes = vec![];
33763380

3381+
if signature.must_use() && ctx.options().rust_features().must_use_function {
3382+
attributes.push(attributes::must_use());
3383+
}
3384+
33773385
if let Some(comment) = item.comment(ctx) {
33783386
attributes.push(attributes::doc(comment));
33793387
}

src/features.rs

+6
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ macro_rules! rust_target_base {
9898
=> Stable_1_25 => 1.25;
9999
/// Rust stable 1.26
100100
=> Stable_1_26 => 1.26;
101+
/// Rust stable 1.27
102+
=> Stable_1_27 => 1.27;
101103
/// Nightly rust
102104
=> Nightly => nightly;
103105
);
@@ -178,6 +180,10 @@ rust_feature_def!(
178180
/// [i128 / u128 support](https://doc.rust-lang.org/std/primitive.i128.html)
179181
=> i128_and_u128;
180182
}
183+
Stable_1_27 {
184+
/// `must_use` attribute on functions ([PR](https://github.com/rust-lang/rust/pull/48925))
185+
=> must_use_function;
186+
}
181187
Nightly {
182188
/// `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202))
183189
=> thiscall_abi;

src/ir/function.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ pub struct FunctionSig {
221221
/// Whether this function is variadic.
222222
is_variadic: bool,
223223

224+
/// Whether this function's return value must be used.
225+
must_use: bool,
226+
224227
/// The ABI of this function.
225228
abi: Abi,
226229
}
@@ -308,14 +311,16 @@ impl FunctionSig {
308311
/// Construct a new function signature.
309312
pub fn new(
310313
return_type: TypeId,
311-
arguments: Vec<(Option<String>, TypeId)>,
314+
argument_types: Vec<(Option<String>, TypeId)>,
312315
is_variadic: bool,
316+
must_use: bool,
313317
abi: Abi,
314318
) -> Self {
315319
FunctionSig {
316-
return_type: return_type,
317-
argument_types: arguments,
318-
is_variadic: is_variadic,
320+
return_type,
321+
argument_types,
322+
is_variadic,
323+
must_use,
319324
abi: abi,
320325
}
321326
}
@@ -387,6 +392,7 @@ impl FunctionSig {
387392
}
388393
};
389394

395+
let must_use = cursor.has_simple_attr("warn_unused_result");
390396
let is_method = cursor.kind() == CXCursor_CXXMethod;
391397
let is_constructor = cursor.kind() == CXCursor_Constructor;
392398
let is_destructor = cursor.kind() == CXCursor_Destructor;
@@ -458,7 +464,7 @@ impl FunctionSig {
458464
warn!("Unknown calling convention: {:?}", call_conv);
459465
}
460466

461-
Ok(Self::new(ret.into(), args, ty.is_variadic(), abi))
467+
Ok(Self::new(ret.into(), args, ty.is_variadic(), must_use, abi))
462468
}
463469

464470
/// Get this function signature's return type.
@@ -484,6 +490,11 @@ impl FunctionSig {
484490
self.is_variadic && !self.argument_types.is_empty()
485491
}
486492

493+
/// Must this function's return value be used?
494+
pub fn must_use(&self) -> bool {
495+
self.must_use
496+
}
497+
487498
/// Are function pointers with this signature able to derive Rust traits?
488499
/// Rust only supports deriving traits for function pointers with a limited
489500
/// number of parameters and a couple ABIs.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
#![allow(
4+
dead_code,
5+
non_snake_case,
6+
non_camel_case_types,
7+
non_upper_case_globals
8+
)]
9+
10+
#[repr(C)]
11+
#[derive(Debug, Default, Copy, Clone)]
12+
pub struct Foo {
13+
pub _address: u8,
14+
}
15+
#[test]
16+
fn bindgen_test_layout_Foo() {
17+
assert_eq!(
18+
::std::mem::size_of::<Foo>(),
19+
1usize,
20+
concat!("Size of: ", stringify!(Foo))
21+
);
22+
assert_eq!(
23+
::std::mem::align_of::<Foo>(),
24+
1usize,
25+
concat!("Alignment of ", stringify!(Foo))
26+
);
27+
}
28+
extern "C" {
29+
#[must_use]
30+
#[link_name = "\u{1}_ZN3Foo3fooEi"]
31+
pub fn Foo_foo(this: *mut Foo, arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
32+
}
33+
impl Foo {
34+
#[inline]
35+
#[must_use]
36+
pub unsafe fn foo(&mut self, arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int {
37+
Foo_foo(self, arg1)
38+
}
39+
}
40+
extern "C" {
41+
#[must_use]
42+
#[link_name = "\u{1}_Z3fooi"]
43+
pub fn foo(arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
#![allow(
4+
dead_code,
5+
non_snake_case,
6+
non_camel_case_types,
7+
non_upper_case_globals
8+
)]
9+
10+
#[repr(C)]
11+
#[derive(Debug, Default, Copy, Clone)]
12+
pub struct Foo {
13+
pub _address: u8,
14+
}
15+
#[test]
16+
fn bindgen_test_layout_Foo() {
17+
assert_eq!(
18+
::std::mem::size_of::<Foo>(),
19+
1usize,
20+
concat!("Size of: ", stringify!(Foo))
21+
);
22+
assert_eq!(
23+
::std::mem::align_of::<Foo>(),
24+
1usize,
25+
concat!("Alignment of ", stringify!(Foo))
26+
);
27+
}
28+
extern "C" {
29+
#[link_name = "\u{1}_ZN3Foo3fooEi"]
30+
pub fn Foo_foo(this: *mut Foo, arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
31+
}
32+
impl Foo {
33+
#[inline]
34+
pub unsafe fn foo(&mut self, arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int {
35+
Foo_foo(self, arg1)
36+
}
37+
}
38+
extern "C" {
39+
#[link_name = "\u{1}_Z3fooi"]
40+
pub fn foo(arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// bindgen-flags: --rust-target 1.27
2+
3+
class Foo {
4+
public:
5+
__attribute__((warn_unused_result))
6+
int foo(int);
7+
};
8+
9+
__attribute__((warn_unused_result))
10+
int foo(int);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class Foo {
2+
public:
3+
__attribute__((warn_unused_result))
4+
int foo(int);
5+
};
6+
7+
__attribute__((warn_unused_result))
8+
int foo(int);

0 commit comments

Comments
 (0)