From 501ec52df4142c6cb9665c92baf9551734caba20 Mon Sep 17 00:00:00 2001 From: David Herzka Date: Tue, 28 Nov 2023 19:29:26 +0100 Subject: [PATCH] Change CLI argument to --derive --- c2rust-transpile/src/lib.rs | 11 +++++- c2rust-transpile/src/translator/mod.rs | 50 ++++++++++++++++++-------- c2rust/src/bin/c2rust-transpile.rs | 39 +++++++++++++++++--- scripts/test_translator.py | 12 +++++-- tests/structs/src/debug_derive_bad.c | 2 +- tests/structs/src/debug_derive_good.c | 2 +- 6 files changed, 91 insertions(+), 25 deletions(-) diff --git a/c2rust-transpile/src/lib.rs b/c2rust-transpile/src/lib.rs index 9b67fa3680..7631f22ba8 100644 --- a/c2rust-transpile/src/lib.rs +++ b/c2rust-transpile/src/lib.rs @@ -25,6 +25,7 @@ use itertools::Itertools; use log::{info, warn}; use regex::Regex; use serde_derive::Serialize; +use strum_macros::Display; use crate::c_ast::Printer; use crate::c_ast::*; @@ -82,7 +83,7 @@ pub struct TranspilerConfig { pub disable_refactoring: bool, pub preserve_unused_functions: bool, pub log_level: log::LevelFilter, - pub derive_debug: bool, + pub derives: HashSet, // Options that control build files /// Emit `Cargo.toml` and `lib.rs` @@ -92,6 +93,14 @@ pub struct TranspilerConfig { pub binaries: Vec, } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Display)] +pub enum Derive { + Clone, + Copy, + Debug, + BitfieldStruct, +} + impl TranspilerConfig { fn binary_name_from_path(file: &Path) -> String { let file = Path::new(file.file_stem().unwrap()); diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index c6c00d9321..2391b665d2 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -11,6 +11,7 @@ use dtoa; use failure::{err_msg, format_err, Fail}; use indexmap::indexmap; use indexmap::{IndexMap, IndexSet}; +use itertools::Itertools; use log::{error, info, trace, warn}; use proc_macro2::{Punct, Spacing::*, Span, TokenStream, TokenTree}; use syn::spanned::Spanned as _; @@ -27,12 +28,12 @@ use c2rust_ast_builder::{mk, properties::*, Builder}; use c2rust_ast_printer::pprust::{self}; use crate::c_ast::iterators::{DFExpr, SomeId}; -use crate::c_ast::*; use crate::cfg; use crate::convert_type::TypeConverter; use crate::renamer::Renamer; use crate::with_stmts::WithStmts; use crate::{c_ast, format_translation_err}; +use crate::{c_ast::*, Derive}; use crate::{ExternCrate, ExternCrateDetails, TranspilerConfig}; use c2rust_ast_exporter::clang_ast::LRValue; @@ -1640,14 +1641,6 @@ impl<'c> Translation<'c> { can_derive_debug, } = self.convert_struct_fields(decl_id, fields, platform_byte_size)?; - let mut derives = vec![]; - if !contains_va_list { - derives.push("Copy"); - derives.push("Clone"); - }; - if self.tcfg.derive_debug && can_derive_debug { - derives.push("Debug"); - } let has_bitfields = fields .iter() @@ -1656,10 +1649,27 @@ impl<'c> Translation<'c> { _ => unreachable!("Found non-field in record field list"), }); if has_bitfields { - derives.push("BitfieldStruct"); self.use_crate(ExternCrate::C2RustBitfields); } + let derives = self + .tcfg + .derives + .iter() + .flat_map(|derive| { + let can_derive = match derive { + Derive::Clone | Derive::Copy => !contains_va_list, + Derive::Debug => can_derive_debug, + Derive::BitfieldStruct => has_bitfields, + }; + if can_derive { + Some(derive.to_string()) + } else { + None + } + }) + .collect_vec(); + let mut reprs = vec![simple_metaitem("C")]; let max_field_alignment = if is_packed { // `__attribute__((packed))` forces a max alignment of 1, @@ -1710,10 +1720,22 @@ impl<'c> Translation<'c> { let repr_attr = mk().meta_list("repr", outer_reprs); let outer_field = mk().pub_().enum_field(mk().ident_ty(inner_name)); - let mut outer_struct_derives = vec!["Copy", "Clone"]; - if self.tcfg.derive_debug { - outer_struct_derives.push("Debug"); - } + let outer_struct_derives = self + .tcfg + .derives + .iter() + .flat_map(|derive| { + let can_derive = match derive { + Derive::Clone | Derive::Copy | Derive::Debug => true, + Derive::BitfieldStruct => false, + }; + if can_derive { + Some(derive.to_string()) + } else { + None + } + }) + .collect_vec(); let outer_struct = mk() .span(span) diff --git a/c2rust/src/bin/c2rust-transpile.rs b/c2rust/src/bin/c2rust-transpile.rs index 1baa6f2131..1bd7b955e3 100644 --- a/c2rust/src/bin/c2rust-transpile.rs +++ b/c2rust/src/bin/c2rust-transpile.rs @@ -3,7 +3,9 @@ use log::LevelFilter; use regex::Regex; use std::{fs, path::PathBuf}; -use c2rust_transpile::{Diagnostic, ReplaceMode, TranspilerConfig}; +use c2rust_transpile::{Derive, Diagnostic, ReplaceMode, TranspilerConfig}; + +const DEFAULT_DERIVES: &[Derive] = &[Derive::Clone, Derive::Copy, Derive::BitfieldStruct]; #[derive(Debug, Parser)] #[clap( @@ -156,9 +158,16 @@ struct Args { #[clap(long)] fail_on_multiple: bool, - /// Derive `Debug` trait for any structs that allow it (i.e., do not recursively contain any unions) - #[clap(long)] - derive_debug: bool, + /// Add extra derived traits to generated structs in addition to the default + /// set of derives (Copy, Clone, BitfieldStruct). Specify multiple times to + /// add more than one derive. A struct will derive all traits in the set for + /// which it is eligible. + /// + /// For example, a struct containing a union cannot derive Debug, so + /// `#[derive(Debug)]` will not be added to that struct regardless of + /// whether `--derive Debug` is specified. + #[clap(long = "derive", value_enum, value_name = "TRAIT")] + extra_derives: Vec, } #[derive(Debug, PartialEq, Eq, ValueEnum, Clone)] @@ -168,9 +177,29 @@ enum InvalidCodes { CompileError, } +#[derive(Clone, Copy, Debug, ValueEnum)] +#[clap(rename_all = "PascalCase")] +enum ExtraDerive { + Debug, +} + +impl ExtraDerive { + fn to_transpiler_derive(&self) -> Derive { + match self { + Self::Debug => Derive::Debug, + } + } +} + fn main() { let args = Args::parse(); + let derives = DEFAULT_DERIVES + .iter() + .cloned() + .chain(args.extra_derives.iter().map(|d| d.to_transpiler_derive())) + .collect(); + // Build a TranspilerConfig from the command line let mut tcfg = TranspilerConfig { dump_untyped_context: args.dump_untyped_clang_ast, @@ -220,7 +249,7 @@ fn main() { emit_no_std: args.emit_no_std, enabled_warnings: args.warn.into_iter().collect(), log_level: args.log_level, - derive_debug: args.derive_debug, + derives, }; // binaries imply emit-build-files if !tcfg.binaries.is_empty() { diff --git a/scripts/test_translator.py b/scripts/test_translator.py index 6d71ad5b48..6c880457e1 100755 --- a/scripts/test_translator.py +++ b/scripts/test_translator.py @@ -72,7 +72,12 @@ def __init__(self, log_level: str, path: str, flags: Set[str] = set()) -> None: self.translate_const_macros = "translate_const_macros" in flags self.reorganize_definitions = "reorganize_definitions" in flags self.emit_build_files = "emit_build_files" in flags - self.derive_debug = "derive_debug" in flags + + derive_flag_prefix = "derive:" + derives = set() + for derive_flag in filter(lambda f: f.startswith(derive_flag_prefix), flags): + derives.add(derive_flag.removeprefix(derive_flag_prefix)) + self.derives = derives def translate(self, cc_db: str, ld_lib_path: str, extra_args: List[str] = []) -> RustFile: extensionless_file, _ = os.path.splitext(self.path) @@ -97,8 +102,9 @@ def translate(self, cc_db: str, ld_lib_path: str, extra_args: List[str] = []) -> args.append("--reorganize-definitions") if self.emit_build_files: args.append("--emit-build-files") - if self.derive_debug: - args.append("--derive-debug") + + for derive in self.derives: + args.extend(["--derive", derive]) if self.log_level == 'DEBUG': args.append("--log-level=debug") diff --git a/tests/structs/src/debug_derive_bad.c b/tests/structs/src/debug_derive_bad.c index 65ea83ad67..fa62720424 100644 --- a/tests/structs/src/debug_derive_bad.c +++ b/tests/structs/src/debug_derive_bad.c @@ -1,4 +1,4 @@ -//! derive_debug +//! derive:Debug // A struct containing a union is not debuggable typedef struct { diff --git a/tests/structs/src/debug_derive_good.c b/tests/structs/src/debug_derive_good.c index 8c9bbb48a1..bdbf71ea03 100644 --- a/tests/structs/src/debug_derive_good.c +++ b/tests/structs/src/debug_derive_good.c @@ -1,4 +1,4 @@ -//! derive_debug +//! derive:Debug #include