Skip to content
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

feat: add custom attributes to fn items #3088

Open
wants to merge 66 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
fd12a02
chore: bump `itertools` semver and activate `use_alloc`
oberrich Feb 5, 2025
75860ae
feat(integration): add integration tests for custom attributes
oberrich Feb 5, 2025
2c4742f
feat(attrs): add tests for custom attributes and cli
oberrich Feb 5, 2025
8506bc9
feat(attrs): implement codegen
oberrich Feb 5, 2025
4ae1854
feat(attrs): add `merge_cfg_attributes` pass
oberrich Feb 5, 2025
1599556
doc(attrs): fix docs for `merge_cfg_attributes` pass
oberrich Feb 5, 2025
8542bfa
fix: visibility of `FunctionKind` and `MethodKind`
oberrich Feb 5, 2025
2878dbe
feat(attrs): implement cli
oberrich Feb 5, 2025
d12cce9
feat(attrs): use `Vec` instead of `HashSet`
oberrich Feb 5, 2025
48b8599
feat(attrs): format attribute tokens using config
oberrich Feb 6, 2025
ab272ad
feat(attrs): always use `prettyplease::unparse` and some refactoring
oberrich Feb 6, 2025
6ec8523
refactor: unnest codegen for var to reduce code duplication
oberrich Feb 6, 2025
3e98b56
style: spelling
oberrich Feb 6, 2025
fe77c8b
docs: fix comments
oberrich Feb 6, 2025
678e52b
fix: remove debug prints
oberrich Feb 6, 2025
724fc19
fix(attrs): use `Vec<_>` over `HashSet<_>`
oberrich Feb 6, 2025
91928be
chore: fix rustfmt check
oberrich Feb 6, 2025
3600115
fix: crate version
oberrich Feb 6, 2025
31e3c5f
fix: remove unused imports
oberrich Feb 6, 2025
3c552de
fix: cli build
oberrich Feb 6, 2025
8dc4963
chore: run rustfmt on `codegen/mod.rs`
oberrich Feb 6, 2025
b2b3181
chore: run rustfmt on `options/cli.rs`
oberrich Feb 6, 2025
1be9954
fix: make clippy happy
oberrich Feb 6, 2025
3d1ff2c
chore: run rustfmt on `codegen/mod.rs`
oberrich Feb 6, 2025
a833e10
feat(attrs): always enable `prettyplease`
oberrich Feb 6, 2025
b4eb4fc
fix(attrs): emit comment as first attribute
oberrich Feb 6, 2025
786c1bc
style: minor fixes
oberrich Feb 6, 2025
dc468ee
fix(attrs): only quote `cfg_attrs` for constified enum variant `impl`s
oberrich Feb 6, 2025
7834cdc
fix(regr): prepend space inside block comments
oberrich Feb 6, 2025
d2b5b19
chore: run rustfmt on `codegen/mod.rs`
oberrich Feb 6, 2025
ed8fc84
chore: apply clippy fixes
oberrich Feb 6, 2025
63f7863
fix: improve comment formatting, unnest branch
oberrich Feb 6, 2025
c270044
fix: don't skip empty lines indiscriminately
oberrich Feb 6, 2025
ec0a0dc
fix: skip -> take
oberrich Feb 6, 2025
9305a1d
fix: use itertools for logic
oberrich Feb 6, 2025
67ef11f
fix: attempt to not trim whitespaces
oberrich Feb 6, 2025
031be86
fix: syntax error
oberrich Feb 6, 2025
dd80133
chore: run rustfmt on `codegen/mod.rs`
oberrich Feb 6, 2025
e1d3855
fix: filter out `#[must_use]` attributes
oberrich Feb 6, 2025
c4c5358
fix: invert logic
oberrich Feb 6, 2025
9dcbd24
fix: invert operator
oberrich Feb 6, 2025
9c02ec5
fix(tests): apply new `#[must_use]` order
oberrich Feb 6, 2025
d646345
chore: run rustfmt on `codegen/mod.rs`
oberrich Feb 6, 2025
c7d0890
fix: test to apply attrs for function_item
oberrich Feb 6, 2025
de6805e
fix: remaining expectations related to new attribute handling
oberrich Feb 6, 2025
1caa01a
chore: run rustfmt on `codegen/mod.rs`
oberrich Feb 6, 2025
6fe99c9
chore: apply clippy fix
oberrich Feb 6, 2025
a4f583d
fix: attempt to fix CI
oberrich Feb 6, 2025
ffaec44
fix: terminate comment
oberrich Feb 6, 2025
f27db9a
fix: attempt to fix ci
oberrich Feb 6, 2025
fad0ff5
fix: attempt to fix ci
oberrich Feb 6, 2025
d0652a0
fix: attempt to fix ci
oberrich Feb 6, 2025
b625a69
fix: only remove `#[must_use]` for non-dynamic fns
oberrich Feb 6, 2025
726ff75
chore: run rustfmt on `codegen/mod.rs`
oberrich Feb 6, 2025
f7eeecd
fix: attempt to fix ci
oberrich Feb 6, 2025
00663bf
fix: attempt to fix ci
oberrich Feb 6, 2025
e6c4596
fix: attempt to fix ci at last
oberrich Feb 6, 2025
05178c6
fix: attempt to fix ci - see what happens
oberrich Feb 6, 2025
cd86a8f
fix: make it compile
oberrich Feb 6, 2025
a0c642e
fix: invert condition
oberrich Feb 6, 2025
973cca1
fix: attempt to fix ci
oberrich Feb 6, 2025
f2aecce
fix: setting attributes on dynamic_functions seems bad, so let's not …
oberrich Feb 6, 2025
d5c134b
fix: syntax errors
oberrich Feb 6, 2025
0698c4f
fix: try the helper again
oberrich Feb 6, 2025
c9c772b
fix: try to combine the attrs
oberrich Feb 6, 2025
e95c7e5
fix: version number
oberrich Feb 6, 2025
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ clang-sys = "1"
clap = "4"
clap_complete = "4"
env_logger = "0.10.0"
itertools = { version = ">=0.10,<0.14", default-features = false }
itertools = { version = ">=0.10,<0.15", default-features = false, features = ["use_alloc"] }
libloading = "0.8"
log = "0.4"
objc = "0.2"
Expand Down
3 changes: 1 addition & 2 deletions bindgen-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ path = "main.rs"
name = "bindgen"

[dependencies]
bindgen = { workspace = true, features = ["__cli", "experimental", "prettyplease"] }
bindgen = { workspace = true, features = ["__cli", "experimental"] }
env_logger = { workspace = true, optional = true }
log = { workspace = true, optional = true }
proc-macro2.workspace = true
Expand All @@ -33,7 +33,6 @@ default = ["logging", "runtime"]
logging = ["bindgen/logging", "dep:env_logger", "dep:log"]
static = ["bindgen/static"]
runtime = ["bindgen/runtime"]
prettyplease = ["bindgen/prettyplease"]

## The following features are for internal use and they shouldn't be used if
## you're not hacking on bindgen
Expand Down
3 changes: 3 additions & 0 deletions bindgen-integration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ build = "build.rs"
rust-version.workspace = true
edition.workspace = true

[dependencies]
syn = { workspace = true, features = ["full", "visit"] }

[build-dependencies]
bindgen = { workspace = true, default-features = true, features = ["experimental"] }
cc.workspace = true
Expand Down
10 changes: 9 additions & 1 deletion bindgen-integration/build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
extern crate bindgen;

use bindgen::callbacks::{
DeriveInfo, IntKind, MacroParsingBehavior, ParseCallbacks,
AttributeItemKind, DeriveInfo, FunctionKind, IntKind, MacroParsingBehavior,
ParseCallbacks,
};
use bindgen::{Builder, EnumVariation, Formatter};
use std::collections::HashSet;
Expand Down Expand Up @@ -140,7 +141,14 @@ impl ParseCallbacks for MacroCallback {
info: &bindgen::callbacks::AttributeInfo<'_>,
) -> Vec<String> {
if info.name == "Test" {
assert_eq!(info.kind, AttributeItemKind::Struct);
vec!["#[cfg_attr(test, derive(PartialOrd))]".into()]
} else if info.name == "coord" {
assert_eq!(
info.kind,
AttributeItemKind::Function(FunctionKind::Function)
);
vec!["#[must_use]".into()]
} else {
vec![]
}
Expand Down
56 changes: 56 additions & 0 deletions bindgen-integration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,62 @@ fn test_custom_derive() {
assert!(!(test1 > test2));
}

#[test]
fn test_custom_fn_attribute() {
use std::env;
use std::fs;
use std::path::Path;
use syn::visit::Visit;
use syn::{parse_file, File, ForeignItem, Item, ItemForeignMod};

let out_dir =
std::env::var("OUT_DIR").expect("OUT_DIR environment variable not set");
let test_file_path = Path::new(&out_dir).join("test.rs");
let file_content = fs::read_to_string(&test_file_path)
.expect("Failed to read test.rs file");
let syntax_tree: File =
parse_file(&file_content).expect("Failed to parse test.rs");

struct FunctionVisitor {
found_coord: bool,
has_must_use: bool,
}

impl<'ast> Visit<'ast> for FunctionVisitor {
fn visit_item_foreign_mod(
&mut self,
foreign_mod: &'ast ItemForeignMod,
) {
for foreign_item in &foreign_mod.items {
if let ForeignItem::Fn(item_fn) = foreign_item {
if item_fn.sig.ident == "coord" {
self.found_coord = true;
self.has_must_use = item_fn
.attrs
.iter()
.any(|attr| attr.path().is_ident("must_use"));
}
}
}
}
}

let mut visitor = FunctionVisitor {
found_coord: false,
has_must_use: false,
};
visitor.visit_file(&syntax_tree);

assert!(
visitor.found_coord,
"The function 'coord' was not found in the source."
);
assert!(
visitor.has_must_use,
"The function 'coord' does not have the #[must_use] attribute."
);
}

#[test]
fn test_custom_attributes() {
// The `add_attributes` callback should have added `#[cfg_attr(test, derive(PartialOrd))])`
Expand Down
2 changes: 1 addition & 1 deletion bindgen-tests/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub fn main() {

for entry in entries {
// TODO: file_is_cpp() in bindgen/lib.rs checks for hpp,hxx,hh, and h++ - should this be consistent?
if entry.path().extension().map_or(false, |ext| {
if entry.path().extension().is_some_and(|ext| {
ext.eq_ignore_ascii_case("h") || ext.eq_ignore_ascii_case("hpp")
}) {
let func = entry
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions bindgen-tests/tests/expectations/tests/attribute-custom.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions bindgen-tests/tests/expectations/tests/enum-doc-mod.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion bindgen-tests/tests/headers/attribute-custom-cli.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// bindgen-flags: --default-enum-style rust --default-non-copy-union-style manually_drop --no-default=".*" --no-hash=".*" --no-partialeq=".*" --no-debug=".*" --no-copy=".*" --with-attribute-custom="foo_[^e].*=#[doc(hidden)]" --with-attribute-custom-struct="foo.*=#[derive(Default)]" --with-attribute-custom-enum="foo.*=#[cfg_attr(test, derive(PartialOrd, Copy))]" --with-attribute-custom-union="foo.*=#[derive(Clone)],#[derive(Copy)]"
// bindgen-flags: --default-enum-style rust --default-non-copy-union-style manually_drop --no-default=".*" --no-hash=".*" --no-partialeq=".*" --no-debug=".*" --no-copy=".*" --with-attribute-custom="foo_[^e].*=#[doc(hidden)]" --with-attribute-custom-struct="foo.*=#[derive(Default)]" --with-attribute-custom-enum="foo.*=#[cfg_attr(test, derive(PartialOrd, Copy))]" --with-attribute-custom-union="foo.*=#[derive(Clone)],#[derive(Copy)]" --with-attribute-custom-function="foo.*=#[must_use],#[doc(hidden)]"
struct foo_struct {
int inner;
};
Expand All @@ -12,3 +12,4 @@ union foo_union {
struct non_matching {
int inner;
};
int foo_function() { return 1; }
5 changes: 5 additions & 0 deletions bindgen-tests/tests/headers/attribute-custom.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ struct my_type2 {
struct my_type3 {
unsigned long a;
};

/**
* <div rustbindgen attribute="#[must_use]" attribute="#[doc(hidden)]"></div>
*/
int function_attributes() { return 1; }
6 changes: 3 additions & 3 deletions bindgen-tests/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn should_overwrite_expected() -> bool {
return true;
}
assert!(
var == "0" || var == "",
var == "0" || var.is_empty(),
"Invalid value of BINDGEN_OVERWRITE_EXPECTED"
);
}
Expand Down Expand Up @@ -57,7 +57,7 @@ fn error_diff_mismatch(
if let Some(var) = env::var_os("BINDGEN_TESTS_DIFFTOOL") {
//usecase: var = "meld" -> You can hand check differences
let Some(std::path::Component::Normal(name)) =
filename.components().last()
filename.components().next_back()
else {
panic!("Why is the header variable so weird?")
};
Expand Down Expand Up @@ -187,7 +187,7 @@ fn compare_generated_header(
header.display(),
looked_at,
),
};
}

let (builder, roundtrip_builder) = builder.into_builder(check_roundtrip)?;

Expand Down
4 changes: 2 additions & 2 deletions bindgen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ clap = { workspace = true, features = ["derive"], optional = true }
clap_complete = { workspace = true, optional = true }
itertools = { workspace = true }
log = { workspace = true, optional = true }
prettyplease = { workspace = true, optional = true, features = ["verbatim"] }
prettyplease = { workspace = true, features = ["verbatim"] }
proc-macro2.workspace = true
quote.workspace = true
regex = { workspace = true, features = ["std", "unicode-perl"] }
Expand All @@ -43,7 +43,7 @@ shlex.workspace = true
syn = { workspace = true, features = ["full", "extra-traits", "visit-mut"] }

[features]
default = ["logging", "prettyplease", "runtime"]
default = ["logging", "runtime"]
logging = ["dep:log"]
static = ["clang-sys/static"]
runtime = ["clang-sys/runtime"]
Expand Down
33 changes: 29 additions & 4 deletions bindgen/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub use crate::ir::analysis::DeriveTrait;
pub use crate::ir::derive::CanDerive as ImplementsTrait;
pub use crate::ir::enum_ty::{EnumVariantCustomBehavior, EnumVariantValue};
pub use crate::ir::function::FunctionKind;
pub use crate::ir::int::IntKind;
use std::fmt;

Expand Down Expand Up @@ -133,10 +134,19 @@ pub trait ParseCallbacks: fmt::Debug {
///
/// If no additional attributes are wanted, this function should return an
/// empty `Vec`.
// TODO: Call this process_attributes function in codegen/mod.rs
fn add_attributes(&self, _info: &AttributeInfo<'_>) -> Vec<String> {
vec![]
}

/// Process an item's attribute
fn process_attributes(
&self,
_info: &AttributeInfo<'_>,
_attributes: &mut Vec<String>,
) {
}

/// Process a source code comment.
fn process_comment(&self, _comment: &str) -> Option<String> {
None
Expand Down Expand Up @@ -231,15 +241,30 @@ pub struct DeriveInfo<'a> {
pub kind: TypeKind,
}

/// Relevant information about a type to which new attributes will be added using
/// Relevant information about an item to which new attributes will be added using
/// [`ParseCallbacks::add_attributes`].
#[derive(Debug)]
#[non_exhaustive]
pub struct AttributeInfo<'a> {
/// The name of the type.
/// The name of the item.
pub name: &'a str,
/// The kind of the type.
pub kind: TypeKind,
/// The kind of the item.
pub kind: AttributeItemKind,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// The kind of the current item.
pub enum AttributeItemKind {
/// The item is a Rust `struct`.
Struct,
/// The item is a Rust `enum`.
Enum,
/// The item is a Rust `union`.
Union,
/// The item is a Rust variable.
Var,
/// The item is a Rust `fn`.
Function(FunctionKind),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down
Loading
Loading