C#: Add implicit span conversion test case.

This commit is contained in:
Michael Nebel
2025-12-18 11:50:44 +01:00
parent 119ecff3b7
commit 44c9c58b48
3 changed files with 79 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
public interface CovariantInterface<out T> { }
public interface InvariantInterface<T> { }
public interface Interface<out T1, T2> { }
public class Base { }
public class Derived : Base { }
public class C
{
public void M()
{
string[] stringArray = [];
string[][] stringArrayArray;
string[,] stringArray2D;
Span<string> stringSpan = stringArray; // string[] -> Span<string>;
// Covariant conversions to ReadOnlySpan
// Assignments are included to illustrate that this compiles.
// Only the use of the types matter in terms of test output.
ReadOnlySpan<CovariantInterface<Base>> covariantInterfaceBaseReadOnlySpan;
ReadOnlySpan<CovariantInterface<Derived>> covariantInterfaceDerivedReadOnlySpan = default;
Span<CovariantInterface<Derived>> covariantInterfaceDerivedSpan = default;
CovariantInterface<Derived>[] covariantInterfaceDerivedArray = [];
covariantInterfaceBaseReadOnlySpan = covariantInterfaceDerivedReadOnlySpan; // ReadOnlySpan<CovariantInterface<Derived>> -> ReadOnlySpan<CovariantInterface<Base>>
covariantInterfaceBaseReadOnlySpan = covariantInterfaceDerivedSpan; // Span<CovariantInterface<Derived>> -> ReadOnlySpan<CovariantInterface<Base>>
covariantInterfaceBaseReadOnlySpan = covariantInterfaceDerivedArray; // CovariantInterface<Derived>[] -> ReadOnlySpan<CovariantInterface<Base>>
// Identify conversions to ReadOnlySpan
ReadOnlySpan<string> stringReadOnlySpan;
stringReadOnlySpan = stringSpan; // Span<string> -> ReadOnlySpan<string>;
stringReadOnlySpan = stringArray; // string[] -> ReadOnlySpan<string>;
// Convert string to ReadOnlySpan<char>
string s = "";
ReadOnlySpan<char> charReadOnlySpan = s; // string -> ReadOnlySpan<char>
// Use the non-covariant interfaces to show that no conversion is possible.
ReadOnlySpan<InvariantInterface<Base>> invariantInterfaceBaseReadOnlySpan;
ReadOnlySpan<InvariantInterface<Derived>> invariantInterfaceDerivedReadOnlySpan;
Span<InvariantInterface<Derived>> invariantInterfaceDerivedSpan;
InvariantInterface<Derived>[] invariantInterfaceDerivedArray;
ReadOnlySpan<Interface<Base, string>> interfaceBaseReadOnlySpan;
ReadOnlySpan<Interface<Derived, string>> interfaceDerivedReadOnlySpan;
Span<Interface<Derived, string>> interfaceDerivedSpan;
Interface<Derived, string>[] interfaceDerivedArray;
}
}

View File

@@ -0,0 +1,16 @@
| CovariantInterface<Derived>[] | ReadOnlySpan<CovariantInterface<Base>> |
| CovariantInterface<Derived>[] | ReadOnlySpan<CovariantInterface<Derived>> |
| CovariantInterface<Derived>[] | Span<CovariantInterface<Derived>> |
| Interface<Derived,String>[] | ReadOnlySpan<Interface<Derived, string>> |
| Interface<Derived,String>[] | Span<Interface<Derived, string>> |
| InvariantInterface<Derived>[] | ReadOnlySpan<InvariantInterface<Derived>> |
| InvariantInterface<Derived>[] | Span<InvariantInterface<Derived>> |
| ReadOnlySpan<CovariantInterface<Derived>> | ReadOnlySpan<CovariantInterface<Base>> |
| Span<CovariantInterface<Derived>> | ReadOnlySpan<CovariantInterface<Base>> |
| Span<CovariantInterface<Derived>> | ReadOnlySpan<CovariantInterface<Derived>> |
| Span<Interface<Derived, string>> | ReadOnlySpan<Interface<Derived, string>> |
| Span<InvariantInterface<Derived>> | ReadOnlySpan<InvariantInterface<Derived>> |
| Span<string> | ReadOnlySpan<string> |
| String[] | ReadOnlySpan<string> |
| String[] | Span<string> |
| string | ReadOnlySpan<char> |

View File

@@ -0,0 +1,9 @@
import semmle.code.csharp.Conversion
private class InterestingType extends Type {
InterestingType() { exists(LocalVariable lv | lv.getType() = this) }
}
from InterestingType sub, InterestingType sup
where convSpan(sub, sup) and sub != sup
select sub.toStringWithTypes() as s1, sup.toStringWithTypes() as s2 order by s1, s2