Skip to content

v0.67.2

Choose a tag to compare

@Goldziher Goldziher released this 23 Aug 16:32
· 802 commits to main since this release
v0.67.2
3f485ff

Fixed

  • Java: a non-optional Vec/Map field carrying #[serde(default, skip_serializing_if = "...")]
    no longer emits @Nullable on the generated record component. The builder already defaulted such
    fields to List.of()/Map.of(), but the record component was independently marked @Nullable
    because has_serde_default alone drove that decision -- so a payload omitting the key (which
    skip_serializing_if guarantees for an empty collection) passed null into the record's
    canonical constructor, throwing NullPointerException on .isEmpty() downstream even though the
    underlying Rust Vec<T>/HashMap<K, V> is never null. The record now emits a compact-constructor
    line normalizing null to 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 as freezed 4.0.0-dev.3 before 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 &param.0, treating
    the bridge String as 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>-ffi assumes 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 all format gate and the publish-asset guard are hermetic across platforms. The
    format gate installs its own stub formatter on PATH instead of depending on poly being
    present, and the publish-asset guard's Unix-only shell helpers are cfg-gated so the suite
    compiles on Windows.

  • Dart FRB: frb_generated.rs no longer diverges between alef build and alef generate on
    identical input. alef build's CarryFrbCfgGates post-build step wrote
    flutter_rust_bridge_codegen's raw, unformatted output straight to disk, while alef generate
    additionally ran a separate poly fmt pass over the same file afterward -- two alef commands
    regenerating unchanged input then disagreed on the committed bytes (e.g. use import grouping
    order), producing spurious diffs on every regeneration. CarryFrbCfgGates now normalizes the
    file through the same normalize_content pass the guarded generator path hashes against, so
    both commands converge on one canonical form. (#179)

  • alef verify now detects Dart FRB frb_generated.rs drift. The file is written by an external
    tool and rewritten in place by CarryFrbCfgGates, so it never carries alef's own embedded hash
    marker and was structurally invisible to alef verify's per-file staleness check -- it could
    silently fall behind (stale #[cfg(...)] gates, or non-canonical formatting) with zero signal.
    alef verify now recomputes the same canonical form CarryFrbCfgGates would write and reports
    a difference as drift. (#179)

  • e2e/java: stop inlining large fixture values as a single Java string literal. The JVM caps
    a CONSTANT_Utf8 constant-pool entry (and javac a 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_object setup (snippet.rs + snippet_json_object_setup.jinja), the e2e test method's
    from_json builder path (test_method.rs), the HTTP mock request body (http.rs), the
    equals assertion literal (assertions.rs), and the handle/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-byte CONSTANT_Utf8 cap, so it had the same live
    defect. kotlin_string_literal (new, src/e2e/codegen/kotlin/values.rs) mirrors
    java_string_literal. Wired through json_to_kotlin, both snippet_json_object_setup.jinja
    call sites (the handle-config and json_object paths in args.rs), the streaming-request
    from_json builder path shared by snippet.rs and test_method.rs, the HTTP mock request body
    (http.rs), the equals assertion literal, and the array-element json_object embed.

  • alef build no longer silently discards PostBuildOutcome::skipped_missing_tools: both
    post-build call sites in build_with_environment now 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 signal alef generate/alef all already gave via run_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. Mirrors ensure_requested_suites_will_run's semantics for alef test. A run with no
    --lang filter and no [e2e].languages configured anywhere is unaffected (still a legitimate
    non-fatal no-op).

  • e2e/java: an equals assertion carrying a literal null against a non-optional collection
    field no longer renders assertEquals(null, result.field()) -- a comparison the generated
    binding can never satisfy, because its Jackson builder defaults an absent, serde-defaulted
    collection to List.of(). with_ir_collection_map was 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.shows nor docs.presentation no longer emits
    a snippet that bottoms out at a bare print(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 scaffold now allowlists bare cfg(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, but cfg(alef) is never a real declared cfg, so rustc's
    unexpected_cfgs fired on every use and any lane compiling with -D warnings denied it.

  • A user [e2e.format] override's {dir} placeholder now expands to a path a POSIX shell can
    cd into on Windows. canonicalize returns the extended-length form \\?\C:\..., and sh
    reads every \ as an escape, so the cd in 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.rs nested its stale-backup cleanup inside if had_destination, which clippy rejects as collapsible_if under -D warnings. Because the
    file carries generated_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.*.exs in mix 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 every alef build. The emission
    now wraps per entry exactly where the formatter would — honouring line_length from the package's
    .formatter.exs, falling back to Elixir's default of 98 — and renders through a Minijinja template
    instead of push_str(&format!(...)).

  • Tests that shell out to git no longer inherit the ambient global git configuration. Fixture
    repositories are now built through a single hermetic test_support::git_command helper that
    neutralizes GIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEM and pins identity, signing, excludes and the
    default branch name. Previously a developer with commit.gpgsign = true set 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 that git could 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_android file_path behaviours 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 PHP options_type import assertion exposed a wrong
    expectation the dead arm had masked (use SampleCrate\… where codegen emits use 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, and cargo test --lib does not run them.

Changed

  • Split four over-cap source files at their concept boundaries so the file-size ratchet passes:
    dart build.rs/flutter_rust_bridge.yaml emission out of gen_rust_crate/cargo.rs, java
    bracket-wildcard assertion rendering out of e2e/codegen/java/assertions.rs, crate-attribute
    formatting out of codegen/shared.rs, and fixture field resolution out of e2e/codegen/mod.rs.
    Pure restructuring; existing public paths are preserved by re-export.

Added

  • tests/test_vacuity_gate.rs: a mechanical guard over tests/**/*.rs rejecting three shapes of
    check that cannot fail — a #[test] with an empty body, a x.contains(a) || x.contains(b)
    disjunction whose needles subsume each other, and a narrowed cargo test invocation in
    ci.yml's test job (which would retire all ~253 integration binaries while every signal
    stayed green).