use super::*;
fn assert_every_field_unresolved(resolved: &[(String, DefaultValue)], reason: &str) {
assert!(!resolved.is_empty(), "the fixture must resolve at least one field");
for (name, value) in resolved {
assert!(
matches!(value, DefaultValue::Unresolved(_)),
"`{name}` must be Unresolved because {reason}, got {value:?}"
);
}
}
#[test]
fn map_insert_is_unresolved_because_the_ir_cannot_represent_a_populated_map() {
let resolved = defaults_for_typed(
r#"
pub struct Prefs { pub headers: HashMap<String, String> }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { headers: HashMap::new() };
prefs.headers.insert("accept".to_string(), "json".to_string());
prefs
}
}
"#,
"Prefs",
&[(
"headers",
TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::String)),
)],
);
assert_every_field_unresolved(&resolved, "the IR has no populated-map default");
}
#[test]
fn set_insert_is_unresolved_for_the_same_reason_as_map_insert() {
let resolved = defaults_for_typed(
r#"
pub struct Prefs { pub allowed: HashSet<String> }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { allowed: HashSet::new() };
prefs.allowed.insert("alpha".to_string());
prefs
}
}
"#,
"Prefs",
&[("allowed", TypeRef::Named("HashSet".to_string()))],
);
assert_every_field_unresolved(&resolved, "a populated set has no IR representation");
}
#[test]
fn custom_named_push_and_extend_are_unresolved() {
let resolved = defaults_for_typed(
r#"
#[derive(Default)]
pub struct Bag(Vec<String>);
impl Bag {
fn push(&mut self, value: String) { self.0.push(value); }
fn extend(&mut self, values: Vec<String>) { self.0.extend(values); }
}
pub struct Prefs { pub pushed: Bag, pub extended: Bag }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self {
pushed: Bag::default(),
extended: Bag::default(),
};
prefs.pushed.push("alpha".to_string());
prefs.extended.extend(vec!["beta".to_string()]);
prefs
}
}
"#,
"Prefs",
&[
("pushed", TypeRef::Named("Bag".to_string())),
("extended", TypeRef::Named("Bag".to_string())),
],
);
assert_every_field_unresolved(
&resolved,
"a custom named type's methods do not prove Vec mutation semantics",
);
}
#[test]
fn handing_the_binding_to_a_helper_makes_every_field_unresolved() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub max_depth: u32 }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { max_depth: 0 };
tune(&mut prefs);
prefs
}
}
"#,
"Prefs",
&["max_depth"],
);
assert_every_field_unresolved(&resolved, "the binding escaped into a helper");
}
#[test]
fn an_assignment_whose_value_reads_the_binding_makes_every_field_unresolved() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub width: u32, pub height: u32 }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { width: 0, height: 3 };
prefs.width = scale(&prefs);
prefs
}
}
"#,
"Prefs",
&["width", "height"],
);
assert_every_field_unresolved(&resolved, "the assignment's value aliases the binding");
}
#[test]
fn a_branch_returning_two_different_values_is_unresolved() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub max_depth: u32 }
impl Default for Prefs {
fn default() -> Self {
let base = Self { max_depth: 1 };
if compact() { base } else { Self { max_depth: 2 } }
}
}
"#,
"Prefs",
&["max_depth"],
);
assert_every_field_unresolved(&resolved, "the body returns one of two different values");
}
#[test]
fn an_early_return_is_unresolved() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub max_depth: u32 }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { max_depth: 1 };
if compact() { return prefs; }
prefs.max_depth = 2;
prefs
}
}
"#,
"Prefs",
&["max_depth"],
);
assert_every_field_unresolved(&resolved, "an early return leaves the mutation count unknown");
}
#[test]
fn a_loop_that_pushes_is_unresolved() {
let resolved = defaults_for_typed(
r#"
pub struct Prefs { pub tags: Vec<String> }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { tags: Vec::new() };
for tag in seed_tags() { prefs.tags.push(tag); }
prefs
}
}
"#,
"Prefs",
&[("tags", TypeRef::Vec(Box::new(TypeRef::String)))],
);
assert_every_field_unresolved(&resolved, "a loop pushes an unknown number of elements");
}
#[test]
fn an_unmodelled_mutating_method_is_unresolved() {
let resolved = defaults_for_typed(
r#"
pub struct Prefs { pub tags: Vec<String> }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { tags: vec!["alpha".to_string()] };
prefs.tags.clear();
prefs
}
}
"#,
"Prefs",
&[("tags", TypeRef::Vec(Box::new(TypeRef::String)))],
);
assert_every_field_unresolved(&resolved, "`clear` empties the very list the literal filled");
}
#[test]
fn a_compound_assignment_is_unresolved() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub max_depth: u32 }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { max_depth: 1 };
prefs.max_depth += 4;
prefs
}
}
"#,
"Prefs",
&["max_depth"],
);
assert_every_field_unresolved(&resolved, "a compound assignment is not a modelled mutation");
}
#[test]
fn a_nested_field_assignment_is_unresolved() {
let resolved = defaults_for_typed(
r#"
pub struct Prefs { pub limits: Limits }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { limits: Limits::new() };
prefs.limits.max_depth = 3;
prefs
}
}
"#,
"Prefs",
&[("limits", TypeRef::Named("Limits".to_string()))],
);
assert_every_field_unresolved(&resolved, "the nested value's own shape was never read");
}
#[test]
fn a_second_binding_of_another_type_is_never_read_as_this_types_default() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub max_depth: u32 }
impl Default for Prefs {
fn default() -> Self {
let prefs = Self { max_depth: 1 };
let unrelated = Limits { max_depth: 2 };
prefs
}
}
"#,
"Prefs",
&["max_depth"],
);
assert_every_field_unresolved(&resolved, "an unreturned binding of another type is not the default");
}
#[test]
fn a_push_of_an_unfoldable_element_is_unresolved() {
let resolved = defaults_for_typed(
r#"
pub struct Prefs { pub tags: Vec<String> }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { tags: Vec::new() };
prefs.tags.push(detect_locale());
prefs
}
}
"#,
"Prefs",
&[("tags", TypeRef::Vec(Box::new(TypeRef::String)))],
);
assert_every_field_unresolved(&resolved, "the pushed element could not be folded");
}
#[test]
fn a_mutated_literal_with_a_rest_base_is_unresolved() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub width: u32, pub height: u32 }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { width: 1, ..seed() };
prefs.width = 2;
prefs
}
}
"#,
"Prefs",
&["width", "height"],
);
assert_every_field_unresolved(&resolved, "the literal's `..base` was never read");
}
#[test]
fn a_cfg_gated_push_is_unresolved_when_the_feature_would_be_on() {
let resolved = defaults_for_typed(
r#"
pub struct Prefs { pub tags: Vec<String> }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { tags: Vec::new() };
#[cfg(feature = "extras")]
prefs.tags.push("alpha".to_string());
prefs
}
}
"#,
"Prefs",
&[("tags", TypeRef::Vec(Box::new(TypeRef::String)))],
);
assert_every_field_unresolved(&resolved, "a cfg-gated mutation may or may not exist in a build");
}
#[test]
fn a_cfg_gated_push_is_unresolved_when_the_feature_would_be_off() {
let resolved = defaults_for_typed(
r#"
pub struct Prefs { pub tags: Vec<String> }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { tags: Vec::new() };
#[cfg(not(feature = "extras"))]
prefs.tags.push("alpha".to_string());
prefs
}
}
"#,
"Prefs",
&[("tags", TypeRef::Vec(Box::new(TypeRef::String)))],
);
assert_every_field_unresolved(&resolved, "a cfg-gated push may or may not exist in a build");
}
#[test]
fn a_cfg_attr_gated_mutation_is_unresolved() {
let resolved = defaults_for_typed(
r#"
pub struct Prefs { pub tags: Vec<String> }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { tags: Vec::new() };
#[cfg_attr(feature = "extras", allow(unused))]
prefs.tags.push("alpha".to_string());
prefs
}
}
"#,
"Prefs",
&[("tags", TypeRef::Vec(Box::new(TypeRef::String)))],
);
assert_every_field_unresolved(&resolved, "cfg_attr hides an arbitrary attribute behind a feature");
}
#[test]
fn an_unrecognized_attribute_on_a_mutation_is_unresolved() {
let resolved = defaults_for_typed(
r#"
pub struct Prefs { pub tags: Vec<String> }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { tags: Vec::new() };
#[rustfmt::skip]
prefs.tags.push("alpha".to_string());
prefs
}
}
"#,
"Prefs",
&[("tags", TypeRef::Vec(Box::new(TypeRef::String)))],
);
assert_every_field_unresolved(&resolved, "an unmodelled attribute is refused rather than allowlisted");
}
#[test]
fn an_attributed_local_binding_is_unresolved() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub max_depth: u32 }
impl Default for Prefs {
fn default() -> Self {
#[cfg(feature = "extras")]
let prefs = Self { max_depth: 7 };
#[cfg(not(feature = "extras"))]
let prefs = Self { max_depth: 0 };
prefs
}
}
"#,
"Prefs",
&["max_depth"],
);
assert_every_field_unresolved(&resolved, "the binding itself is cfg-gated");
}
#[test]
fn a_tail_literal_after_a_conditional_early_return_is_unresolved() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub max_depth: u32 }
impl Default for Prefs {
fn default() -> Self {
if compact() {
return Self { max_depth: 1 };
}
Self { max_depth: 2 }
}
}
"#,
"Prefs",
&["max_depth"],
);
assert_every_field_unresolved(&resolved, "an earlier exit returns a different value");
}
#[test]
fn a_tail_literal_after_a_conditional_early_return_of_a_binding_is_unresolved() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub max_depth: u32 }
impl Default for Prefs {
fn default() -> Self {
let fallback = Self { max_depth: 1 };
if compact() {
return fallback;
}
Self { max_depth: 2 }
}
}
"#,
"Prefs",
&["max_depth"],
);
assert_every_field_unresolved(&resolved, "an earlier exit returns a different value");
}
#[test]
fn a_macro_statement_before_a_tail_literal_is_unresolved() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub max_depth: u32 }
impl Default for Prefs {
fn default() -> Self {
return_if_configured!(compact());
Self { max_depth: 2 }
}
}
"#,
"Prefs",
&["max_depth"],
);
assert_every_field_unresolved(&resolved, "a macro's expansion is not parsed");
}
#[test]
fn a_cfg_gated_block_of_mutations_is_unresolved() {
let resolved = defaults_for(
r#"
pub struct Prefs { pub max_depth: u32 }
impl Default for Prefs {
fn default() -> Self {
let mut prefs = Self { max_depth: 0 };
#[cfg(feature = "extras")]
{ prefs.max_depth = 9; }
prefs
}
}
"#,
"Prefs",
&["max_depth"],
);
assert_every_field_unresolved(&resolved, "the gated block may or may not run");
}