feat: add AOT path parameter support#2209
Closed
TimothyMakkison wants to merge 37 commits into
Closed
Conversation
5 tasks
glennawatson
added a commit
that referenced
this pull request
Jul 13, 2026
- Resolved in favour of the request-building rework: the branch predates it and its path-parameter code is superseded by the inline rework. - Dropped PathFragmentModel (the rework has its own path models). - Kept the updated analyzer rule configuration. Co-Authored-By: Timothy Makkison <timmakkison@gmail.com>
glennawatson
added a commit
that referenced
this pull request
Jul 14, 2026
* feat: added route generation, currently not emitting, don't know why * fix: can run tests without error messages * feat: add exceptions and unmatched guard logic * feat: move `subProperties` into `RefitModel` * feat: partially move route logic into `Parser` * chore: refactor and rename `BuildObjectParamValidationDict` * feat: enable inline generation for route parameters * feat: run tests and accept changes * feat: use route fragment based generation in `Emitter` * fix: correctly emit object property fragment * chore: remove unused code * chore: remove unneeded model * chore: reorder and try to fix some errors * chore: refactor add summaries * chore: add summaries for `GeneratedRequestRunner` * fix: use `ConcurrentDictionary` to fix concurrency issue * fix: constant path test * fix: update snapshot tests * feat: add type parameter array and add @ to generated parameter names * chchore: remove unused property and summary * feat: pass `genericCount` * feat: refactor `AddStandardParameter` * feat: refactor `AppendObjectPropertyFragment` to `AddRouteObjectProperty` * feat: add `TryParseRouteParameter` * fix: remove local `stackalloc` to support C# 7.3 * feat: add `BuildInlineTypeParameterField` * chore: rename `AddPathObjectProperty` and `AddPathParameter` * chore: make some analysers happy * feat: add `ParameterInfoKey` and `ParameterInfoCacheKeyComparer` * feat: change generated code for `AddRouteParameter` and `AddPathObjectProperty` * chore: code cleanup, add comments, minor refactor * feat: add skeleton for framework `GetMethod` * chore: remove debug code * feat: add `genericTypeArgument` emitter * chore: add generic argument, refactor and add documentation * chore: add comments, remove replaces references to `Route` to `Path` * feat: fix missing argument and invert generic emitter * feat!: reflection-free source-generated request building Completes the move to generated, trim- and Native-AOT-clean request building, keeping the reflection path as an opt-in package. - Generate query strings, query-object flattening, dictionary query params, and implicit [Body] inline, so the common method shapes work in generated-only and Native AOT clients (#2185, #2190, #2191, #2192). - Add [QueryName] valueless flags, [Encoded] encoding opt-out, and [QueryConverter] for runtime-only shapes (#2168, #2169, #2002). - Add pluggable return-type adapters via IReturnTypeAdapter (#2165). - Generate generic interface methods inline; support extern aliases (#1101). - Split the reflection request builder into the opt-in Refit.Reflection package; add RF006/RF007 analyzers for methods that still need it. - Stop generated fallback methods leaking IL2026/IL3050 (#2200). - Unroll all-scalar form bodies straight-line; keep generated code at the C# 7.3 baseline and emit modern syntax when the language version allows. - Reduce hot-path allocations and move some hot async members to ValueTask. BREAKING CHANGE: some hot-path async members return ValueTask instead of Task; the reflection request builder is now the opt-in Refit.Reflection package; fully generated interfaces validate on first call, not in RestService.For; query objects flatten by declared type and honor the content serializer property names. See docs/breaking-changes.md. * perf(generator): span-formattable path parameter fast-write - Format an integer path parameter straight into the path buffer with no intermediate string and no escaping (net6+); its invariant rendering is URL-safe, so nothing is escaped. - Format any other ISpanFormattable path value into a stack buffer and escape span-to-string (net9+), skipping the ToString allocation. - Detect ISpanFormattable and the span Uri.EscapeDataString overload once per compilation and thread the result, so the generator emits the best path for the consumer target and nothing extra for older frameworks. Brings over the ISpanFormattable idea from #2209. Co-authored-by: Timothy Makkison <timmakkison@gmail.com> * refactor(generator): make InterfaceGenerationContext a reference type - Change the transient InterfaceGenerationContext from a readonly record struct to a sealed record, so the compilation and symbols it carries are passed by reference, not copied, and it reads as pass-scoped scaffolding. It is never a pipeline value; the emitted models stay the equatable, cache-safe types, so nothing symbol-bound crosses the incremental cache. - Use a concrete INamedTypeSymbol[] for the discovered return-type adapters (context and analyzer state) instead of IReadOnlyList. - Drop the now-redundant in modifier on the context parameters. * perf(generator): cut generator allocations ~17-18% - Drop the discarded RequestParameterModel builds on the Try* parameter false paths; the caller only reads the out value on success. - Replace per-parameter ToDisplayString comparisons with structural matches (cancellation token, Refit attributes, generic enumerable). - Cache extern-alias lookups per pass instead of re-resolving a metadata reference for every type node in QualifyType. - Walk each interface's inherited members once (methods + properties), and resolve IFormattable/ISpanFormattable in a single interface pass. - Back emitter accumulation with a pooled-buffer PooledStringBuilder (ArrayPool) in place of StringBuilder across the emitter. - Append query statements and the request prologue straight into the builder rather than building intermediate interpolated strings. - Cache indentation strings and add no-escape fast paths for C#/XML literal escaping; fuse the parameter-info emission into one pass. Measured on a 60-interface corpus: per-keystroke and cold-build allocations both down ~17-18%. Generated output is byte-identical. * feat(generator): close reflection-fallback gaps and expand coverage - Flatten nullable nested value-type (Nullable<T>) query-object properties inline, unwrapping to the underlying struct and accessing via .Value. - Generate an inline Authorization header for [Authorize] parameters using the compile-time scheme; multiple [Authorize] falls back so it still throws. - Flatten dictionary properties inside a query object inline, expanding entries under the property key. - Generate non-[Encoded] round-trip {**param} catch-all paths of any type inline via GeneratedRequestRunner.RoundTripEscapePath. - Add runtime parity tests (generated output matches the reflection builder) and generator coverage tests across the touched files. * feat(generator): bind path objects and flatten their residual query - Split a dotted {param.Prop} object between the path and the query: matched properties fill the path placeholders and every property left unbound flattens into the query string inline, mirroring how the reflection builder splits a bound object instead of falling back. An unsupported residual property shape still falls the whole parameter back. - Sort path replacements by template position before emitting. The runtime BuildRequestPath overloads slice between consecutive placeholders, so an object binding filling an earlier placeholder than a later parameter threw ArgumentOutOfRange in the parameter-precedence case. - Cover the feature with a live reflection-parity test (generated URI equals the reflection builder) and a PathObjectBindingGenerationTests file spanning inline, residual-inline and unsupported-residual fallback. - Upgrade StyleSharp/PerformanceSharp analyzers to 3.19.0 and satisfy the new rules without suppressions or editorconfig changes: name repeated test literals, seal the top-level Program types, return a cached read-only view from StubHttp.Requests, and scope the SST1472 case to the tested constructor. * test: cover PR-touched files to 100% and narrow coverage exclusions - Add tests across generator, reflection and runtime paths so every production file changed in this PR reaches full line and branch coverage: alias/adapter parsing, query-object flattening, path-object binding, the reflection request/adapter builders, the query-string builder, the System.Text.Json query converter, and request-execution helpers. - Restructure genuinely-unreachable defensive code so it no longer needs a method-level [ExcludeFromCodeCoverage] that hid real, tested logic: drop the path-parameter Locations-null guard, unconditionally treat a second IEnumerable<T> as ambiguous, simplify the first-query-attribute lookup, extract the ExceptionDispatchInfo rethrow into a tiny helper, return the string body after its await-using, and drop the adapter arity guard whose caller already guarantees equal counts. - The only remaining exclusions are a debugger-only display string and the unreachable trailing return of the rethrow helper. * build: disable S4027 to match SonarCloud ruleset - Set S4027 severity to none so the local build agrees with SonarCloud, where it is disabled. - Remove the now-redundant S4027 suppressions on the API exception classes and the DerivedApiException test fixture (SST1462). * build: apply uniform analyzer standards to test code - Reorganize the analyzer config so production and test code share one standard: keep only genuine test-specific LINQ relaxations in src/tests/.editorconfig and drop the blanket SST1432 test exemption. - Correct S4027's rationale to a CA1032 duplicate rather than a SonarCloud match. - Suppress SST1432 on the Http.Client naming fixture, which is passed to UniqueName.ForType<T> as a type argument and so cannot be static. * test: drop the fragile synthetic-net10 span-escape generator test - Remove GeneratesInlinePathAgainstNet10SpanEscapeReferenceSet and its TryCreateNet10Compilation helper. Hand-building a compilation from the net10 reference pack plus the host-target Refit.dll mixed corelibs, which on a net11 host bound the [Get] path argument to an error constant and dropped the path entirely — a deterministic failure, not a product bug (normal generation is correct on net11). - The span-escape probe's positive branch is exercised naturally on the net10.0/net11.0 target test runs, where the host references genuinely expose Uri.EscapeDataString(ReadOnlySpan<char>). * build: disable analyzer rules that duplicate their SST equivalents - Turn off ~80 CA and Sonar (S) diagnostics that are already covered by a canonical StyleSharp (SST) rule, so each concern is enforced by exactly one analyzer rather than reported twice. * perf(generator): span-format query values without an intermediate string Render default-formatted scalar and collection query values straight into the builder as ISpanFormattable, dropping the per-value throwaway string on the source-generated request hot path. Query URIs stay byte-identical. - Add GeneratedQueryStringBuilder.AddFormatted / AddCollectionValueFormatted (net6+): TryFormat into a stack buffer, append URL-unreserved integers verbatim and span-escape other values (net9+), matching Add/AppendPair. - Emit the fast call on the default-formatting branch for span-formattable values, reusing the parser's IsUrlSafeSpanFormattable / IsSpanFormattableEscapable tiers; customized-formatter, TreatAsString, enum and string paths are unchanged. - Extract the enum/converter formatter helpers into a new partial file to keep Emitter.Inline.Query.cs within the file-length limit. - Cover int/long scalars, a formatted double, int Csv/Multi collections and a null nullable int in the reflection-parity live suite. * feat(generator): source-generated multipart request building - Build [Multipart] method content inline instead of falling back to reflection, matching the reflection builder's AddMultipartItem dispatch and part field-name/file-name selection for statically-resolvable types - Support StreamPart/ByteArrayPart/FileInfoPart (and MultipartItem subclasses), Stream, string, FileInfo, byte[], HttpContent, and date/time and Guid parts, plus reference-typed enumerables (one part per element); reproduce custom boundaries and the null-parameter skip - Keep object/serialized parts and [Multipart]+[Body] on the reflection builder (the whole method falls back), so RF006/RF007 stay in lockstep - Add MultipartPartKind/MultipartPartModel plus Parser.Request.Multipart and Emitter.Inline.Multipart partials; register the shared files in both analyzer csprojs - Cover with a reflection-parity live suite and a compliance-project scenario; flip the multipart-falls-back tests to assert inline generation * build: fix the SST reference in the CA1044 dedup comment - CA1044 (write-only properties) is covered by SST1421, not SST2307. * perf(generator): drop per-call query-builder scan and empty attribute dicts - Emit the compile-time query-presence into a new GeneratedQueryStringBuilder ctor overload so the built path is not rescanned for '?' on every call (gated off when a pre-encoded path segment could inject one at runtime). - Emit a shared GeneratedParameterAttributeProvider.Empty singleton for parameters that declare no attributes, instead of a fresh empty dictionary and Lazy per parameter. * fix(tests): update Verify.TUnit version and remove carriage return handling * feat(generator): widen inline request coverage and unroll path builder - Replace the fixed-arity and params BuildRequestPath overloads with a single ReadOnlySpan overload; the generator emits a [...] collection expression on C# 12 (stack-allocated on net8.0+, zero heap for any arity) and an inferred array on older consumers, both binding through the array-to-span conversion. - Bind sealed and value ToString-only path parameters inline through the same UrlParameterFormatter.Format call the reflection builder makes; open, object, and interface types stay on reflection to avoid runtime-type divergence. - Source-generate [QueryUriFormat] methods, re-encoding the whole path and query with the attribute's UriFormat via a helper that mirrors the reflection builder's Uri.GetComponents(PathAndQuery, format) pass. - Render a [Query(Format)] on a complex or collection query-object property as a single whole-value pair inline instead of falling back. - Flatten a dictionary of a sealed complex value type under each entry key (key.Property=value), matching the reflection builder's nested BuildQueryMap. - Match return-type adapters whose wrapper reorders or concretely mixes the adapter's type parameters, mirrored in the reflection resolver so both agree. - Inline a custom HTTP method attribute whose Method getter is a literal new HttpMethod("VERB"); other overrides keep using the reflection builder. Every closed gap is pinned by a generated-vs-reflection URI parity test. * test(generator): pin the HTTP QUERY verb scenario - Add a custom QUERY verb attribute and body-carrying methods to the live query harness, covering the draft-standard HTTP QUERY method. - A generated QUERY request uses the custom verb, carries an explicit [Body], and - since the verb is not yet body-capable - flattens an un-attributed complex parameter into the query, all at parity with the reflection request builder. - Compare the HTTP method in AssertParityAsync so every parity test now pins the verb, not just the URI. * perf(generator): span-format nullable value-type query parameters - A nullable value type (int?/Guid?/DateTime?) query parameter now writes its unwrapped .Value through the span-formattable fast path after the existing null guard, instead of materializing an intermediate string via FormatInvariant. - ComputeSpanFormattableTiers no longer excludes nullables; the path and collection-element fast paths opt out separately, keeping their existing string-formatting path (a nullable path value needs a null branch the fast overload does not model). - Correctness is covered by the existing Paged(int? page) live parity test; a generation test pins that the fast write fires. * feat(generator): source-generate serialized multipart parts - A multipart part whose declared type is a bool, enum, or sealed DTO now serializes inline through settings.ContentSerializer.ToHttpContent(value), matching the reflection builder's AddSerializedMultipartItem serializer fallback; the declared type drives ToHttpContent<T>, so a sealed/value part's serialized form is byte-identical. - An open, interface, or object-typed part keeps falling back (runtime type decides the serialized shape); a non-sealed DTO part is a declared-type divergence handled separately. - Live parity test serializes a bool and a sealed DTO part byte-for-byte. * feat(generator): inline concrete-class path parameters - A path parameter of any concrete class or struct (not only sealed/value) now binds inline. The reflection builder renders a route value with a virtual ToString call that dispatches to the runtime type; the generated ToString does the same, so a runtime subtype renders identically - the only divergence is the vanishing case of a subtype whose IFormattable.ToString(null, invariant) differs from its parameterless ToString(). - object, interface, and open generic path parameters keep falling back: they have no usable declared shape and must be inspected at runtime. * feat(generator): inline collection and array path parameters - A collection or array path parameter (List<int>, byte[], int[]) now binds inline: the reflection builder renders it with the URL parameter formatter, which for a non-IFormattable value is value.ToString() (the System.Collections text), and the generated ToString reproduces that identically. * feat(generator): inline concrete (non-sealed) dict values and multipart parts - A dictionary value or multipart part of any concrete class or struct (not only sealed/value) now flattens/serializes inline by its declared type, the same accepted declared-type divergence a non-sealed query object already carries (matching the System.Text.Json source generator). - A value that is not a runtime subtype renders identically to the reflection builder; a polymorphic subtype renders by its declared type. object, interface, and open generic types keep falling back - they have no usable declared shape. - Live parity tests flatten a non-sealed dict value and serialize a non-sealed multipart part, both byte-for-byte vs reflection. * chore: untrack agent worktree and ignore .claude - Remove the .claude/worktrees gitlink accidentally staged into the previous commit; the directory is a transient local agent worktree, not project content. - Add .claude/ to .gitignore. * feat(generator): inline IObservable return shape as cold observable - Generate IObservable<T> methods inline through a new GeneratedRequestRunner.SendObservable helper instead of the reflection request builder, closing the last non-Task return-shape fallback where the shape is statically known. - The helper returns a ReactiveUI.Primitives cold FromAsyncSignal that rebuilds and re-sends the request per subscription, linking the method and subscription cancellation tokens. - Wrap request construction in a per-subscription local function so a second subscription never reuses a disposed request. - Mirror the async send's isApiResponse/dispose/buffer flags, so IObservable<ApiResponse<T>> and IObservable<IApiResponse<T>> reach parity with the async path. - Move the return-shape inline/fallback switch tests into their own file and add a live cold-observable parity test. * feat(generator): inline no-leading-slash relative paths - Stop forcing a reflection fallback for method paths without a leading slash; they now build inline like any other path. - Under RFC 3986 resolution the HttpClient merges the base address with the relative path; under legacy resolution the generated request rejects it with the same ArgumentException the reflection builder raises, so behaviour matches in both modes. - Add a live test covering inline generation, RFC 3986 parity, and the legacy rejection. * feat: inline and reflect multi-level dotted path params - Bind nested {a.b.c} route placeholders through a property chain in both the source-generated and reflection request builders, where only a single {a.b} level bound before. - Walk the chain with null-conditional access so a null intermediate renders an empty segment instead of throwing, matching the reflection builder; the top-level property is marked consumed so residual object properties still flatten into the query correctly. - Extend the public RestMethodParameterProperty with the navigation chain (single-level bindings remain a one-element chain). * fix: restore reflection parity for inlined multipart and slashless routes - Wrap a generated multipart part's serialization failure in the same descriptive ArgumentException the reflection request builder raises, via a new GeneratedRequestRunner.SerializeMultipartPart helper. - Update the leading-slash-less route tests: generated request building validates the slash when the request is built, so the throw now surfaces on the first call, not from RestService.For. Document the timing in the README. - Switch the generated-factory test interface off the now-inline IObservable shape onto a dynamic query-map parameter so it still exercises the reflection factory registration path. * perf(generator): cache custom-verb HttpMethod in a static field - A custom HTTP verb previously allocated a new HttpMethod on every call through inline request building. Emit it once into a static field and reference it, matching the reflection builder, which reads the verb from the attribute once per method. - A known verb still resolves to the framework-cached singleton, so only custom-verb methods gain the field. * perf(generator): pre-escape constant query keys at generation time - A scalar query parameter's key is a compile-time constant, so escape it once during generation and append it verbatim at request time instead of escaping it on every call. - Add GeneratedQueryStringBuilder.AddPreEscapedKey / AddFormattedPreEscapedKey overloads that take an already-escaped key; the escaping-key overloads remain for runtime keys (dictionary entries, [QueryName] values). - Uri.EscapeDataString follows RFC 3986 consistently across target frameworks, so the generation-time escape matches the reflection builder's runtime escape (parity tests confirm). * perf: percent-encode span-formatted values in place, extend to net8 - Escape a span-formatted URL value directly into the builder with an in-place RFC 3986 encoder (StringHelpers.AppendUriDataEscaped) shared by path and query building, instead of allocating an escaped string via Uri.EscapeDataString(span). - Enable the span-escape tier wherever ISpanFormattable exists (net8+), not just where Uri.EscapeDataString(ReadOnlySpan) does (net9+), so net8 formats-and-escapes escapable values without the intermediate string. - The formatted spans are invariant ASCII; a non-ASCII character defers to Uri.EscapeDataString for exact UTF-8 parity. * fix: use the net9 span overload of Uri.EscapeDataString in the escaper fallback * test(benchmarks): cover the verb-cache and span-escape query paths - Add TimestampQuery / TimestampPath rows (a DateTimeOffset whose invariant form needs escaping) so the span-escape encoder is measured against the reflection builder for both query and path values. - Add a custom-verb row so the cached HttpMethod path is exercised. * test(benchmarks): use explicit new HttpMethod so the custom verb inlines * test(analyzers): treat IObservable as inline-supported, not a fallback - IObservable methods now build inline, so RF006 no longer fires for them; move the shape from the reflection-fallback analyzer cases to the inline-supported cases, matching the generator's fallback contract. * refactor: extract attribute flattening to drop the SST1443 suppression - Move the nested attribute-array flatten out of the AllAttributesCache getter into a static FlattenAttributes helper that copies each array with Array.Copy, removing the nested loop that needed the suppression. * build: move hot-path LINQ analyzer rules to PerformanceSharp - Replace the StyleSharp SST2229/2230/2233 and stylesharp.* hot-path LINQ configuration with the PerformanceSharp PSH1100/1101/1102 and performancesharp.* equivalents in the root and tests .editorconfig. - Point the S103/S3880 duplicate-rule notes at their new owners and keep the 200-char line limit the repo enforces. * refactor: update severity levels for various diagnostics in .editorconfig * test: cover source-generated request building to 100% - Add runtime, generator, and reflection tests bringing every file touched by the reflection-free request-building feature to 100% line coverage. - Remove dead code surfaced by the coverage push: inline FindHttpMethodProperty so its unreachable return null folds into the covered fallback, drop the dead PropertyDeclarationSyntax getter arm, and delete the redundant adapter bounds guard (the source-generated binder indexes the same slots without one). - Bump StyleSharp/PerformanceSharp to 3.21.2, SonarAnalyzer.CSharp to 10.29.0.143774, TUnit to 1.59.0, Verify.TUnit to 31.24.1, and Serilog to 4.4.0; the analyzer update clears the SonarCloud SST2015/SST1462 build errors. * feat: Add support for parsing query objects and generating request bodies - Implemented `Parser.Request.Query.Objects` to flatten complex query values into query pairs for Refit stubs. - Introduced `GeneratedRequestRunner.BodyContent` for serializing request bodies in various formats (JSON, JSON Lines, URL-encoded). - Added `GeneratedRequestRunner.Sending` to handle sending requests and processing responses, including support for observables and streaming responses. * fix: update SharpAnalyzersVersion to 3.21.5 * refactor: bring remaining projects into analyzer compliance - Split oversized files into behavior-named partial classes and extract long methods (SST1522/SST1523) across Refit.Reflection, Refit.Testing, and the test and benchmark projects. - Fix mechanical findings: buried ++/-- (SST2015), numeric-suffix casing (SST2244), redundant interface public (SST1491), replaceable collections (SST2305), per-call serializer options (PSH1416), and assorted others. - Suppress the few genuinely-unfixable findings with justifications: CS0501- mandated abstract on an explicit-interface re-abstraction, options capturing a per-call resolver, the interface-dispatch and sync-stream test surfaces, and the BCL-mirror [Flags] enum polyfill. - Behavior unchanged; the full solution builds analyzer-clean and all tests pass. * refactor: update SharpAnalyzersVersion to 3.21.13 and apply static lambdas in various files * test: cover reachable branches across the touched surface - Add branch-covering tests across the generator, runtime, reflection, testing, analyzer and code-fix files, closing 74 of the 125 partial/uncovered branches flagged on the touched files. - Drive the real generated/parsed and reflection request-building surface for each untested condition outcome (attribute combos, param/return shapes, dotted paths, query objects, dictionaries, multipart parts, custom verbs, adapters, RFC-3986 escaping, cancellation linking, and error/fallback paths). - The remaining 51 branches are provably unreachable (compiler-generated string jump tables, dead null-conditionals on never-null operands, exhaustive switch defaults, async-iterator dispose-mode IL edges, contract-guaranteed non-null). * refactor: close the uncoverable branch residual to reach 100% per file - Remove provably-dead branches surfaced by the coverage push: drop `?.`/`??` guards on operands the surrounding invariant proves non-null (Roslyn symbol namespaces/containers, JsonSerializerOptions.GetTypeInfo, a String JSON element's GetString, a CanRead property's getter, the resolved adapter type), and dead ternary/switch arms that cannot be reached. - Isolate the structurally-uncoverable branches into tiny private helpers marked [ExcludeFromCodeCoverage] (compiler string-switch jump tables, async-iterator dispose-mode IL edges, BCL-nullable defensive guards, analyzer-blocked arms), keeping the excluded surface minimal and the surrounding coverable logic intact. - Consolidate the duplicated known-verb string switch into one shared Parser.MapKnownHttpVerb, and the linked-cancellation-token pattern into one GeneratedRequestRunner.ResolveRequestCancellationToken. - Behavior unchanged: generator snapshots byte-identical, all tests pass, and every touched file now reports 100% branch coverage. * build: adopt SST2307 in place of S4018 for uninferable generics - Bump StyleSharp/PerformanceSharp to 3.22.0, which adds SST2307 (a generic method whose type parameter cannot be inferred from its parameters, so every caller must name it). - Disable Sonar S4018 as a duplicate of SST2307 and enable SST2307 as error, matching the repo's swap convention for Sonar/Sharp overlaps. - Swap every product and test [SuppressMessage] from S4018 to SST2307. * build: clear suppressions targeting analyzer-disabled rules - Bump StyleSharp/PerformanceSharp to 3.23.1. - Swap S1541 -> SST1442 and S2360 -> SST2309, the Sonar rules now disabled as duplicates of the Sharp equivalents that fire on the same code. - Remove suppressions of rules set to none with no firing equivalent (CA1031, CA1040, CA1044, CA1835, CA2208, IDE0079, IDE1006, S100, S1118, S2326); delete the now-empty GlobalSuppressions.cs and the usings that orphaned. - Swap CA2227 -> SST2305 on the settable-collection test model where the Sharp rule does fire once the CA suppression is gone. - Clears the SonarScanner build, where disabled Sonar rules made those suppressions dead and tripped SST1462. * build: finish Sonar->Sharp rule migration on analyzers 3.25.2 - Bump StyleSharp/PerformanceSharp to 3.25.2. - Swap the remaining Sonar S-rules (S1133, S2339, S2342, S2930, S3903, S4022, S4070, S6566, S6966) to their Sharp/CA/PSH equivalents, so the SonarScanner build no longer trips SST1462 on suppressions of the S-rules it strips. - Repair escaped-quote corruption in the S2930/S6566 swaps, whose rule titles contain \" and broke the substitution. - Drop now-dead suppressions (S4070, RCS1157, CA1849) and an orphaned using. - Disable SST2314: ObsoleteAttribute.DiagnosticId is unavailable on the net4x targets. - Relax SST2016 and SST2410 in the test tree for DateTime test models and short-lived test disposables. --------- Co-authored-by: Timothy Makkison <timmakkison@gmail.com>
Contributor
|
Hey Tim - as discussed on Slack, closing this one out. The couple of ideas we grabbed from here - the Cheers! |
Collaborator
Author
|
Thanks, excited to check out the query support 👍 |
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.
Follow on from #2182
Hey, here's my most recent take on adding path parameter support, don't know which one you prefer. I reckon I could modify Main to support object parameters and
**paramtypes, and possibly query params if needed, this may have some issues, I can elaborate as needed. I haven't bothered to rebase this PR onto main yet as I'm not sure if it's still wanted or what issues need to be resolved.I'd be curious to see what the performance difference is between this and the current approach, my benchmarks suggest this uses a little less memory but might be slower due to dictionary lookups, plus I don't trust my laptop to do microbenchmarks during a heatwave. The advantage of adding each parameter individually is that we can prevent boxing of value types and potentially use
ISpanFormattablewhenDefaultUrlFormatteris used for allocation free writes, ie an integer parameter with 2 digits will likely save 24+32 bytes with this approach, not a massive saving.ValueStringBuilderHow to handle the use of
ValueStringBuilderEmit
Emit
ValueStringBuilderas a inner class ofGenerated, would also have to emitAddPathParameterandAddPathObjectPropertyas they need access to it. This would make testing function that rely on the emittedValueStringBuildervery tricky.Expose
Make
ValueStringBuilderpublic, this means that people who useRefitwill have access to it. Not a great idea IMOInner class
Make
ValueStringBuilderan inner class ofGenerated, this way all emitted code can still reference it. We would have to makeGeneratedRequestRunneran inner class ofGeneratedso it can seeValueStringBuilder, this way users can't accessValueStringBuilderbut we can still write tests.Reflection
My approach to reflection is continued from #1965 where I've tried to make
ICustomAttributeProvideridentical to the runtime generated version. As I imagine some people internally casted the types back intoParameterInfoIf my understanding of AOT is correct, my current approach should safely access all reflection data, even if generics are used.
I did experiment with a version which didn't accept generic arrays and just used the generic argument count, but wouldn't close the method to correct generic type.
I could use #2174 approach, as you seem to be okay with this. This will have an edge case where it will break in the rare cases where a parameter has a generic attribute, however this can be fixed.
Failing tests
How to handle the failing query tests, most of the tests are failing because they expect query values alongside object path parameters.
Generated code: