v0.67.2
Fixed
-
Java: a non-optional
Vec/Mapfield carrying#[serde(default, skip_serializing_if = "...")]
no longer emits@Nullableon the generated record component. The builder already defaulted such
fields toList.of()/Map.of(), but the record component was independently marked@Nullable
becausehas_serde_defaultalone drove that decision -- so a payload omitting the key (which
skip_serializing_ifguarantees for an empty collection) passednullinto the record's
canonical constructor, throwingNullPointerExceptionon.isEmpty()downstream even though the
underlying RustVec<T>/HashMap<K, V>is never null. The record now emits a compact-constructor
line normalizingnullto the same empty-collection literal the builder uses, and both
generators now read that literal from one shared function
(serde_default_collection_literal) rather than each deriving it. Changes generated Java output
for any consumer with such a field. -
Dart generation now uses flutter_rust_bridge 2.13 and bypasses its redundant dependency
preflight. Alef emits the bridge dependencies itself, while FRB's check rejected valid Dart
prereleases such asfreezed 4.0.0-dev.3before generation could complete. -
Swift tagged-enum parameters are deserialized before the source call. A data-carrying enum
crosses swift-bridge as a JSON string; a referenced parameter was emitted as¶m.0, treating
the bridgeStringas an opaque wrapper and failing to compile with E0609. -
The generated FFI crate builds by manifest path rather than package ID.
cargo build -p <crate>-ffiassumes the emitted crate is a member of the invoking workspace; a standalone
generated manifest is not, and cargo rejected the package spec outright. -
The
alef allformat gate and the publish-asset guard are hermetic across platforms. The
format gate installs its own stub formatter onPATHinstead of depending onpolybeing
present, and the publish-asset guard's Unix-only shell helpers arecfg-gated so the suite
compiles on Windows. -
Dart FRB:
frb_generated.rsno longer diverges betweenalef buildandalef generateon
identical input.alef build'sCarryFrbCfgGatespost-build step wrote
flutter_rust_bridge_codegen's raw, unformatted output straight to disk, whilealef generate
additionally ran a separatepoly fmtpass over the same file afterward -- two alef commands
regenerating unchanged input then disagreed on the committed bytes (e.g.useimport grouping
order), producing spurious diffs on every regeneration.CarryFrbCfgGatesnow normalizes the
file through the samenormalize_contentpass the guarded generator path hashes against, so
both commands converge on one canonical form. (#179) -
alef verifynow detects Dart FRBfrb_generated.rsdrift. The file is written by an external
tool and rewritten in place byCarryFrbCfgGates, so it never carries alef's own embedded hash
marker and was structurally invisible toalef verify's per-file staleness check -- it could
silently fall behind (stale#[cfg(...)]gates, or non-canonical formatting) with zero signal.
alef verifynow recomputes the same canonical formCarryFrbCfgGateswould write and reports
a difference as drift. (#179) -
e2e/java: stop inlining large fixture values as a single Java string literal. The JVM caps
aCONSTANT_Utf8constant-pool entry (andjavaca string literal) at 65535 bytes, a limit no
amount of escaping can raise; a fixture body long enough to threaten it made the generated Java
doc snippet, e2e test method, or HTTP mock body fail to compile.java_string_literal(new,
src/e2e/codegen/java/values.rs) renders short values exactly as before and splits longer ones
into+-concatenated literal chunks, each safely under the cap. Wired through
json_to_java_typed,emit_java_object_array,java_builder_expression, the doc-snippet
json_objectsetup (snippet.rs+snippet_json_object_setup.jinja), the e2e test method's
from_jsonbuilder path (test_method.rs), the HTTP mock request body (http.rs), the
equalsassertion literal (assertions.rs), and thehandle/IR-typed-struct JSON embeds in
args.rs. Task #180. -
e2e/kotlin: apply the identical fix to the Kotlin backend. Kotlin compiles to the same JVM
bytecode as Java and shares the exact 65535-byteCONSTANT_Utf8cap, so it had the same live
defect.kotlin_string_literal(new,src/e2e/codegen/kotlin/values.rs) mirrors
java_string_literal. Wired throughjson_to_kotlin, bothsnippet_json_object_setup.jinja
call sites (thehandle-config andjson_objectpaths inargs.rs), the streaming-request
from_jsonbuilder path shared bysnippet.rsandtest_method.rs, the HTTP mock request body
(http.rs), theequalsassertion literal, and the array-elementjson_objectembed. -
alef buildno longer silently discardsPostBuildOutcome::skipped_missing_tools: both
post-build call sites inbuild_with_environmentnow route through
record_post_build_outcome, which warns per language and adds a "post-build tool(s) skipped
(not on PATH)" count to the backend build summary, matching the signalalef generate/alef allalready gave viarun_resolved_post_builds. A missing post-build tool remains non-fatal
(falling back to committed generated output is intentional), but is no longer indistinguishable
from a clean run. -
alef test-apps run --lang <target>now fails with a clear error when the requested target(s)
matched no crate's configured[e2e].languages, instead of silently exiting 0 with no test
apps run. Mirrorsensure_requested_suites_will_run's semantics foralef test. A run with no
--langfilter and no[e2e].languagesconfigured anywhere is unaffected (still a legitimate
non-fatal no-op). -
e2e/java: an
equalsassertion carrying a literalnullagainst a non-optional collection
field no longer rendersassertEquals(null, result.field())-- a comparison the generated
binding can never satisfy, because its Jackson builder defaults an absent, serde-defaulted
collection toList.of().with_ir_collection_mapwas wired into the csharp, kotlin, swift and
rust e2e generators but never java, so java's assertion side had no IR-backed view of which
result fields are collections. Task #200. -
e2e: a docs-tagged fixture with neither
docs.showsnordocs.presentationno longer emits
a snippet that bottoms out at a bareprint(result). Field access is derived from the fixture's
own assertions, which already anchor on the same field paths the assertion resolver renders
against. Python and Rust additionally resolved presentation after clearing assertions; both are
hoisted above the clear. Task #199. -
alef scaffoldnow allowlists barecfg(alef)in[workspace.lints.rust], not just
feature = "alef-meta".#[cfg_attr(alef, alef(skip))]is alef's documented and far more
common exclusion marker, butcfg(alef)is never a real declared cfg, so rustc's
unexpected_cfgsfired on every use and any lane compiling with-D warningsdenied it. -
A user
[e2e.format]override's{dir}placeholder now expands to a path a POSIX shell can
cdinto on Windows.canonicalizereturns the extended-length form\\?\C:\..., andsh
reads every\as an escape, so thecdin the conventional(cd {dir} && ...)override
failed before the formatter ever ran. The shell then exited 1, which is not the
command-not-found status 127, so an absent formatter was misclassified as "the formatter ran
and rejected the code" and killed the run instead of being recorded as a deferred
environment gap.run_in_dir's built-in residual steps already avoided this by never going
through a shell; the override path, which must go through one, now normalises the path. -
The generated FFI crate's
build.rsnested its stale-backup cleanup insideif had_destination, which clippy rejects ascollapsible_ifunder-D warnings. Because the
file carriesgenerated_header: true, no consumer edit survived regeneration, so a consumer
had to suppress the lint in its own CI — and any lint pass alef did not know about (poly runs
its own whole-project clippy) hit it anyway. Flattened with an early return rather than a
let-chain, so the emitted crate's edition does not matter. -
Emit
checksum-Elixir.*.exsinmix format's canonical wrapped form so regeneration no longer
produces pure-reformat diffs. Each map entry was written on a single line;mix format(the sole
formatter for generated.ex/.exs) then moved every over-width digest onto its own continuation
line and dropped the trailing comma, so the file was dirty after everyalef build. The emission
now wraps per entry exactly where the formatter would — honouringline_lengthfrom the package's
.formatter.exs, falling back to Elixir's default of 98 — and renders through a Minijinja template
instead ofpush_str(&format!(...)). -
Tests that shell out to
gitno longer inherit the ambient global git configuration. Fixture
repositories are now built through a single hermetictest_support::git_commandhelper that
neutralizesGIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEMand pins identity, signing, excludes and the
default branch name. Previously a developer withcommit.gpgsign = trueset globally signed
every fixture commit, making the suite depend on a working gpg-agent, and the same tests would
behave differently on CI, which has no signing key. -
go_tag's fixture builder asserted only thatgitcould be spawned, not that it succeeded, so a
failed commit or annotated tag left the tests asserting against an empty repository instead of
the fixture they name. Each step now checks the child's exit status. -
Four
#[test]functions had empty bodies and had passed unconditionally since they were written;
they now assert the PHP namespace-qualification, PHP streaming-field disambiguation and
kotlin_androidfile_pathbehaviours against real codegen output. Fifteen further integration
assertions could not fail — ten had a dead disjunction arm, five matched needles too weak to
detect a regression. Tightening the PHPoptions_typeimport assertion exposed a wrong
expectation the dead arm had masked (use SampleCrate\…where codegen emitsuse Mylib\…). -
Refreshed Dart snapshots left stale by the flutter_rust_bridge 2.13 bump and the relocation of
carry_frb_cfg_gates()into the successful-regeneration arm. Both changes are deliberate and
separately tested; only the committed snapshots lagged, andcargo test --libdoes not run them.
Changed
- Split four over-cap source files at their concept boundaries so the file-size ratchet passes:
dartbuild.rs/flutter_rust_bridge.yamlemission out ofgen_rust_crate/cargo.rs, java
bracket-wildcard assertion rendering out ofe2e/codegen/java/assertions.rs, crate-attribute
formatting out ofcodegen/shared.rs, and fixture field resolution out ofe2e/codegen/mod.rs.
Pure restructuring; existing public paths are preserved by re-export.
Added
tests/test_vacuity_gate.rs: a mechanical guard overtests/**/*.rsrejecting three shapes of
check that cannot fail — a#[test]with an empty body, ax.contains(a) || x.contains(b)
disjunction whose needles subsume each other, and a narrowedcargo testinvocation in
ci.yml'stestjob (which would retire all ~253 integration binaries while every signal
stayed green).