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

Add rust language editions support #3000

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 12 additions & 3 deletions bindgen-tests/tests/expectations/tests/strings_cstr2.rs

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

4 changes: 4 additions & 0 deletions bindgen-tests/tests/expectations/tests/strings_cstr2_2021.rs

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

5 changes: 5 additions & 0 deletions bindgen-tests/tests/headers/strings_cstr2_2021.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// bindgen-flags: --rust-target=1.77 --rust-edition=2021 --generate-cstr

const char* MY_STRING_UTF8 = "Hello, world!";
const char* MY_STRING_INTERIOR_NULL = "Hello,\0World!";
const char* MY_STRING_NON_UTF8 = "ABCDE\xFF";
52 changes: 51 additions & 1 deletion bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ use quote::{ToTokens, TokenStreamExt};
use crate::{Entry, HashMap, HashSet};
use std::borrow::Cow;
use std::cell::Cell;
use std::cmp::PartialOrd;
use std::collections::VecDeque;
use std::ffi::CStr;
use std::fmt::{self, Write};
Expand Down Expand Up @@ -726,7 +727,10 @@ impl CodeGenerator for Var {

if let Some(cstr) = cstr {
let cstr_ty = quote! { ::#prefix::ffi::CStr };
if rust_features.literal_cstr {
if rust_features.literal_cstr &&
options.get_rust_edition() >=
RustEdition::Rust2021
{
let cstr = proc_macro2::Literal::c_string(&cstr);
result.push(quote! {
#(#attrs)*
Expand Down Expand Up @@ -3913,6 +3917,52 @@ impl std::str::FromStr for MacroTypeVariation {
}
}

/// Enum for the edition of Rust language to use.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
pub enum RustEdition {
/// Rust 2015 language edition
Rust2015,
/// Rust 2018 language edition
Rust2018,
/// Rust 2021 language edition
Rust2021,
/// Rust 2024 language edition
Rust2024,
}

impl fmt::Display for RustEdition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
RustEdition::Rust2015 => "2015",
RustEdition::Rust2018 => "2018",
RustEdition::Rust2021 => "2021",
RustEdition::Rust2024 => "2024",
};
s.fmt(f)
}
}

impl FromStr for RustEdition {
type Err = std::io::Error;

/// Create a `RustEdition` from a string.
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"2015" => Ok(RustEdition::Rust2015),
"2018" => Ok(RustEdition::Rust2018),
"2021" => Ok(RustEdition::Rust2021),
"2024" => Ok(RustEdition::Rust2024),
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
concat!(
"Got an invalid language edition. Accepted values ",
"are '2015', '2018', '2021', and '2024'"
),
)),
}
}
}

/// Enum for how aliases should be translated.
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub enum AliasVariation {
Expand Down
4 changes: 3 additions & 1 deletion bindgen/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,19 +161,21 @@ define_rust_targets! {
},
Stable_1_77(77) => {
offset_of: #106655,
literal_cstr: #117472,
literal_cstr: #117472, // Edition 2021+ only
},
Stable_1_73(73) => { thiscall_abi: #42202 },
Stable_1_71(71) => { c_unwind_abi: #106075 },
Stable_1_68(68) => { abi_efiapi: #105795 },
Stable_1_64(64) => { core_ffi_c: #94503 },
Stable_1_56(56) => { edition_2021: #88100 },
Stable_1_51(51) => { raw_ref_macros: #80886 },
Stable_1_59(59) => { const_cstr: #54745 },
Stable_1_47(47) => { larger_arrays: #74060 },
Stable_1_43(43) => { associated_constants: #68952 },
Stable_1_40(40) => { non_exhaustive: #44109 },
Stable_1_36(36) => { maybe_uninit: #60445 },
Stable_1_33(33) => { repr_packed_n: #57049 },
// Stable_1_31(31) => { edition_2018: #54057 },
}

/// Latest stable release of Rust that is supported by bindgen
Expand Down
6 changes: 6 additions & 0 deletions bindgen/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ use ir::item::Item;
use options::BindgenOptions;
use parse::ParseError;

use crate::codegen::RustEdition;
use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::env;
Expand Down Expand Up @@ -528,6 +529,11 @@ impl BindgenOptions {
}
}

/// Update rust edition version
pub fn set_rust_edition(&mut self, rust_edition: RustEdition) {
self.rust_edition = Some(rust_edition);
}

/// Update rust target version
pub fn set_rust_target(&mut self, rust_target: RustTarget) {
self.rust_target = rust_target;
Expand Down
7 changes: 6 additions & 1 deletion bindgen/options/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
regex_set::RegexSet,
Abi, AliasVariation, Builder, CodegenConfig, EnumVariation,
FieldVisibilityKind, Formatter, MacroTypeVariation, NonCopyUnionStyle,
RustTarget,
RustEdition, RustTarget,
};
use clap::{
error::{Error, ErrorKind},
Expand Down Expand Up @@ -332,6 +332,9 @@ struct BindgenCommand {
/// Add a RAW_LINE of Rust code to a given module with name MODULE_NAME.
#[arg(long, number_of_values = 2, value_names = ["MODULE_NAME", "RAW_LINE"])]
module_raw_line: Vec<String>,
/// Version of the Rust language edition. Defaults to 2018 if used from CLI, unless target version does not support it. Defaults to current crate's edition if used from API.
#[arg(long)]
rust_edition: Option<RustEdition>,
#[arg(long, help = rust_target_help())]
rust_target: Option<RustTarget>,
/// Use types from Rust core instead of std.
Expand Down Expand Up @@ -587,6 +590,7 @@ where
output,
raw_line,
module_raw_line,
rust_edition,
rust_target,
use_core,
conservative_inline_namespaces,
Expand Down Expand Up @@ -820,6 +824,7 @@ where
exit(0)
},
header,
rust_edition,
rust_target,
default_enum_style,
bitfield_enum,
Expand Down
33 changes: 33 additions & 0 deletions bindgen/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub(crate) mod cli;
use crate::callbacks::ParseCallbacks;
use crate::codegen::{
AliasVariation, EnumVariation, MacroTypeVariation, NonCopyUnionStyle,
RustEdition,
};
use crate::deps::DepfileSpec;
use crate::features::{RustFeatures, RustTarget};
Expand Down Expand Up @@ -1594,6 +1595,24 @@ options! {
as_args: |value, args| (!value).as_args(args, "--no-prepend-enum-name"),
},
/// Version of the Rust compiler to target.
rust_edition: Option<RustEdition> {
methods: {
/// Specify the Rust edition version.
///
/// The default edition is 2018.
pub fn rust_edition(mut self, rust_edition: RustEdition) -> Self {
self.options.set_rust_edition(rust_edition);
self
}
},
as_args: |rust_edition, args| {
if let Some(rust_edition) = rust_edition {
args.push("--rust-edition".to_owned());
args.push(rust_edition.to_string());
}
},
},
/// Version of the Rust compiler to target.
rust_target: RustTarget {
methods: {
/// Specify the Rust target version.
Expand Down Expand Up @@ -2150,3 +2169,17 @@ options! {
as_args: "--clang-macro-fallback-build-dir",
}
}

impl BindgenOptions {
/// Get default Rust edition, unless it is set by the user
pub fn get_rust_edition(&self) -> RustEdition {
self.rust_edition.unwrap_or_else(|| {
if !self.rust_features.edition_2021 {
RustEdition::Rust2018
} else {
// For now, we default to 2018, but this might need to be rethought
RustEdition::Rust2018
}
})
}
}
Loading