diff --git a/csharp/ql/lib/semmle/code/csharp/Conversion.qll b/csharp/ql/lib/semmle/code/csharp/Conversion.qll index 99c58ee51c6..7f943c555ff 100644 --- a/csharp/ql/lib/semmle/code/csharp/Conversion.qll +++ b/csharp/ql/lib/semmle/code/csharp/Conversion.qll @@ -28,6 +28,7 @@ private module Cached { * * - Identity conversions * - Implicit numeric conversions + * - Implicit span conversions * - Implicit nullable conversions * - Implicit reference conversions * - Boxing conversions @@ -38,6 +39,8 @@ private module Cached { or convNumeric(fromType, toType) or + convSpan(fromType, toType) + or convNullableType(fromType, toType) or convRefTypeNonNull(fromType, toType) @@ -81,6 +84,7 @@ private predicate implicitConversionNonNull(Type fromType, Type toType) { * * - Identity conversions * - Implicit numeric conversions + * - Implicit span conversions * - Implicit nullable conversions * - Implicit reference conversions * - Boxing conversions @@ -491,6 +495,53 @@ private predicate convNumericChar(SimpleType toType) { private predicate convNumericFloat(SimpleType toType) { toType instanceof DoubleType } +private class SpanType extends GenericType { + SpanType() { this.getUnboundGeneric() instanceof SystemSpanStruct } + + Type getElementType() { result = this.getTypeArgument(0) } +} + +private class ReadOnlySpanType extends GenericType { + ReadOnlySpanType() { this.getUnboundGeneric() instanceof SystemReadOnlySpanStruct } + + Type getElementType() { result = this.getTypeArgument(0) } +} + +private class SimpleArrayType extends ArrayType { + SimpleArrayType() { + this.getRank() = 1 and + this.getDimension() = 1 + } +} + +/** + * INTERNAL: Do not use. + * + * Holds if there is an implicit span conversion from `fromType` to `toType`. + * + * 10.2.1: Implicit span conversions (added in C# 14). + * [Documentation](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-14.0/first-class-span-types#span-conversions) + */ +predicate convSpan(Type fromType, Type toType) { + fromType.(SimpleArrayType).getElementType() = toType.(SpanType).getElementType() + or + exists(Type fromElementType, Type toElementType | + ( + fromElementType = fromType.(SimpleArrayType).getElementType() or + fromElementType = fromType.(SpanType).getElementType() or + fromElementType = fromType.(ReadOnlySpanType).getElementType() + ) and + toElementType = toType.(ReadOnlySpanType).getElementType() + | + convIdentity(fromElementType, toElementType) + or + convCovariance(fromElementType, toElementType) + ) + or + fromType instanceof SystemStringClass and + toType.(ReadOnlySpanType).getElementType() instanceof CharType +} + /** * INTERNAL: Do not use. * @@ -784,8 +835,8 @@ predicate convConversionOperator(Type fromType, Type toType) { ) } -/** 13.1.3.2: Variance conversion. */ -private predicate convVariance(GenericType fromType, GenericType toType) { +pragma[nomagic] +private predicate convVarianceAux(UnboundGenericType ugt, GenericType fromType, GenericType toType) { // Semantically equivalent with // ```ql // ugt = fromType.getUnboundGeneric() @@ -805,10 +856,23 @@ private predicate convVariance(GenericType fromType, GenericType toType) { // ``` // but performance is improved by explicitly evaluating the `i`th argument // only when all preceding arguments are convertible. - Variance::convVarianceSingle(_, fromType, toType) + Variance::convVarianceSingle(ugt, fromType, toType) or + Variance::convVarianceMultiple(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1) +} + +/** 13.1.3.2: Variance conversion. */ +private predicate convVariance(GenericType fromType, GenericType toType) { + convVarianceAux(_, fromType, toType) +} + +/** + * Holds, if `fromType` is covariance convertible to `toType`. + */ +private predicate convCovariance(GenericType fromType, GenericType toType) { exists(UnboundGenericType ugt | - Variance::convVarianceMultiple(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1) + convVarianceAux(ugt, fromType, toType) and + forall(TypeParameter tp | tp = ugt.getATypeParameter() | tp.isOut()) ) }