Skip to content

Fix generic array binding inside tuple data-source parameters - #7541

Merged
thomhurst merged 3 commits into
mainfrom
issue-7539-tuple-array
Nov 4, 2026
Merged

thomhurst merged 3 commits into
mainfrom
issue-7539-tuple-array

Conversation

@thomhurst

@thomhurst thomhurst commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner

Method data-source rows such as (Fixture, CancellationToken, int[]) incorrectly receive TUnit0001 when the test accepts (Fixture, CancellationToken, T[]). Traverse tuple and array shapes to accept compatible open elements, checking array rank and SZ-array shape along the way.

Roslyn still decides concrete conversions. Tuple fields inside value-type arrays retain identity requirements, and uncertain nested generic bindings remain accepted without a bound-inference engine. Generic tuples without arrays keep their existing behavior; generic constraint work remains separate in #7520.

Validation: 212 analyzer tests passed on net10.0 with SDK 10.0.403: 16 focused tuple/array cases (including imported T[*] metadata), 123 MethodDataSourceAnalyzerTests, 67 ReferenceTupleMethodDataSourceTests, 3 EnumerableMethodDataTupleTypeTests, 2 TupleParameterMismatchTests, and 1 GenericMethodSourceDataTests. Existing NU1902 restore warnings were recorded on #7412.

Closes #7539

Summary by CodeRabbit

  • Bug Fixes
    • Improved analyzer checks for generic tuple and array method data sources, including nested arrays, tuple shapes, and generic element types.
    • Rank-one non-vector arrays are now correctly distinguished from vectors when checking argument compatibility.
    • Valid compatible arguments are accepted, while incompatible argument types continue to produce diagnostics.

@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b9b3f5d6-6dfb-40fa-b3e9-be05c94f70bb
Commits

Reviewing files that changed from the base of the PR and between 53caaf4 and 7d5938d.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review

No actionable comments were generated in the recent review.

Walkthrough

The analyzer now checks eligible tuple and array shapes when it evaluates generic method data-source arguments. New tests cover non-SZ arrays and tuple-array combinations, including cases that expect a wrong-argument-type diagnostic.

Changes: Tuple and open-array matching

Layer / File(s) Summary
Open-array shape matching
src/TUnit.Analyzers/Extensions/CompilationExtensions.cs
HasImplicitConversionOrGenericParameter routes eligible pairs to shape matching. The matcher checks array rank and SZ-array status, array element identity, tuple arity, and tuple elements. It accepts uncertain open generic shapes without validating nested inference, constraints, or shared bounds.
Tuple-array binding tests
tests/TUnit.Analyzers.Tests/GenericTupleArrayMethodDataSourceTests.cs
Tests check rank-one non-SZ arrays and tuple-array binding cases. The cases cover nested shapes, generic types, variance, dynamic, and shared-bound inference, with diagnostic expectations.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Possibly related issues

  • Accept compatible generic array elements inside tuple data-source parameters — This PR adds the requested tuple and open-array shape traversal and covers compatible and incompatible cases.
  • Analyzer and runtime conversion parity for tuple elements — The PR uses Roslyn conversions for concrete nested tuple-array cases, but does not add a shared analyzer/runtime parity matrix.
  • Structural fix for generic tuple-array binding — The PR conservatively accepts uncertain shared generic bounds and does not add bounded inference.

Possibly related PRs

  • thomhurst/TUnit#7517 — Both PRs change open-array and tuple shape matching in CompilationExtensions.cs to address compatible generic array elements in tuple data rows.
Merge Risk: Minimal · previous review 53caa

No concrete issue currently blocks merging; complete the normal CI checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title "Fix generic array binding inside tuple data-source parameters" matches the main change. The raw summary and objectives show the PR routes tuple and array shape matching through HasImplicitC…
Linked Issues check Passed Directly linked issue #7539 is the only active target. #7520 is referenced only as separate scope, so its constraint work is not required here. Coding requirements and evidence at head 53caaf4:

1. A…
Out of Scope Changes check Passed The whole-PR diff against merge base 10a0af7 changes two files. src/TUnit.Analyzers/Extensions/CompilationExtensions.cs is the file that #7539 assigns to this work. `tests/TUnit.Analyzers.Tests/Gen…
Full details: Title check

Explanation

The title "Fix generic array binding inside tuple data-source parameters" matches the main change. The raw summary and objectives show the PR routes tuple and array shape matching through HasImplicitConversionOrGenericParameter, so that compatible rows such as (Fixture, CancellationToken, int[]) against (Fixture, CancellationToken, T[]) no longer raise TUnit0001. The title is concise, specific, and free of noise.

Full details: Linked Issues check

Explanation

Directly linked issue #7539 is the only active target. #7520 is referenced only as separate scope, so its constraint work is not required here. Coding requirements and evidence at head 53caaf4:

  1. Accept (Fixture, CancellationToken, int[]) against (Fixture, CancellationToken, T[]): HasImplicitConversionOrGenericParameter routes tuples that contain an array and a type parameter to MatchesOpenArrayShape. The tuple walk accepts the open element T[] through the array branch. The int element is accepted through the open type-parameter branch. Covered by TupleArrayElements case int[] vs T[] (no diagnostic) and the tuple-level cases.
  2. Nested tuples, jagged arrays, rank and SZ-array mismatches: covered by (int, (int, int[][])) vs (T, (T, T[][])), int[][] vs T[][], int[,] vs T[,], int[] vs T[,] (diagnostic), and RankOneNonSzArrayDoesNotBindToOpenVector, which uses imported T[*] metadata. The code checks Rank and IsSZArray before recursing.
  3. Roslyn conversions for decidable concrete cases: concrete elements use HasImplicitConversionOrGenericParameter or ClassifyConversion. Value-type array elements require IsIdentity. Covered by (int, string)[] vs (T, object)[] (diagnostic), (int, string[]) vs (T, int[]) (diagnostic), and int[] vs string[] (diagnostic).
  4. Conservative acceptance of uncertain open generic compatibility: nested generic named types fall through to return true. Covered by List<int>[] vs IEnumerable<T>[], IConsumer<string, object>[] vs IConsumer<T, string>[], and (int, string[]) vs (T, T[]).
  5. Existing valid variance and identity behavior: (int, string[]) vs (T, object[]) is accepted through reference-array covariance. dynamic/object identity is covered in both directions.
  6. Generic tuples without arrays are not routed (ContainsArray is false), so their existing behavior is unchanged.

Limitation: I did not run the test suite. Test expectations were checked by tracing them against the added logic. The branch issue-7489-tuple-array was not inspected. Its reuse is a process instruction, and the traversal it describes is present at head.

Full details: Out of Scope Changes check

Explanation

The whole-PR diff against merge base 10a0af7 changes two files. src/TUnit.Analyzers/Extensions/CompilationExtensions.cs is the file that #7539 assigns to this work. tests/TUnit.Analyzers.Tests/GenericTupleArrayMethodDataSourceTests.cs contains the focused tuple-array analyzer tests that #7539 requests. The new helpers (ContainsArray, MatchesOpenArrayShape) serve the tuple/array traversal. The added comments refer to #7520 and document the deliberate exclusion of constraint and shared-bound inference. No unrelated production code, configuration, or documentation changes appear in the diff.

Full details: Docstring Coverage

Explanation

Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 4 functions across 2 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

A rabbit checks each tuple row,
Through nested arrays, shapes can show.
Rank and bounds are tested with care,
A wrong-type warning marks mismatch there.
Then carrots hop into the burrow.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: fda05f40-51ef-4099-8c7f-52a47fb71397
Commits

Reviewing files that changed from the base of the PR and between 10a0af7 and 53caaf4.

Files selected for processing: 2
  • src/TUnit.Analyzers/Extensions/CompilationExtensions.cs
  • tests/TUnit.Analyzers.Tests/GenericTupleArrayMethodDataSourceTests.cs

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 1 remain after this review.

@thomhurst

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-04T04:25:10.459760Z 7d5938d New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Review of #7541

I found no blocking issues. The change is scoped to the analyzer, and the tests cover the edge cases that matter.

What I like

  • The tuple and array traversal checks rank and IsSZArray. The metadata-imported T[*] test is a good way to cover a case C# cannot spell.
  • Requiring identity for tuple fields inside value-type arrays matches C# array invariance. The (int, int)[] → (T, int)[] rows cover this.
  • Uncertain nested generic bindings stay permissive, so the change cannot add new false-positive TUnit0001 diagnostics. Roslyn still makes the final call.

Non-blocking notes

  1. requiresIdentity is set once an array element is reached and is then passed down unchanged. That is right for value-type elements, but it also applies to reference-type elements such as string[][] → T[][]. Array covariance would allow those. The fallthrough return true for anything containing a type parameter hides this today. If someone later tightens that fallthrough, they could produce false positives. Setting requiresIdentity only when the element type is a value type would be more precise. A comment or a test row on this would also help.
  2. ContainsArray and ContainsTypeParameter are called on every comparison before the shape matching starts. They walk the type each time. This is probably fine for analyzer paths, but a single combined walk would be cheaper.
  3. The special case in HasImplicitConversionOrGenericParameter routes only types that contain both an array and a type parameter. That is the right narrow scope, but a short comment pointing at Analyzer misses generic constraint violations inside tuple and array parameters #7520 for the constraint and inference follow-up would keep the intent clear.

Looks good to merge once CI is green.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a4fde8161

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@greptile-apps

greptile-apps Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issues were found.

Summary

Fixes generic array matching inside tuple data-source parameters.

  • Generic method data-source rows match tuple and array shapes.

The earlier scalar-to-tuple finding is fixed: tuple targets now reject non-tuple sources after checking conversions. That thread has no supplied comment number.

thomhurst explicitly leaves uncertain nested generic bindings accepted and defers inference and constraint work to #7520.

Reviews (4) · Last reviewed commit: "Fix identity conversion matching in tupl..." · Reviewed by Greptile

@thomhurst

Copy link
Copy Markdown
Owner Author

Review dispositions for 018ab78:

  • Codex tuple shape mismatch: fixed and thread resolved; 21 focused tests and 123 MethodDataSourceAnalyzerTests pass on net10.0 using SDK 10.0.403.
  • Claude note 1: added a string[][] to T[][] regression row to preserve the intended reference-array behavior. The conservative inference fallback remains intentional.
  • Claude note 2: retaining the separate shape walks for this correctness fix. No measured analyzer performance regression warrants restructuring the traversal here.
  • Claude note 3: added the comment linking constraint/general inference work to Analyzer misses generic constraint violations inside tuple and array parameters #7520.

Existing build warnings remain tracked in #7410 (release tracking/nullable warnings) and #7412 (restore advisories). Keeping this draft for fresh reviews of the new head.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 018ab7847f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Review of PR 7541

The change is correct for the scenarios it targets. The new analyzer path in CompilationExtensions.cs is narrowly gated: it applies only when the parameter type contains both an array and a type parameter. The 20-case matrix, including the imported T[*] metadata case, covers the array-rank/SZ-array checks and the identity requirement for value-type array elements. I found nothing blocking.

Non-blocking observations:

  1. ContainsArray is narrower than the traversal in MatchesOpenArrayShape. It only looks through tuples and arrays. A parameter such as List<(T, int[])> or Func<T[]> skips the new path and uses the old behavior. That matches the stated scope (generic constraint work stays in Analyzer misses generic constraint violations inside tuple and array parameters #7520). A short comment on ContainsArray saying nested generics are deliberately excluded would stop someone from 'fixing' it later.

  2. The fallback return true is permissive on purpose. For (T, object[]) against (int, string[]), the matrix expects no diagnostic because Roslyn does the concrete conversion check. If the same type parameter is bound to two incompatible elements, for example (T, T[]) with (int, string[]), no diagnostic is reported. The PR states this limitation. It would be worth adding one pinning test, so that when Analyzer misses generic constraint violations inside tuple and array parameters #7520 adds bound inference the behavior change shows up as a test diff.

  3. requiresIdentity is a boolean flowing through recursion. It's fine at this size. If more shape rules arrive, a small enum such as Variance { Covariant, Invariant } would read better than a bare true argument.

  4. The metadata-built T[*] test is clever but fragile. It hand-assembles a PE image with magic bytes. The comment helps. Extracting the blob into a named constant or a small helper would make the signature easier to audit.

Overall this is a well-scoped, well-tested fix. Approving from my side.

@thomhurst

Copy link
Copy Markdown
Owner Author

Review dispositions for 53caaf4:

  • Codex identity-conversion finding: fixed using Roslyn ClassifyConversion(...).IsIdentity, with dynamic/object regression coverage in both directions and nested tuple arrays. Numeric and reference conversion rejection remains covered.
  • Claude observation 1: added a comment documenting the deliberate exclusion of nested generic arguments from ContainsArray.
  • Claude observation 2: added the (int, string[]) to (T, T[]) pinning case. Shared-bound inference remains intentionally permissive, consistent with Analyzer misses generic constraint violations inside tuple and array parameters #7520's uncertainty requirement.
  • Claude observation 3: retain the boolean at this size, as the review recommends; reconsider an enum if additional shape rules are introduced.
  • Claude observation 4: retain the inline metadata signature and its adjacent byte-by-byte description. It has one use, so extracting it would not improve the current test's auditability.

Validation: the three new dynamic/object cases failed before the fix. After the fix, all 26 focused tuple-array tests and 123 MethodDataSourceAnalyzerTests pass using SDK 10.0.403 on net10.0. Existing restore/release-tracking/nullable warnings remain tracked in #7412 and #7410. Keeping the PR draft for fresh review of this head.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 53caaf48bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Review of #7541

I read the diff and did not build it or run the tests. I found no blocking issues.

What it does: it stops TUnit0001 from firing on rows like (Fixture, CancellationToken, int[]) when the test takes (Fixture, CancellationToken, T[]). It walks tuple and array shapes, comparing rank, SZ-array-ness and tuple arity. It requires identity for elements inside arrays, because arrays of value types are invariant. For anything it can't resolve it falls back to accepting, so it won't add false positives.

Strengths

  • The scope is narrow. The new path only runs when the parameter type contains both an array and a type parameter, so existing generic-tuple behaviour is unchanged.
  • requiresIdentity is carried down through nested tuples, which keeps (int, dynamic)[] against (T, object)[] correct.
  • The tests are thorough. They include a metadata-imported T[*] case that C# can't express, which confirms the IsSZArray check.

Minor, non-blocking

Looks good to merge once CI is green.

@thomhurst

Copy link
Copy Markdown
Owner Author

Promoted head 53caaf4 after positive Claude and Codex verdicts and verification that every review thread is resolved. The existing scope comment and covariance regression cover the latest non-blocking observations; retaining the current helper structure. Awaiting the ready-for-review Claude run and CI before auto-merge can be armed.

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Review of #7541

I read the diff but didn't build it or run the tests. My first attempt to run the review skill failed, so this was done by hand.

The fix looks sound and is well scoped. It traverses tuple and array shapes and leaves concrete conversions to Roslyn's ClassifyConversion. It checks rank and IsSZArray, so T[*] metadata arrays are told apart from vectors. It requires identity for elements inside arrays, which matches C# invariance for value-type elements. Nested generic bindings stay permissive, with #7520 as the follow-up. The test matrix is thorough, including the metadata-built T[*] case.

Non-blocking suggestions:

  • ContainsArray only recurses through tuples, while MatchesOpenArrayShape falls through to return true for other open types. A short comment at that fall-through would explain why List<T[]> stays on the old path.
  • requiresIdentity is passed down through tuple recursion. That is correct, but a one-line doc comment on the parameter would make the invariance reasoning easier to follow.
  • The (int, string[]) to (T, T[]) case is pinned as 'no diagnostic'. That is fine as a documented limitation. Reference Analyzer misses generic constraint violations inside tuple and array parameters #7520 in that test so it gets tightened when inference lands.

Nothing blocking. LGTM.

@thomhurst

thomhurst commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner Author

The earlier blocker has cleared: #7549 merged as 4891fce, and .NET run 41766395044 completed successfully. Rebased this PR onto that fresh main and pushed head 7d5938d. Range-diff confirms all three analyzer commits retain identical patches, and git diff --check passed.

Validation after rebase: full TUnit.Analyzers.Tests suite on SDK 10.0.403/net10.0 passed (1,064 passed, 1 skipped, 0 failed). Existing warnings remain tracked in #7410 and #7412. Fresh CI and current-head reviews are required before merging; no workflow rerun, on-hold label, or external decision is needed. This PR still delivers #7539 and is not superseded.

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Review

The fix is sound and narrowly scoped. It does what the description says: it walks tuple and array shapes, checks rank and SZ-array-ness, and leaves generic constraints and inference to #7520. I read the diff only and did not build or run the tests.

What I checked

  • The identity requirement for elements of value-type arrays is applied correctly. Tuple fields inside (…)[] are invariant, and the (int,int)[] → (T,long)[] case reports a diagnostic as it should.
  • The imported T[*] metadata test covers the rank-1 non-vector edge case that C# source can't express. It is a good test, if a bit heavy.
  • The change adds no new reflection or allocations beyond the Any call in ContainsArray, which only runs for tuple parameter types that contain a type parameter.

Non-blocking suggestions

  1. ContainsArray and ContainsTypeParameter gate entry. Arrays nested inside non-tuple generics, such as List<T[]>, still use the old permissive path. The comment mentions this, so a one-line link to Analyzer misses generic constraint violations inside tuple and array parameters #7520 next to the gate would make the limit obvious to future readers.
  2. MatchesOpenArrayShape ends with return true for unknown nested generics. That is intentional, but a short comment saying that it fails open (so it can only miss diagnostics, never add false ones) would help.
  3. Several test rows are labelled "permissive until Analyzer misses generic constraint violations inside tuple and array parameters #7520" while expecting no diagnostic, for example (int, string[]) → (T, T[]). If Analyzer misses generic constraint violations inside tuple and array parameters #7520 later tightens this, those rows will flip silently. A test-case DisplayName or a comment on each row would make that easier to spot.

No blocking issues. Looks good to merge once CI is green.

This was referenced Nov 4, 2026

This branch was previously deployed

1 inactive deployment
Pull Requests — 7d5938da Deployed Nov 4, 2026 by thomhurst via modularpipeline (macos-latest) #21112
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.

Accept compatible generic array elements inside tuple data-source parameters

1 participant