Skip to content

Commit 1032dfa

Browse files
committed
Add rust language editions support
Introduce a new `--rust-edition` parameter with allowed values 2015, 2018, 2021, and 2024. This allows different code generation depending on the language target. In this PR, the C-string literals are now generated differently depending on the language edition (literals like `c"example"` are not available before 2021)
1 parent ee3efc8 commit 1032dfa

File tree

8 files changed

+123
-6
lines changed

8 files changed

+123
-6
lines changed

bindgen-tests/tests/expectations/tests/strings_cstr2.rs

+12-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindgen-tests/tests/expectations/tests/strings_cstr2_2021.rs

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// bindgen-flags: --rust-target=1.77 --rust-edition=2021 --generate-cstr
2+
3+
const char* MY_STRING_UTF8 = "Hello, world!";
4+
const char* MY_STRING_INTERIOR_NULL = "Hello,\0World!";
5+
const char* MY_STRING_NON_UTF8 = "ABCDE\xFF";

bindgen/codegen/mod.rs

+65-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ use quote::{ToTokens, TokenStreamExt};
6060
use crate::{Entry, HashMap, HashSet};
6161
use std::borrow::Cow;
6262
use std::cell::Cell;
63+
use std::cmp::{Ordering, PartialOrd};
6364
use std::collections::VecDeque;
6465
use std::ffi::CStr;
6566
use std::fmt::{self, Write};
@@ -726,7 +727,9 @@ impl CodeGenerator for Var {
726727

727728
if let Some(cstr) = cstr {
728729
let cstr_ty = quote! { ::#prefix::ffi::CStr };
729-
if rust_features.literal_cstr {
730+
if rust_features.literal_cstr &&
731+
options.rust_edition >= RustEdition::Rust2021
732+
{
730733
let cstr = proc_macro2::Literal::c_string(&cstr);
731734
result.push(quote! {
732735
#(#attrs)*
@@ -3913,6 +3916,67 @@ impl std::str::FromStr for MacroTypeVariation {
39133916
}
39143917
}
39153918

3919+
/// Enum for the edition of Rust language to use.
3920+
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
3921+
pub enum RustEdition {
3922+
/// Rust 2015 language edition
3923+
Rust2015,
3924+
/// Rust 2018 language edition
3925+
#[default]
3926+
Rust2018,
3927+
/// Rust 2021 language edition
3928+
Rust2021,
3929+
/// Rust 2024 language edition
3930+
Rust2024,
3931+
}
3932+
3933+
impl From<RustEdition> for usize {
3934+
fn from(edition: RustEdition) -> usize {
3935+
match edition {
3936+
RustEdition::Rust2015 => 2015,
3937+
RustEdition::Rust2018 => 2018,
3938+
RustEdition::Rust2021 => 2021,
3939+
RustEdition::Rust2024 => 2024,
3940+
}
3941+
}
3942+
}
3943+
3944+
impl fmt::Display for RustEdition {
3945+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3946+
let value: usize = (*self).into();
3947+
value.fmt(f)
3948+
}
3949+
}
3950+
3951+
impl FromStr for RustEdition {
3952+
type Err = std::io::Error;
3953+
3954+
/// Create a `RustEdition` from a string.
3955+
fn from_str(s: &str) -> Result<Self, Self::Err> {
3956+
match s {
3957+
"2015" => Ok(RustEdition::Rust2015),
3958+
"2018" => Ok(RustEdition::Rust2018),
3959+
"2021" => Ok(RustEdition::Rust2021),
3960+
"2024" => Ok(RustEdition::Rust2024),
3961+
_ => Err(std::io::Error::new(
3962+
std::io::ErrorKind::InvalidInput,
3963+
concat!(
3964+
"Got an invalid language edition. Accepted values ",
3965+
"are '2015', '2018', '2021', and '2024'"
3966+
),
3967+
)),
3968+
}
3969+
}
3970+
}
3971+
3972+
impl PartialOrd for RustEdition {
3973+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
3974+
let a: usize = (*self).into();
3975+
let b: usize = (*other).into();
3976+
a.partial_cmp(&b)
3977+
}
3978+
}
3979+
39163980
/// Enum for how aliases should be translated.
39173981
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
39183982
pub enum AliasVariation {

bindgen/features.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ define_rust_targets! {
161161
},
162162
Stable_1_77(77) => {
163163
offset_of: #106655,
164-
literal_cstr: #117472,
164+
literal_cstr: #117472, // Edition 2021+ only
165165
},
166166
Stable_1_73(73) => { thiscall_abi: #42202 },
167167
Stable_1_71(71) => { c_unwind_abi: #106075 },

bindgen/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ use ir::item::Item;
6464
use options::BindgenOptions;
6565
use parse::ParseError;
6666

67+
use crate::codegen::RustEdition;
6768
use std::borrow::Cow;
6869
use std::collections::hash_map::Entry;
6970
use std::env;
@@ -528,6 +529,11 @@ impl BindgenOptions {
528529
}
529530
}
530531

532+
/// Update rust edition version
533+
pub fn set_rust_edition(&mut self, rust_edition: RustEdition) {
534+
self.rust_edition = rust_edition;
535+
}
536+
531537
/// Update rust target version
532538
pub fn set_rust_target(&mut self, rust_target: RustTarget) {
533539
self.rust_target = rust_target;

bindgen/options/cli.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
regex_set::RegexSet,
88
Abi, AliasVariation, Builder, CodegenConfig, EnumVariation,
99
FieldVisibilityKind, Formatter, MacroTypeVariation, NonCopyUnionStyle,
10-
RustTarget,
10+
RustEdition, RustTarget,
1111
};
1212
use clap::{
1313
error::{Error, ErrorKind},
@@ -19,6 +19,13 @@ use std::path::{Path, PathBuf};
1919
use std::str::FromStr;
2020
use std::{fs::File, process::exit};
2121

22+
fn rust_edition_help() -> String {
23+
format!(
24+
"Version of the Rust language edition. Defaults to {}.",
25+
RustEdition::default()
26+
)
27+
}
28+
2229
fn rust_target_help() -> String {
2330
format!(
2431
"Version of the Rust compiler to target. Any Rust version after {EARLIEST_STABLE_RUST} is supported. Defaults to {}.",
@@ -332,6 +339,8 @@ struct BindgenCommand {
332339
/// Add a RAW_LINE of Rust code to a given module with name MODULE_NAME.
333340
#[arg(long, number_of_values = 2, value_names = ["MODULE_NAME", "RAW_LINE"])]
334341
module_raw_line: Vec<String>,
342+
#[arg(long, help = rust_edition_help())]
343+
rust_edition: Option<RustEdition>,
335344
#[arg(long, help = rust_target_help())]
336345
rust_target: Option<RustTarget>,
337346
/// Use types from Rust core instead of std.
@@ -587,6 +596,7 @@ where
587596
output,
588597
raw_line,
589598
module_raw_line,
599+
rust_edition,
590600
rust_target,
591601
use_core,
592602
conservative_inline_namespaces,
@@ -820,6 +830,7 @@ where
820830
exit(0)
821831
},
822832
header,
833+
rust_edition,
823834
rust_target,
824835
default_enum_style,
825836
bitfield_enum,

bindgen/options/mod.rs

+18
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub(crate) mod cli;
1010
use crate::callbacks::ParseCallbacks;
1111
use crate::codegen::{
1212
AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle,
13+
RustEdition,
1314
};
1415
use crate::deps::DepfileSpec;
1516
use crate::features::{RustFeatures, RustTarget};
@@ -1594,6 +1595,23 @@ options! {
15941595
as_args: |value, args| (!value).as_args(args, "--no-prepend-enum-name"),
15951596
},
15961597
/// Version of the Rust compiler to target.
1598+
rust_edition: RustEdition {
1599+
default: RustEdition::default(),
1600+
methods: {
1601+
/// Specify the Rust edition version.
1602+
///
1603+
/// The default edition is 2018.
1604+
pub fn rust_edition(mut self, rust_edition: RustEdition) -> Self {
1605+
self.options.set_rust_edition(rust_edition);
1606+
self
1607+
}
1608+
},
1609+
as_args: |rust_edition, args| {
1610+
args.push("--rust-edition".to_owned());
1611+
args.push(rust_edition.to_string());
1612+
},
1613+
},
1614+
/// Version of the Rust compiler to target.
15971615
rust_target: RustTarget {
15981616
methods: {
15991617
/// Specify the Rust target version.

0 commit comments

Comments
 (0)