use super::*;
use crate::core::ir::PrimitiveType;
fn folded_default(source: &str, path: &str, field_ty: TypeRef) -> DefaultValue {
let file: syn::File = syn::parse_str(source).expect("valid module source");
let literal_consts = collect_literal_consts(&file.items);
let constructors = collect_constructors(&file.items);
let free_functions = collect_free_functions(&file.items);
let mut fields = vec![FieldDef {
name: "field".to_string(),
ty: field_ty,
typed_default: Some(DefaultValue::FunctionCall(path.to_string())),
..Default::default()
}];
fold_constant_default_functions(&mut fields, &free_functions, &constructors, &literal_consts);
fields.remove(0).typed_default.expect("typed_default is always Some")
}
fn foldable_cases() -> Vec<(&'static str, &'static str, TypeRef, DefaultValue)> {
vec![
(
"fn default_page_number() -> u32 { 1 }",
"default_page_number",
TypeRef::Primitive(PrimitiveType::U32),
DefaultValue::IntLiteral(1),
),
(
"pub fn default_page_number() -> u32 { 1 }",
"default_page_number",
TypeRef::Primitive(PrimitiveType::U32),
DefaultValue::IntLiteral(1),
),
(
r#"fn default_model() -> String { "gpt".to_string() }"#,
"default_model",
TypeRef::String,
DefaultValue::StringLiteral("gpt".to_string()),
),
(
"fn default_timeout() -> u32 { return 30; }",
"default_timeout",
TypeRef::Primitive(PrimitiveType::U32),
DefaultValue::IntLiteral(30),
),
(
r#"
const DEFAULT_PAGE: u32 = 1;
fn default_page_number() -> u32 { DEFAULT_PAGE }
"#,
"default_page_number",
TypeRef::Primitive(PrimitiveType::U32),
DefaultValue::IntLiteral(1),
),
(
r#"
pub struct Settings;
impl Settings {
fn default_retry_limit() -> u32 { 3 }
}
"#,
"Settings::default_retry_limit",
TypeRef::Primitive(PrimitiveType::U32),
DefaultValue::IntLiteral(3),
),
(
r#"
pub struct Settings;
impl Settings {
pub fn default_retry_limit() -> u32 { 3 }
}
"#,
"crate::settings::Settings::default_retry_limit",
TypeRef::Primitive(PrimitiveType::U32),
DefaultValue::IntLiteral(3),
),
(
"fn default_items() -> Vec<u32> { Vec::new() }",
"default_items",
TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U32))),
DefaultValue::Empty,
),
]
}
#[test]
fn foldable_function_defaults_resolve_to_their_literal_value() {
for (source, path, field_ty, expected) in foldable_cases() {
let resolved = folded_default(source, path, field_ty);
assert_eq!(resolved, expected, "path `{path}` against source:\n{source}");
}
}
#[test]
fn multi_statement_body_is_not_folded() {
let source = r#"
fn default_page_number() -> u32 {
let base = 1;
base
}
"#;
let resolved = folded_default(source, "default_page_number", TypeRef::Primitive(PrimitiveType::U32));
assert_eq!(
resolved,
DefaultValue::FunctionCall("default_page_number".to_string()),
"a multi-statement body must be left exactly as `extract_field` recorded it"
);
}
#[test]
fn body_that_folds_to_unresolved_keeps_the_original_function_call() {
let source = "fn computed_value() -> u32 { read_from_disk() }";
let resolved = folded_default(source, "computed_value", TypeRef::Primitive(PrimitiveType::U32));
assert_eq!(
resolved,
DefaultValue::FunctionCall("computed_value".to_string()),
"an unfoldable body must not overwrite the recorded function path with a guess"
);
}
#[test]
fn function_with_arguments_is_not_folded() {
let source = "fn default_page_number(seed: u32) -> u32 { seed }";
let resolved = folded_default(source, "default_page_number", TypeRef::Primitive(PrimitiveType::U32));
assert_eq!(resolved, DefaultValue::FunctionCall("default_page_number".to_string()));
}
#[test]
fn async_function_is_not_folded() {
let source = "async fn default_page_number() -> u32 { 1 }";
let resolved = folded_default(source, "default_page_number", TypeRef::Primitive(PrimitiveType::U32));
assert_eq!(resolved, DefaultValue::FunctionCall("default_page_number".to_string()));
}
#[test]
fn unresolvable_path_is_not_folded() {
let source = "pub struct Unrelated;";
let resolved = folded_default(source, "default_page_number", TypeRef::Primitive(PrimitiveType::U32));
assert_eq!(resolved, DefaultValue::FunctionCall("default_page_number".to_string()));
}
#[test]
fn cfg_test_gated_function_is_not_resolved() {
let source = r#"
#[cfg(test)]
fn default_page_number() -> u32 { 1 }
"#;
let resolved = folded_default(source, "default_page_number", TypeRef::Primitive(PrimitiveType::U32));
assert_eq!(
resolved,
DefaultValue::FunctionCall("default_page_number".to_string()),
"a #[cfg(test)]-gated function must never be read as if it were the real default"
);
}