1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! Regression tests for [`crate::validate::builder_setter_collisions::validate_no_add_setter_collision`]
//! (issue #661) — the append-setter half of the builder-name reservation.
//!
//! Split out of the sibling `tests_builder_collisions_derived` file rather
//! than appended to it, to stay inside the repo's ~200-LoC ceiling (that
//! file was already at 151 lines).
//!
//! `cratestack-macros/src/builder/fields.rs::build_spec` derives every
//! list-arity field's append setter mechanically as `add_{field.name}`
//! (Rust) / `add{Field}` (Dart, capitalized) — no singularization. A field
//! set that declares both a list field and a real field landing on that
//! same generated name produces two identically-named setters: the
//! generator would go on to emit `error[E0592]: duplicate definitions with
//! name `add_tags`` (Rust) or a `duplicate_definition` from `dart analyze`
//! (Dart), in both cases with no span pointing at the schema line at
//! fault. These tests pin the parse-time rejection instead.
use parse_schema;
// Deliberately no `datasource` block: `cratestack-parser`'s
// `validate_field_list_arity_support` rejects a scalar list-valued model
// field (`tags String[]`) on any schema that declares a `datasource` —
// there is no SQL bind representation for a list column — so a
// datasource-bearing fixture would fail for that unrelated reason before
// ever reaching the append-setter collision check these tests pin. A
// schema reachable only through `include_client_schema!` never binds SQL
// values, so the restriction doesn't apply; see
// `crates/cratestack-client/tests/fixtures/builder_pattern.cstack`'s own
// comment for the precedent.
/// The Rust generator spells the append setter for `tags` (a list field)
/// `add_tags` literally — a sibling field declared with that exact name
/// collides with it.
/// Same clash, camelCase spelling — the Dart generator spells the same
/// setter `addTags`, so a field literally named `addTags` collides in Dart
/// even though it doesn't literally match the Rust spelling `add_tags`.
/// `to_snake_case` must normalize both onto the same comparison, the same
/// way the `build`/`set_build` check already does for `setBuild`.
/// The inverse guard: `add_foo` beside a *scalar* `foo` must keep parsing.
/// A scalar field generates no append setter at all — the reserved name
/// `add_foo` only exists when `foo` is list-arity. Getting this over-broad
/// (rejecting `add_x` beside any `x`, list or not) is a real regression:
/// an earlier revision of the sibling `builder_collisions.rs` validator
/// made exactly this mistake for a different collision class (falsely
/// rejecting `procedure WidgetBuilder` next to `model Widget`) and had to
/// be reverted.