Skip to content

feat: generate request construction to avoid reflection pipeline#2150

Merged
glennawatson merged 13 commits into
mainfrom
refit-generator-v2
Jun 20, 2026
Merged

feat: generate request construction to avoid reflection pipeline#2150
glennawatson merged 13 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 97.56824% with 49 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.49%. Comparing base (c7c14b4) to head (2bcfc7d).

Files with missing lines Patch % Lines
...rc/InterfaceStubGenerator.Shared/Parser.Request.cs 96.61% 0 Missing and 9 partials ⚠️
...rc/InterfaceStubGenerator.Shared/Parser.Helpers.cs 75.00% 3 Missing and 4 partials ⚠️
...it/RequestBuilderImplementation.QueryAndHeaders.cs 90.76% 3 Missing and 3 partials ⚠️
...stBuilderImplementation.QueryAndHeaders.Helpers.cs 89.74% 2 Missing and 2 partials ⚠️
src/InterfaceStubGenerator.Shared/Emitter.cs 99.04% 0 Missing and 3 partials ⚠️
...faceStubGenerator.Shared/Parser.Request.Helpers.cs 96.20% 1 Missing and 2 partials ⚠️
...c/Refit.HttpClientFactory/HttpClientFactoryCore.cs 83.33% 1 Missing and 2 partials ⚠️
src/Refit/RequestBuilderImplementation.cs 93.47% 1 Missing and 2 partials ⚠️
...aceStubGenerator.Shared/ImmutableEquatableArray.cs 93.33% 1 Missing and 1 partial ⚠️
src/InterfaceStubGenerator.Shared/Parser.cs 98.26% 0 Missing and 2 partials ⚠️
... and 6 more
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #2150       +/-   ##
===========================================
+ Coverage   78.17%   97.49%   +19.32%     
===========================================
  Files          97      105        +8     
  Lines        3821     4711      +890     
  Branches      715      845      +130     
===========================================
+ Hits         2987     4593     +1606     
+ Misses        690       31      -659     
+ Partials      144       87       -57     

☔ 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.
- Added generated interface stub snapshots for various test scenarios, including `IGitHubApiDisposable`, `INestedGitHubApi`, and `IServiceWithoutNamespace`.
- Ensures proper test coverage of generated output scenarios.
… support

- Improved `PackageDescription` and `PackageTags` for all projects to better reflect functionality and compatibility.
- Updated README to align with modern .NET platforms and features (`HttpClientFactory`, trimming, AOT).
- Added repository metadata and centralized version management settings.
…rocessing

- Introduced `RequestExecutionHelpers` to encapsulate shared logic for request execution and response handling.
- Added a new `RequestExecutionOptions` struct to configure request processing behavior.
-
- Added new test cases for `ApiException`, `ValidationApiException`, `ApiResponse<T>`, and `PooledBufferWriter` classes.
- Removed legacy configuration files: `filelist.txt` and `signclient.json`.
…pdate stream exception handling

- Replaced repetitive `ArgumentNullException` checks with `ArgumentExceptionHelper.ThrowIfNull` for improved code clarity.
- Updated stream exception handling by removing unnecessary catches for `OperationCanceledException`.
- Annotated certain methods with `[ExcludeFromCodeCoverage]` to improve test coverage tracking.
- Removed `AllowUnsafeBlocks` from project files for safer code compliance.
- Added new and modified tests to verify behavior consistency, including enhanced coverage for API response and exception scenarios.
- Introduced `ArgumentExceptionHelper` and `ArgumentOutOfRangeExceptionHelper` classes to add polyfills for argument validation on older target frameworks.
- Included `CallerArgumentExpressionAttribute` and `NotNullAttribute` polyfills for improved compatibility with legacy frameworks.
- Added unit tests to validate `BodyAttribute`, `AttachmentNameAttribute`, and `DefaultFormUrlEncodedParameterFormatter` behaviors and scenarios.
…rocessing

- Introduced `Emitter.Helpers.cs`, `Parser.Helpers.cs`, and `Parser.Request.Helpers.cs` to modularize and centralize helper functions for generated stubs and request handling logic.
- Moved query and header-handling logic to `RequestBuilderImplementation.QueryAndHeaders.Helpers.cs` for improved organization and maintainability.
- Refactored existing logic to leverage shared helpers, reducing redundancy and increasing code clarity.
@sonarqubecloud

Copy link
Copy Markdown

@glennawatson glennawatson merged commit e28a384 into main Jun 20, 2026
13 checks passed
@glennawatson glennawatson deleted the refit-generator-v2 branch June 20, 2026 12:09
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.

2 participants