Skip to content

feat: generate request construction to avoid reflection pipeline#2150

Open
glennawatson wants to merge 5 commits into
mainfrom
refit-generator-v2
Open

feat: generate request construction to avoid reflection pipeline#2150
glennawatson wants to merge 5 commits into
mainfrom
refit-generator-v2

Conversation

@glennawatson

@glennawatson glennawatson commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Feature

What is the new behavior?

This PR adds the generation 2 request-building path for Refit source-generated clients. When RefitGeneratedRequestBuilding is enabled, eligible generated methods no longer route through the reflective runtime request-builder path. Instead, the generator emits the request construction directly into the generated client method:

  • creates HttpRequestMessage instances in generated code
  • emits static method headers, dynamic [Header] parameters, and [HeaderCollection] handling
  • emits [Body] serialization setup and buffering/streaming behavior
  • emits [Property] parameters and interface property values into request options/properties
  • emits cancellation token plumbing, API response handling, void response handling, and response disposal behavior
  • dispatches through the new GeneratedRequestRunner runtime helper instead of building an invocation delegate through BuildRestResultFuncForMethod

The opt-in keeps the existing reflective path as the default and preserves fallback behavior for request shapes that are not safe to generate inline yet. The tests explicitly assert both sides: switch-off output still uses the reflective builder, while switch-on output avoids it for supported methods.

The generator itself is reworked around the new request model used by this path:

  • introduces request-specific parser and model types for request parameters, headers, body buffering, interface properties, and method request generation
  • keeps Roslyn 4.8 and Roslyn 5 generator packages using the same shared generator implementation
  • replaces the old shared-project/projitems wiring with SDK-style globbing for the shared generator sources
  • gives the incremental generator combine stages named helper records instead of opaque nested tuple access
  • updates generated output emission to share helper code and produce the new generated request-building call sites

The runtime receives the support surface needed by the generated code:

  • adds GeneratedRequestRunner as the shared runtime implementation for generated request sending, body content creation, header application, configured request options, and typed request properties
  • exposes RefitGeneratedRequestBuilding as a compiler-visible MSBuild property with a default of false
  • updates public API baselines for the new generated-runner surface
  • switches cache key hashing to HashCode and adds framework polyfills for HashCode, Index, and Range
  • keeps old target framework support compiling by aligning generator/runtime polyfills with compiler-required members such as Index.GetOffset

The test layout is also updated:

  • moves source-generation tests out of Refit.Tests into Refit.GeneratorTests
  • adds generated request-building coverage for headers, header collections, body serialization metadata, typed properties, interface properties, inherited properties, and fallback behavior
  • adds live compilation coverage that emits, loads, instantiates, and invokes a generated client using the inline request path
  • updates the generator fixture to compile generated output against the trusted platform assembly closure so CI hosts include type-forwarded framework assemblies
  • refreshes generated source snapshots for the new generator shape

What is the current behavior?

Generated Refit clients currently call into BuildRestResultFuncForMethod and pass an object[] argument array to the runtime request builder. That path depends on runtime method metadata lookup and request-building infrastructure even when the generator has enough information to emit the request construction directly.

The source generator implementation is also still wired through the older shared-project/projitems setup, and source-generation tests are mixed into the main runtime test project.

What might this PR break?

The generated request-building path is opt-in and defaults to off, so existing generated clients should continue using the current reflective request-builder path unless a consumer enables RefitGeneratedRequestBuilding.

The PR does add a new public-but-editor-hidden runtime helper surface for generated code and updates public API baselines accordingly. Generated source snapshots change because the generator implementation and emitted helper wiring changed.

Checklist

  • I have read the Contribute guide
  • Tests have been added or updated (for bug fixes / features)
  • Docs have been added or updated (for bug fixes / features)
  • Changes target the main branch
  • PR title follows Conventional Commits

Additional information

Validation run locally:

  • dotnet build src/Refit.slnx -c Release -v:minimal --no-restore
  • dotnet build src/tests/Refit.GeneratorTests/Refit.GeneratorTests.csproj -c Release -f net8.0 -v:minimal --no-restore
  • src/tests/Refit.GeneratorTests/bin/Release/net8.0/Refit.GeneratorTests
  • src/tests/Refit.Tests/bin/Release/net8.0/Refit.Tests

@glennawatson glennawatson changed the title Add generated request builder pipeline feature: Add generated request builder pipeline Jun 19, 2026
@glennawatson glennawatson changed the title feature: Add generated request builder pipeline feat: add generated request builder pipeline Jun 19, 2026
@glennawatson glennawatson changed the title feat: add generated request builder pipeline feat: generate request construction to avoid reflection pipeline Jun 19, 2026
@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.73272% with 342 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.34%. Comparing base (c7c14b4) to head (61e529c).

Files with missing lines Patch % Lines
src/Refit/GeneratedRequestRunner.cs 30.86% 147 Missing and 21 partials ⚠️
...rc/InterfaceStubGenerator.Shared/Parser.Request.cs 77.63% 40 Missing and 32 partials ⚠️
src/InterfaceStubGenerator.Shared/Emitter.cs 83.80% 43 Missing and 20 partials ⚠️
src/InterfaceStubGenerator.Shared/Parser.cs 90.37% 8 Missing and 5 partials ⚠️
src/Refit/ValueStringBuilder.cs 30.00% 7 Missing ⚠️
...rfaceStubGenerator.Shared/ITypeSymbolExtensions.cs 54.54% 2 Missing and 3 partials ⚠️
...aceStubGenerator.Shared/ImmutableEquatableArray.cs 88.00% 1 Missing and 2 partials ⚠️
...rfaceStubGenerator.Shared/Models/WellKnownTypes.cs 0.00% 3 Missing ⚠️
src/Refit/Buffers/PooledBufferWriter.Stream.cs 0.00% 2 Missing ⚠️
...Generator.Shared/ImmutableEquatableArrayFactory.cs 85.71% 0 Missing and 1 partial ⚠️
... and 5 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2150      +/-   ##
==========================================
- Coverage   78.17%   76.34%   -1.84%     
==========================================
  Files          97      103       +6     
  Lines        3821     4806     +985     
  Branches      715      887     +172     
==========================================
+ Hits         2987     3669     +682     
- Misses        690      915     +225     
- Partials      144      222      +78     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…t targets

- Removed `refit.targets` as it is no longer needed with updated source generators.
- Added new generator options for disabling source generation and enabling request building.
- Updated tests and snapshots to reflect new default behaviors with generated request construction.
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant