Skip to content

Commit be1e165

Browse files
committed
feat(config): add workspace.extra_clippy_allows for generated bindings
Consumers can append clippy lints to allow in every generated Rust binding file via [workspace] extra_clippy_allows. The list is merged (union, dedup) on top of each backend's built-in default allow-list and emitted as a single extra #![allow(...)] after the defaults. When absent or empty, generated output is byte-identical to the previous behaviour. Threaded through all 9 Rust-emitting backends (pyo3, napi, magnus, php, rustler, extendr, wasm, dart, swift). Lets liter-llm allow single_match / collapsible_match on its generated bindings without hand-editing.
1 parent 43e8116 commit be1e165

18 files changed

Lines changed: 290 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- **config**: `[workspace] extra_clippy_allows` — a string list of additional clippy lints
13+
to allow in every generated Rust binding file. Entries may be bare lint names
14+
(`"single_match"`) or `clippy::`-prefixed (`"clippy::single_match"`); both forms are
15+
accepted and normalised internally. The configured lints are merged (union,
16+
de-duplicated; defaults first, extras appended) with each backend's built-in default
17+
allow-list, and a single extra `#![allow(...)]` attribute is emitted after the defaults.
18+
When the list is absent or empty the generated output is byte-identical to the previous
19+
behaviour. Affected backends: pyo3, napi, magnus, php, rustler, extendr, wasm, dart,
20+
swift.
21+
22+
Example:
23+
```toml
24+
[workspace]
25+
extra_clippy_allows = ["single_match", "collapsible_match"]
26+
```
27+
1028
## [0.30.10] - 2026-07-02
1129

1230
### Fixed
@@ -26,6 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2644

2745
- **chore**: consolidate the typos allowlist into `poly.toml` and drop dead configs.
2846

47+
## [0.30.9] - 2026-07-02
48+
2949
### Fixed
3050

3151
- **codegen/ffi**: complete the service-owner forward-declaration fix from 0.30.8. The new

schemas/alef.schema.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6902,6 +6902,14 @@
69026902
"default": null,
69036903
"description": "Default Elixir/Rustler backend settings."
69046904
},
6905+
"extra_clippy_allows": {
6906+
"default": [],
6907+
"description": "Extra clippy lints to allow in every generated Rust binding file, merged\n(union, de-duplicated) with each backend's built-in default allow-list.\n\nEntries may be bare lint names (`\"single_match\"`) or `clippy::`-prefixed\n(`\"clippy::single_match\"`); both forms are accepted and normalised\ninternally. When absent or empty the emitted allow-list is byte-identical\nto the backend default — no diff in consumers that do not set this field.\n\nExample:\n```toml\n[workspace]\nextra_clippy_allows = [\"single_match\", \"collapsible_match\"]\n```",
6908+
"items": {
6909+
"type": "string"
6910+
},
6911+
"type": "array"
6912+
},
69056913
"ffi": {
69066914
"anyOf": [
69076915
{
@@ -7381,6 +7389,7 @@
73817389
"ruby": "struct"
73827390
},
73837391
"elixir": null,
7392+
"extra_clippy_allows": [],
73847393
"ffi": null,
73857394
"format": {
73867395
"command": null,

src/backends/dart/gen_rust_crate/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ fn emit_lib_rs(
115115
// works for both wrapped and unwrapped string keys/values.
116116
content.push_str(" clippy::useless_conversion,\n");
117117
content.push_str(")]\n");
118+
if let Some(extra_attr) = crate::codegen::shared::format_extra_clippy_allows(&config.extra_clippy_allows) {
119+
content.push_str(&format!("#![{extra_attr}]\n"));
120+
}
118121
// Declare frb_generated after the crate-level attrs so FRB doesn't inject it at line 1.
119122
content.push_str("mod frb_generated;\n");
120123
content.push_str("use flutter_rust_bridge::frb;\n");

src/backends/extendr/gen_bindings/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ impl Backend for ExtendrBackend {
7979
let mut builder = RustFileBuilder::new().with_generated_header();
8080
builder.add_inner_attribute("allow(dead_code, unused_imports, unused_variables)");
8181
builder.add_inner_attribute("allow(clippy::too_many_arguments, clippy::let_unit_value, clippy::needless_borrow, clippy::map_identity, clippy::just_underscores_and_digits, clippy::unused_unit, clippy::unnecessary_cast, clippy::unwrap_or_default, clippy::derivable_impls, clippy::needless_borrows_for_generic_args, clippy::unnecessary_fallible_conversions)");
82+
if let Some(extra_attr) = crate::codegen::shared::format_extra_clippy_allows(&config.extra_clippy_allows) {
83+
builder.add_inner_attribute(&extra_attr);
84+
}
8285
builder.add_import("extendr_api::prelude::*");
8386
// HashMap is needed for fields of type HashMap<K, V> (extendr prelude does not re-export it)
8487
builder.add_import("std::collections::HashMap");

src/backends/magnus/gen_bindings/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ impl Backend for MagnusBackend {
127127
clippy::needless_borrows_for_generic_args, clippy::unnecessary_fallible_conversions, \
128128
clippy::type_complexity, clippy::useless_conversion, clippy::clone_on_copy)",
129129
);
130+
if let Some(extra_attr) = crate::codegen::shared::format_extra_clippy_allows(&config.extra_clippy_allows) {
131+
builder.add_inner_attribute(&extra_attr);
132+
}
130133
builder.add_import(
131134
"magnus::{function, method, prelude::*, Error, Ruby, IntoValueFromNative, try_convert::TryConvertOwned}",
132135
);

src/backends/napi/gen_bindings/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ impl Backend for NapiBackend {
147147
builder.add_inner_attribute(
148148
"allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::default_trait_access, clippy::useless_conversion, clippy::unsafe_derive_deserialize, clippy::must_use_candidate, clippy::return_self_not_must_use, clippy::use_self, clippy::missing_const_for_fn, clippy::missing_errors_doc, clippy::needless_pass_by_value, clippy::doc_markdown, clippy::derive_partial_eq_without_eq, clippy::uninlined_format_args, clippy::redundant_clone, clippy::implicit_clone, clippy::redundant_closure_for_method_calls, clippy::wildcard_imports, clippy::option_if_let_else, clippy::too_many_lines)",
149149
);
150+
if let Some(extra_attr) = crate::codegen::shared::format_extra_clippy_allows(&config.extra_clippy_allows) {
151+
builder.add_inner_attribute(&extra_attr);
152+
}
150153
builder.add_import("napi::*");
151154
builder.add_import("napi_derive::napi");
152155

src/backends/php/gen_bindings/rust_bindings.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ pub(super) fn generate_bindings(api: &ApiSurface, config: &ResolvedCrateConfig)
188188
// PHP parameter names are lowerCamelCase; Rust complains about non-snake_case variables.
189189
builder.add_inner_attribute("allow(non_snake_case)");
190190
builder.add_inner_attribute("allow(clippy::too_many_arguments, clippy::let_unit_value, clippy::needless_borrow, clippy::map_identity, clippy::just_underscores_and_digits, clippy::unnecessary_cast, clippy::unused_unit, clippy::unwrap_or_default, clippy::derivable_impls, clippy::needless_borrows_for_generic_args, clippy::unnecessary_fallible_conversions, clippy::arc_with_non_send_sync, clippy::collapsible_if, clippy::clone_on_copy, clippy::should_implement_trait, clippy::useless_conversion)");
191+
if let Some(extra_attr) = crate::codegen::shared::format_extra_clippy_allows(&config.extra_clippy_allows) {
192+
builder.add_inner_attribute(&extra_attr);
193+
}
191194
builder.add_import("ext_php_rs::prelude::*");
192195

193196
// Import serde_json when available (needed for serde-based param conversion)

src/backends/pyo3/gen_bindings/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ impl Backend for Pyo3Backend {
9999
let adapter_bodies = crate::adapters::build_adapter_bodies(config, Language::Python)?;
100100

101101
let mut builder = RustFileBuilder::new().with_generated_header();
102-
support_items::add_generated_module_attributes(&mut builder);
102+
support_items::add_generated_module_attributes(&mut builder, &config.extra_clippy_allows);
103103
builder.add_import("pyo3::prelude::*");
104104
// Note: core_import and path_mapping crates are referenced via fully-qualified paths
105105
// in generated code (e.g. `core_import::TypeName`), so no bare `use crate_name;`

src/backends/pyo3/gen_bindings/support_items.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::codegen::builder::RustFileBuilder;
2+
use crate::codegen::shared::format_extra_clippy_allows;
23

3-
pub(super) fn add_generated_module_attributes(builder: &mut RustFileBuilder) {
4+
pub(super) fn add_generated_module_attributes(builder: &mut RustFileBuilder, extras: &[String]) {
45
// Suppress documentation and cast lints in generated code. Python stubs carry
56
// docs, and numeric casts are intentional FFI conversions.
67
builder.add_inner_attribute("allow(missing_docs)");
@@ -14,6 +15,9 @@ pub(super) fn add_generated_module_attributes(builder: &mut RustFileBuilder) {
1415
);
1516
builder.add_inner_attribute("allow(clippy::multiple_unsafe_ops_per_block)");
1617
builder.add_inner_attribute("allow(unsafe_code)");
18+
if let Some(extra_attr) = format_extra_clippy_allows(extras) {
19+
builder.add_inner_attribute(&extra_attr);
20+
}
1721
}
1822

1923
pub(super) fn add_py_visitor_ref(builder: &mut RustFileBuilder) {

src/backends/rustler/gen_bindings/native.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ pub(super) fn generate_bindings(api: &ApiSurface, config: &ResolvedCrateConfig)
106106
let mut builder = RustFileBuilder::new().with_generated_header();
107107
builder.add_inner_attribute("allow(dead_code, unused_imports, unused_variables)");
108108
builder.add_inner_attribute("allow(clippy::too_many_arguments, clippy::let_unit_value, clippy::needless_borrow, clippy::map_identity, clippy::just_underscores_and_digits, clippy::unused_unit, clippy::unnecessary_cast, clippy::unwrap_or_default, clippy::derivable_impls, clippy::needless_borrows_for_generic_args, clippy::unnecessary_fallible_conversions)");
109+
if let Some(extra_attr) = crate::codegen::shared::format_extra_clippy_allows(&config.extra_clippy_allows) {
110+
builder.add_inner_attribute(&extra_attr);
111+
}
109112
builder.add_import("rustler::ResourceArc");
110113
builder.add_import("rustler::Encoder");
111114

0 commit comments

Comments
 (0)