Skip to content

Revive NRefactory conversion tests and cover all of C# standard chapter 10#3912

Merged
siegfriedpammer merged 4 commits into
masterfrom
revive-conversion-tests
Jul 25, 2026
Merged

Revive NRefactory conversion tests and cover all of C# standard chapter 10#3912
siegfriedpammer merged 4 commits into
masterfrom
revive-conversion-tests

Conversation

@siegfriedpammer

Copy link
Copy Markdown
Member

Revives the NRefactory-era conversion tests that were commented out in ConversionTests.cs and ExplicitConversionTest.cs ("TODO: we should probably revive these tests somehow") and extends the suites so that every rule of the conversions chapter of the draft-v8 C# standard has a test case.

  • All commented-out tests now run without a resolver or AST: method groups via directly constructed MethodGroupResolveResults over fixture types compiled into the test assembly (extension methods injected through the internal extensionMethods field), type-parameter conversions via DefaultTypeParameter with cross-referencing constraints, user-defined operators as metadata-backed fixture types, and constant expressions via ConstantResolveResult.
  • Deviations from the NRefactory originals are documented inline. Notably, MethodGroupConversion_RefArgumentObjectVsDynamic now expects a valid conversion: object/dynamic mismatch in a ref position is an identity conversion and current csc accepts the assignment.
  • New tests fill every remaining chapter-10 gap: exhaustive implicit/explicit numeric matrices, the full boxing/unboxing rule set including variance, tuple literals and nullable-annotation/ValueTuple identity, interpolated-string and throw-expression conversions, generic method groups (type inference, explicit type arguments, no inference from the return type), and the anonymous-function compatibility checks CSharpConversions performs itself.
  • Two rules are implementation gaps and carry [Ignore]d placeholder tests: default literal conversions (10.2.16, an explicit TODO in CSharpConversions) and switch expression conversions (10.2.18, no ResolveResult representation).
  • Differentially fuzzing the classification matrix against csc surfaced two real CSharpConversions bugs, captured as [Ignore]d known-bug tests: nullable conversions derived from tuple conversions are missing entirely, and explicit user-defined conversions to a nullable target are rejected when the operator result additionally needs an explicit numeric conversion.

This pull request was authored by an AI agent (Claude Code) operated by @siegfriedpammer.

🤖 Generated with Claude Code

The block of tests inherited from NRefactory's ConversionsTest was
commented out because it depended on ResolverTestBase (full AST +
CSharpResolver). Rewrite all of them against CSharpConversions directly:
hand-built MethodGroupResolveResults over fixture types compiled into the
test assembly (extension methods injected via the internal
extensionMethods field), DefaultTypeParameter instances with
cross-referencing constraints, and metadata-backed fixture interfaces for
the ExpansiveInheritance termination test.

Two deviations from the NRefactory originals:
MethodGroupConversion_RefArgumentObjectVsDynamic now expects a valid
conversion, because object/dynamic mismatch in a ref parameter is an
identity conversion and current csc accepts the assignment (verified);
PreferUserDefinedConversionOverReferenceConversion resolved an invocation,
so it is recast as an OverloadResolution test over FakeMethod candidates.

Assisted-by: Claude:claude-fable-5:Claude Code
Convert the commented-out block in ExplicitConversionsTest the same way as
the implicit ConversionTest block: type-parameter casts via
DefaultTypeParameter with cross-referencing constraints, user-defined
operators as fixture types in the test assembly, constant sources via
ConstantResolveResult. The rr.Input asserts of the originals were resolver
artifacts and are dropped; UseDefinedExplicitConversion_Lifted instead
exercises the ResolveResult-based ExplicitConversion entry point.

Also extend PreferAmbiguousConversionOverReferenceConversion with the
overload-resolution half of the original NRefactory test (the ambiguous
conversion must not prevent M(BB) from being chosen over M(object)), which
the first revival pass had reduced to the conversion classification alone.

Assisted-by: Claude:claude-fable-5:Claude Code
Audit the conversion test suites against the conversions chapter of the
draft-v8 C# standard and add tests for every rule that had none:
exhaustive implicit/explicit numeric conversion matrix, tuple/ValueTuple
and nullable-annotation identity, interpolated-string / throw-expression /
tuple-literal conversions, the boxing rule set including variance-based
boxing and unboxing, delegate-to-System.Delegate and IReadOnlyList<T>
reference conversions, type-parameter variance and effective-base-class
casts, generic method groups (inference, explicit type arguments, no
inference from the return type), the anonymous-function compatibility
checks CSharpConversions performs itself (via a LambdaResolveResult test
double), standard-conversion exclusion of user-defined operators, and
operators declared in base classes of the source type.

Two rules are implementation gaps rather than test gaps and get ignored
placeholder tests naming the gap: default literal conversions (10.2.16)
are an explicit TODO in CSharpConversions, and switch expressions
(10.2.18) have no ResolveResult representation. The full
section-by-section map is in
Analyses/ILSpy/2026-07-24_conversions-spec-coverage.md.

Assisted-by: Claude:claude-fable-5:Claude Code
Dumping the implicit/explicit classification of all pairs from a 120-type
universe and diffing it against what csc actually compiles surfaced two
divergences (16 affected pairs), both proven by a compiling-and-running
snippet and captured here as ignored known-bug tests:

1. Nullable conversions derived from tuple conversions are missing:
   csc accepts "(long, object)? a = t;" for t of type (int, string) as
   well as the lifted and explicit forms, CSharpConversions returns None.
   The ECMA spec's 10.6.1 does not list tuple conversions as liftable,
   so this is a case of Roslyn exceeding the spec.
2. An explicit user-defined conversion to a nullable target is rejected
   when the operator result additionally needs an explicit numeric
   conversion: csc accepts "(int?)new ImplicitToLong()" (operator to
   long, then explicit long -> int?), CSharpConversions returns None.

The same sweep confirmed the SByte..Decimal TypeCode range in
ImplicitEnumerationConversion is correct: csc accepts zero constants of
any numeric type (0.0, 0f, 0m) for enum conversion and rejects '\0',
matching the implementation exactly.

Assisted-by: Claude:claude-fable-5:Claude Code
@siegfriedpammer
siegfriedpammer merged commit 6932586 into master Jul 25, 2026
13 checks passed
@christophwille
christophwille deleted the revive-conversion-tests branch July 25, 2026 06:43
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