chore: release fp-library v0.16.0 / fp-macros v0.7.0#26
Merged
Conversation
Add lychee to the Nix dev shell and integrate offline markdown link checking into the `just doc` recipe. Broken relative links in README and docs now fail the doc check. Update release.yml validate job to use `just verify` instead of raw cargo commands, matching ci.yml and ensuring unicode, link, and doc checks run consistently. Fix README doc links to point to fp-library/docs/ after the docs move.
The library contains no unsafe blocks. Forbidding unsafe at the crate level prevents accidental introduction and documents this guarantee.
Design plan for extending the type class hierarchy with by-ref trait variants (RefPointed, RefLift, RefSemiapplicative, RefSemimonad) and marker-type dispatch to unify map/bind/apply free functions across owned and borrowed containers. Enables Lazy to implement the full Functor -> Applicative -> Monad chain. Key decisions: independent traits (no sub/supertrait relationship), Fn over FnOnce for by-ref closures, Clone bound only on RefPointed, ref qualifier for m_do!, skip RefFoldable/RefTraversable initially.
Add marker-type dispatch infrastructure in functor_dispatch.rs that routes a single `map` free function to either Functor::map or RefFunctor::ref_map based on the closure's argument type. Val/Ref marker types and a FunctorDispatch trait enable the compiler to infer the correct dispatch target at compile time with no runtime cost. Remove the separate `map` free function from functor.rs and `ref_map` free function from ref_functor.rs. Update all call sites from `map::<Brand, _, _>` to `map::<Brand, _, _, _>` (adding the inferred Marker parameter). Update the a_do! macro codegen to emit 4 generic arguments. Fix benchmark imports to use functor_dispatch::map. Update compile-fail test stderr for arc_coyoneda after associated type bounds changes. Add #![forbid(unsafe_code)] to the crate. Add benchmark compile check to just verify. Fix test cache find paths to include fp-library/tests and fp-library/benches. Add plan for by-reference trait hierarchy with unified dispatch, documenting design decisions, POC results, and rejected alternatives.
Design plan for a DefaultBrand trait that maps concrete types back to their brand, enabling turbofish-free calls like map_infer(f, xs) for types with a single unambiguous brand (Option, Vec, Identity, CatList, Lazy, Coyoneda variants, etc.). Purely additive; existing turbofish code is unaffected. Types with multiple brands (Result, Tuple2, Pair, ControlFlow) remain explicit.
Make inference-based functions the primary API (map, bind, apply) with explicit-brand versions renamed to _brand suffix. Add POC step to verify GAT equality bounds before implementation. Switch impl_kind! DefaultBrand generation to opt-out (#[no_default_brand]) instead of opt-in. Drop pure_infer (return-type inference is unreliable). Remove absolute filesystem paths from references. Add section on interaction with ref-hierarchy dispatch extensions.
Add POC tests in functor_dispatch.rs confirming that DefaultBrand trait enables turbofish-free map calls. Inference works for Option, Vec (Val dispatch) and RcLazy (Ref dispatch). ArcLazy blocked on SendRefFunctor dispatch from the ref-hierarchy plan. Expand plan with DefaultBrand trait family using content-hash naming to mirror Kind traits across all arities. This enables bimap inference for bifunctor types (Result, Tuple2, Pair, ControlFlow) that are ambiguous at arity 1 but unambiguous at arity 2. Add trait_default_brand! and DefaultBrand! macros to the implementation order. Add diagnostic::on_unimplemented for error messages. Use _explicit suffix. Document incremental scope and parallel function exclusion.
…o Fn Change ref_map and send_ref_map trait method signatures from FnOnce(&A) -> B to Fn(&A) -> B. This enables types like Vec to implement RefFunctor (calling the closure per element) while Lazy continues to work (calls it once). Update FunctorDispatch Ref impl, Lazy and TryLazy trait impls to match. Breaking change: closures that move out of captures (FnOnce but not Fn) no longer compile with ref_map. These are rare and can be restructured. This change is a prerequisite for the by-reference trait hierarchy (RefApplicative, RefMonad, etc.).
Add three new by-reference type class traits enabling memoized types to participate in the applicative/monadic hierarchy: - RefPointed: construct a context from &A (requires A: Clone) - RefLift: lift a binary Fn(&A, &B) -> C over two contexts - RefSemimonad: sequence with Fn(&A) -> Of<B> Implement all three for LazyBrand<RcLazyConfig>. ArcLazyConfig impls require SendRef variants (SendRefPointed, SendRefLift, SendRefSemimonad) due to Send + Sync bounds on ArcLazy::new, to be added in a follow-up. Register new modules in classes.rs. Free functions (ref_pure, ref_lift2, ref_bind) are auto-re-exported via generate_function_re_exports.
Mark Steps 3-4 as done (FnOnce->Fn change, RefPointed/RefLift/ RefSemimonad with RcLazy impls). Add open questions section covering RefSemiapplicative integration with CloneableFn and whether RefMonad can bypass RefApplicative. Recommend deferring RefSemiapplicative and defining RefMonad as RefPointed + RefSemimonad directly.
Replace deferred RefSemiapplicative approach with CloneableFn<Mode> design: add a default Mode parameter to CloneableFn/SendCloneableFn using the Val/Ref markers from FunctorDispatch. This enables RefSemiapplicative without new function wrapper traits, unblocking the standard RefApplicative and RefMonad hierarchy. Update implementation order to include CloneableFn Mode parameter step and re-sequence remaining steps. Mark all open questions as resolved.
Replace the naive CloneableFn<Mode> design with the ClosureMode trait approach. Document the two obstacles (Deref bound on associated type, Function: Category + Strong supertrait) and explain how ClosureMode resolves them by parameterizing the Deref target. Include fallback approaches (separate RefCloneableFn trait on backup/ref-cloneable-fn branch, remove Deref bound entirely) in case the ClosureMode GAT interaction doesn't compile. Add open question about unsized GAT + Deref interaction verification.
Document the Callable/CloneableCallable/Arrow naming scheme for the function wrapper trait hierarchy. Renames are deferred to Step 10 (after structural changes are verified) since they are mechanical and independent of the ClosureMode design. Callable<Mode> extracts the shared Deref bound. CloneableFn becomes CloneableCallable. Function becomes Arrow (aligning with Haskell's Arrow type class).
Use LiftFn/SendLiftFn as pre-rename names for the construction trait (split from CloneableFn::new). Finalize post-rename names: Function<Mode> (base callable), Arrow (composable), CloneFn (cloneable), LiftFn (construction). The rename step reclaims Function for the general concept and introduces Arrow for the Category + Strong specialization.
Add ClosureMode trait with Val/Ref impls that parameterize the Deref target of CloneableFn wrappers. CloneableFn<Val> wraps Fn(A) -> B (default, unchanged for existing code). CloneableFn<Ref> wraps Fn(&A) -> B (new, for RefSemiapplicative). Split CloneableFn::new into a separate LiftFn: CloneableFn<Val> trait because the closure parameter type depends on the mode and cannot be expressed in a single trait method signature. Rename the free function from cloneable_fn_new to lift_fn_new. Remove Function supertrait from CloneableFn. CloneableFn's purpose is cloneable+callable wrappers, not composability. Code needing Category/Strong adds + Function explicitly. Add coerce_ref_fn to UnsizedCoercible for constructing by-ref wrappers. Implement for RcBrand and ArcBrand. Convert many explicit class imports to wildcards to prevent future import breakage when new traits are added to modules.
Mark Step 5 (ClosureMode + LiftFn split) as done. Add new Step 6 for CloneableFn<Ref> impl and SendCloneableFn parameterization. Renumber remaining steps. Update completed changes section with all ClosureMode-related work.
ClosureMode approach succeeded. Remove fallback approaches B and C. Mark unsized GAT open question as verified.
…Ref> Add SendTarget GAT to ClosureMode for Send+Sync closure targets. Parameterize SendCloneableFn with Mode, defaulting to Val. Split SendCloneableFn::send_cloneable_fn_new into SendLiftFn::new trait. Add CloneableFn<Ref> and SendCloneableFn<Ref> impls for FnBrand<P>, using P::CloneableOf and P::SendOf with Fn(&A) -> B targets. These enable RefSemiapplicative to use CloneableFn<Ref> for by-ref function wrappers without creating new traits. Rename free function from send_cloneable_fn_new to send_lift_fn_new. Update all call sites and regenerate compile-fail test stderr files.
Add RefSemiapplicative trait for applying wrapped by-ref functions (CloneableFn<Ref>) within contexts. The wrapped functions receive &A instead of owned A, so no Clone bound on elements is needed. Implement for LazyBrand<RcLazyConfig>: evaluates both the function and value lazily, then applies the by-ref function to produce the result.
RefApplicative = RefPointed + RefSemiapplicative. RefMonad = RefApplicative + RefSemimonad. LazyBrand<RcLazyConfig> automatically implements both via the blanket impls, completing the by-ref Functor -> Applicative -> Monad chain for RcLazy. Document applicative and monad laws with RcLazy examples showing left identity, right identity, and associativity. Add plan entry for ref parity traits (RefApplyFirst, RefApplySecond, ref_monad_if, etc.) and their SendRef equivalents.
Add four thread-safe by-ref traits: SendRefPointed, SendRefLift, SendRefSemiapplicative, SendRefSemimonad. Each mirrors its Ref counterpart but adds Send + Sync bounds on element types and Send on closures. Implement all four for LazyBrand<ArcLazyConfig>, completing the thread-safe by-ref trait hierarchy for ArcLazy. Fix doc links in RefApplicative and RefMonad module headers.
SendRefApplicative = SendRefPointed + SendRefSemiapplicative. SendRefMonad = SendRefApplicative + SendRefSemimonad. LazyBrand<ArcLazyConfig> automatically implements both, completing the thread-safe by-ref Functor -> Applicative -> Monad chain for ArcLazy. Document monad laws with ArcLazy examples.
Add RefApplyFirst and RefApplySecond with blanket impls for any RefLift implementor. Add SendRefApplyFirst and SendRefApplySecond with blanket impls for any SendRefLift implementor. Update RefApplicative and SendRefApplicative to include ApplyFirst/ApplySecond supertraits, matching the non-ref Applicative. Add ref_if_m and ref_unless_m free functions to ref_monad.rs for conditional monadic actions via by-ref bind.
Mark steps 6-11 as done. Reorder remaining steps: rename (12), dispatch unification (13, moved after rename), documentation (14), m_do! integration (15). Fold remaining free function parity items into the dispatch unification step since each gets a dispatched version directly. Update completed changes section.
Skip the Callable<Mode> base trait extraction (GAT associated type bounds unsupported on stable Rust, re-declaring Of creates non-interoperable types). Update rename table: CloneableFn -> CloneFn, SendCloneableFn -> SendCloneFn (independent, SendOf -> Of), Function -> Arrow (new -> arrow). Document rationale for each decision.
Rename CloneableFn to CloneFn, SendCloneableFn to SendCloneFn, and Function to Arrow across the entire codebase. Arrow aligns with Haskell's Arrow type class (arr + Category + first/second). Arrow::new renamed to Arrow::arrow. Free function fn_new removed (profunctor::arrow already provides this). Make SendCloneFn independent (remove CloneFn supertrait). Rename SendOf to Of on SendCloneFn since there is no longer a name conflict with the parent trait. Rename files: function.rs -> arrow.rs, cloneable_fn.rs -> clone_fn.rs, send_cloneable_fn.rs -> send_clone_fn.rs. Fix document_parameters for single-parameter methods. Regenerate compile-fail test stderr files. Update doc links in limitations-and-workarounds.md, std-coverage-checklist.md, and optics-analysis.md.
Break dispatch into three categories: closure-dispatched (bind, lift2, etc. infer mode from closure args), container-dispatched (apply infers from wrapped function type), and not dispatched (pure has no argument to infer from). Single mode parameter for lift2 (both args must match, mixed modes rejected). Document apply dispatch fallback if compiler can't differentiate container types.
Add dispatch traits for bind and lift2 that route to either the by-value (Semimonad::bind, Lift::lift2) or by-ref (RefSemimonad::ref_bind, RefLift::ref_lift2) trait method based on the closure's argument type. Follows the same pattern as FunctorDispatch. Currently pub(crate) to avoid name conflicts with existing free functions. Will be promoted to pub when the old free functions are removed and call sites are updated.
Replace the separate bind (Semimonad) and ref_bind (RefSemimonad) free functions with a unified bind that dispatches based on the closure's argument type: Fn(A) -> Of<B> routes to Semimonad::bind, Fn(&A) -> Of<B> routes to RefSemimonad::ref_bind. Update all call sites from bind::<Brand, _, _> to bind::<Brand, _, _, _> (adding inferred Marker parameter). Update m_do! macro codegen to emit 4 generic arguments. Fix ref_if_m and ref_unless_m to call Brand::ref_bind instead of Brand::bind.
Replace issue table with researched approaches from agent investigation: - Tuple closures: parse sub-arrow strings into impl Fn types - Tuple returns: add Tuple variant to ReturnStructure, detect via syn::Type::Tuple - WithIndex: fix in ast_builder.rs, detect 2-segment associated type paths in HM conversion (one-line fix) - apply_first/second: extend DispatchTraitInfo with associated type resolution from Val impl - traverse FA: confirmed resolved by previous commit
Three HM signature rendering fixes:
1. Tuple closures (bimap, bi_fold_*, bi_traverse, compose_kleisli):
Parse sub-arrow strings into bare fn pointer types and combine in
a tuple parameter. The HM pipeline converts fn(A) -> B to A -> B.
E.g., bimap: ((A -> B, C -> D), Brand A C) -> Brand B D.
2. Tuple return types (partition, partition_map, separate):
Add Tuple(Vec<Vec<String>>) variant to ReturnStructure. Detect
by checking if the trait method return type is syn::Type::Tuple.
Build tuple of <Brand as Kind>::Of<...> qualified paths.
E.g., partition: (A -> bool, Brand A) -> (Brand A, Brand A).
3. WithIndex Brand::Index rendering (map_with_index, fold_*_with_index,
filter_*_with_index, traverse_with_index):
Fix in ast_builder.rs: detect 2-segment path where first segment
is a known generic name and last has no angle brackets. Treat
second segment as associated type variable, returning
HmAst::Variable("Index") instead of HmAst::Variable("Brand").
E.g., map_with_index: ((Index, A) -> B, Brand A) -> Brand B.
The closureless dispatch fallback (for container params with InferableBrand bounds not in container_map) now handles Nested and Tuple return structures in addition to Applied. Fixes separate showing FA instead of Brand E. Traverse still shows Brand B (wrong element type) due to extract_last_of_type_args_clean picking up the output element type instead of the input element type from the Apply! macro.
…m parser
Replace all string-based macro text parsing in dispatch analysis with
calls to get_apply_macro_parameters(), the existing token-stream-based
Apply! parser. This eliminates fragile rfind("Of"), string counting,
and comma splitting on rendered macro text.
Changes:
- Add extract_apply_type_args() helper that parses Type::Macro via
get_apply_macro_parameters and returns type arg names.
- Rewrite extract_return_structure to use classify_return_type() which
dispatches on syn::Type variants (Tuple, Macro, other) and uses the
proper parser for Apply! macros.
- Rewrite classify_arrow_output to parse Apply! brands and args
structurally instead of string matching.
- Rewrite extract_container_params to use extract_apply_type_args
instead of string-based extract_last_of_type_args_clean.
- Remove deprecated string-based functions: extract_last_of_type_args_clean,
extract_non_brand_param, extract_apply_brand, extract_apply_brand_name.
This fixes wither (now produces correct signature) and improves
robustness for all functions. Remaining edge cases (traverse container
element, compose_kleisli arrow output, apply_first/second projection)
are separate issues not caused by string parsing.
Replace output table with current state: 30 functions produce correct signatures after replacing string-based parsing with proper Apply! parser. Document 5 remaining issues with root causes and approaches. Add step 7-8g: regression tests for all 37 function signatures to catch future regressions. Tests will assert exact HM output for every inference wrapper function.
The container_map was using trait param names (e.g., "FTA" for traverse) but the function uses different names (e.g., "FA"). This caused the container_map lookup to fail, falling back to the InferableBrand heuristic which used the wrong element type. Fix: build_container_map() scans the function's impl *Dispatch<...> parameter (or where-clause dispatch bounds for closureless dispatch) to find the function's actual type arg names, then maps them to the element types from dispatch_info.container_params. Fixes traverse showing Brand B instead of Brand A. Also removes all debug eprintln! statements added during investigation.
Update output status to 32/37 correct after container map fix. Document resolved traverse issue (build_container_map from function's dispatch trait type args). Update remaining issues table to 5 functions with refined root causes and approaches. Add resolved-since-last-update note for traverse.
Fix the last 5 inference wrapper functions that produced incorrect Hindley-Milner type signatures: - compose_kleisli, compose_kleisli_flipped, bi_traverse: Sub-arrow outputs showed () instead of Brand B / Brand C. Root cause was a lossy string round-trip in tuple closure extraction. Added SubArrow variant to DispatchArrowParam to preserve structured arrow data. - wilt: Fell through to standalone macro entirely. Added NestedTuple variant to ReturnStructure for nested applications containing tuples of brand applications (M (Brand E, Brand O)). Changed build_applied_type to parse elements as syn::Type instead of syn::Ident, enabling compound types like Result<O, E>. - apply_first, apply_second: Second parameter showed raw FA instead of Brand B. Added associated type extraction from Val impl blocks to resolve <FA as Dispatch>::FB projections. - separate: Input container showed Brand E instead of Brand (Result O E). Added self-type element extraction from Val impl's Apply! self type for closureless dispatch where the trait is implemented on the container itself. Additional improvements: - Element type params collected from all sources, deduplicated, and sorted alphabetically for consistent forall ordering. - Tuple param detection for where-clause dispatch (compose_kleisli_flipped). - Wither now correctly renders closure parameter (A -> M (Option B)).
…7-8g) Plan for insta-based snapshot tests that run document_module_worker on real dispatch module source files and assert on the generated HM signature strings. Covers all 37 inference wrapper functions across 18 test functions.
…d Apply! Two changes to obtain information from direct sources instead of indirect derivation: 1. Type param ordering: replace alphabetical sort with the dispatch trait definition's generic param list (type_param_order field). This preserves the trait author's intended ordering. Affects wither (Brand M A B instead of Brand A B M) and wilt (Brand M A E O instead of Brand A E O M). 2. Self-type element resolution: inner Apply! macros in self_type_elements are now resolved to qualified paths via apply_worker during extraction. Fixes compact showing Brand A instead of Brand (Option A), and join showing Brand A instead of Brand (Brand A). Removes the Apply!/Kind! string filter heuristic that was working around the unresolved macros.
Revised to process the full dispatch.rs module instead of per-file isolation, matching production behavior. Added heuristic replacement tasks (build_container_map positional alignment, brand_param from trait definition, InferableBrand fallback from type_param_order) and edge case test specifications for robustness.
…nfrastructure Heuristic replacements: - build_container_map: replaced "next unmatched multi-letter ident" scanning with direct positional lookup using ContainerParam.position. Introduced ContainerParam struct with name, position, and element_types fields. - find_brand_param: added find_brand_param_from_trait_def that finds the type param with a Kind_* bound in the trait definition (direct source). Used as primary, with Val impl scan as fallback. - InferableBrand fallback: added type_param_order-based fallback between self_type_elements and return structure. Extracts single-letter element types from the trait definition's params. Snapshot test infrastructure: - Added insta dev-dependency to fp-macros. - Created signature_snapshot_tests module with 18 per-file tests. - Tests extract mod inner body, run through document_module_worker, and snapshot the generated HM signatures. - Closure-based dispatch tests produce correct output. Closureless dispatch tests (alt, compact, join, separate, apply_first/second) produce FA instead of Brand A in per-file isolation; this is a test-context issue (production docs are correct). To be investigated.
Heuristic replacements (1a, 1b, 1c) are done. Snapshot infrastructure is implemented with 18 per-file tests. Closure-based dispatch (31 functions) produces correct output. Closureless dispatch (6 functions) shows FA instead of branded types in per-file test isolation; production docs are correct. Documented the module structure finding (each dispatch file has its own #[document_module], not a single parent module) and the remaining investigation needed.
18 per-file snapshot tests covering all 37 inference wrapper function signatures. Each test reads the dispatch submodule source, extracts the mod inner body, runs it through document_module_worker, and asserts the generated HM signatures against insta snapshots. The closureless dispatch issue (FA instead of Brand A) was caused by the explicit:: submodule's functions overwriting inference wrapper signatures in the BTreeMap (both named "alt", "compact", etc.). Fixed by skipping mod explicit during signature collection.
Dispatch analysis edge cases (9 tests): - Brand param in middle of generic param list (not first after lifetime) - Brand param with unusual name (F instead of Brand) - No semantic constraint in Val impl where clause - No Val impl found (orphan dispatch trait) - Extra type params between Brand and Marker - Associated types extracted from Val impl - Container param position indices correct - Type param order preserves trait definition order Signature generation edge cases (5 tests): - Missing Kind hash falls back (no dispatch signature generated) - No #[document_signature] attribute (function skipped) - #[document_signature] without dispatch trait reference (left for standalone) - Simple dispatch produces correct full-pipeline output - Bifunctor two-element container with tuple closure
All three tasks complete: heuristic replacement (1a, 1b, 1c), snapshot regression tests (18 per-file, all 37 signatures), and edge case tests (9 dispatch analysis + 5 signature generation). Documented the mod explicit name collision fix.
Created dispatch/contravariant.rs with ContravariantDispatch trait, Val impl, inference wrapper, and explicit function. This follows the same pattern as all other dispatch modules, enabling automatic HM signature generation for contramap. Only a Val dispatch exists (no Ref variant) because contramap's closure produces elements (Fn(B) -> A), not consumes them, so the &A convention does not apply. Generated signature: forall Brand A B. Contravariant Brand => (B -> A, Brand A) -> Brand B Deleted functions/contravariant.rs (replaced by dispatch module). Updated functions.rs re-exports, explicit re-exports, and call sites in doc examples and property tests.
#[document_signature("forall A B. (A -> B) -> A -> B")] now emits
the provided string directly as the HM signature, bypassing the
generation pipeline. Works in both the standalone macro context and
the document_module dispatch-aware context (where it takes precedence
over auto-generation).
Tests added for both paths:
- Standalone: string emitted, auto-generation still works, empty
string rejected, non-string argument rejected (4 tests).
- document_module: override emits string, override takes precedence
over dispatch-aware generation (2 tests).
Also: removed unused parse_empty_attributes utility.
Updated four documentation files to reflect that inference wrapper functions now live in dispatch modules (not functions/), and that each dispatch module contains the dispatch trait, Val/Ref impls, inference wrapper, and explicit submodule. - dispatch.md: Updated module structure diagram. - architecture.md: Revised "Free Functions" section from three layers to two layers + facade. Documented colocation rationale. - project-structure.md: Updated dispatch and functions module descriptions. - CLAUDE.md: Updated dependency graph (added dispatch layer), key locations (added dispatch/ and analysis/dispatch.rs).
All 5 doc examples in dispatch/contravariant.rs now demonstrate actual contramap usage with ProfunctorSecondAppliedBrand<RcFnBrand, _> and assert on expected output, instead of assert!(true) placeholders.
- Replaced 5 assert!(true) placeholders in dispatch/contravariant.rs with real contramap examples using ProfunctorSecondAppliedBrand. - Added explanation in inference wrapper doc for why the explicit variant is used (profunctor types have ambiguous brands). - Updated #[document_signature] proc macro docs in lib.rs to document the manual override string argument (with usage example and expanded output) and dispatch-aware generation. - Update todo.md
Shebang recipes don't receive just's *args via bash $@. Added
set -- {{args}} to populate bash positional params before the
test guard and cargo invocation.
Changed markdown links like [hkt.md](./hkt.md) to use the document's actual title, e.g., [Higher-Kinded Types](./hkt.md). Updated links in features.md, dispatch.md, limitations-and-workarounds.md, and README.md.
- update-nixpkgs: weekly (Monday) flake.lock update via nix flake update. - update-cargo-deps: weekly (Thursday) Cargo.lock update via cargo update. Both create PRs with auto-merge enabled, gated on CI status checks. Uses GitHub App token to ensure CI triggers on automated PRs. Also enabled repo settings: allow_auto_merge, delete_branch_on_merge, and branch protection on main requiring Formatting, Clippy, Documentation, and Tests status checks.
The stacker dependency pulls in ar_archive_writer which uses this license. The LLVM exception is strictly more permissive than plain Apache-2.0 (relaxes restrictions on derivative works).
Added cargo-deny check as a just recipe and inserted it into the verify pipeline between clippy and doc. This closes the gap where CI ran cargo-deny but local verification did not. Updated CLAUDE.md and CONTRIBUTING.md to reflect the six-step verify pipeline.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Test plan
just verifypasses (fmt, check, clippy, deny, doc, test).