Skip to content
Merged
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
6 changes: 6 additions & 0 deletions daft-derive/src/internals/imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,12 @@ impl DiffFields {
// future.
f.attrs = vec![];

// Drop default field values (Rust RFC 3681). The default is an
// expression of the original field type, so retaining it while
// rewriting `f.ty` would emit something like `a: <u32 as
// Diffable>::Diff<'__daft> = 5`, which is definitely wrong.
f.default = None;

Some((f, config))
}

Expand Down
106 changes: 106 additions & 0 deletions daft-derive/tests/integration/default_field_values.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//! Test that default field values don't make their way into generated diff
//! structs.
//!
//! Default field values ([Rust RFC 3681]) let a struct field carry an
//! initializer, as in `struct Foo { a: u32 = 5 }`. Daft drops these in its own
//! struct definitions.
//!
//! These tests live here rather than as snapshot tests for two reasons:
//! because trybuild runs against stable where (as of Rust 1.97) default values
//! aren't supported, and because prettyplease 0.3.0 seems to eat default field
//! values. Worth revisiting in the future.
//!
//! [Rust RFC 3681]: https://rust-lang.github.io/rfcs/3681-default-field-values.html

use quote::ToTokens;

#[path = "../../src/internals/mod.rs"]
mod internals;

/// Parse `input`, run the derive macro over it, and return the generated diff
/// struct.
fn derive_diff_struct(input: &str) -> syn::ItemStruct {
let parsed: syn::DeriveInput =
syn::parse_str(input).expect("input parsed as a DeriveInput");
let diff_name = format!("{}Diff", parsed.ident);

let generated = internals::derive_diffable(parsed).into_token_stream();
let file: syn::File =
syn::parse2(generated.clone()).unwrap_or_else(|err| {
panic!("generated code parsed as a file: {err}\n{generated}");
});

file.items
.into_iter()
.find_map(|item| match item {
syn::Item::Struct(s) if s.ident == diff_name => Some(s),
_ => None,
})
.unwrap_or_else(|| {
panic!("generated code defines {diff_name}:\n{generated}");
})
}

/// Assert that no field of `s` carries a default value.
fn assert_no_field_defaults(s: &syn::ItemStruct) {
let kept: Vec<_> = s
.fields
.iter()
.enumerate()
.filter(|(_, f)| f.default.is_some())
.map(|(i, f)| match &f.ident {
Some(ident) => ident.to_string(),
None => i.to_string(),
})
.collect();

assert!(
kept.is_empty(),
"fields of {} kept a default value from the original struct: {:?}\n{}",
s.ident,
kept,
s.to_token_stream(),
);
}

/// Return the names of `s`'s fields, in declaration order.
fn field_names(s: &syn::ItemStruct) -> Vec<String> {
s.fields
.iter()
.map(|f| f.ident.as_ref().expect("named field").to_string())
.collect()
}

/// Test that default on a named field is dropped from the diff struct.
///
/// The rest of the struct still diffs normally when a default is present.
#[test]
fn named_field_default_is_dropped() {
let diff =
derive_diff_struct("struct HasDefault { a: u32 = 5, b: String }");
assert_no_field_defaults(&diff);
assert_eq!(field_names(&diff), ["a", "b"]);
}

/// Test that a default on a `#[daft(leaf)]` field is dropped too.
#[test]
fn leaf_field_default_is_dropped() {
let diff = derive_diff_struct(
"struct HasLeafDefault { #[daft(leaf)] a: u32 = 5 }",
);
assert_no_field_defaults(&diff);
assert_eq!(field_names(&diff), ["a"]);
}

/// Test that a default on an ignored field doesn't reappear.
///
/// `#[daft(ignore)]` drops the field entirely, so the diff struct is empty and
/// there is nothing for a default to attach to.
#[test]
fn ignored_field_default_is_dropped() {
let diff = derive_diff_struct(
"struct HasIgnoredDefault { #[daft(ignore)] a: u32 = 5, b: String }",
);
assert_no_field_defaults(&diff);
assert_eq!(field_names(&diff), ["b"]);
}
1 change: 1 addition & 0 deletions daft-derive/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
//! test modules don't have to build a separate binary.

mod basic;
mod default_field_values;
mod generics;
Loading