Skip to content

Commit 7cdaa79

Browse files
authored
Merge pull request #18329 from michaelnebel/csharp/params
C# 13: params modifier on collection types.
2 parents f23e56b + fe4ec59 commit 7cdaa79

30 files changed

+2234
-1752
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* C# 13: Added QL library support for *collection* like type `params` parameters.

csharp/ql/lib/semmle/code/csharp/Callable.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Stmt
88
import Type
99
import exprs.Call
1010
private import commons.QualifiedName
11+
private import commons.Collections
1112
private import semmle.code.csharp.ExprOrStmtParent
1213
private import semmle.code.csharp.metrics.Complexity
1314
private import TypeRef
@@ -273,7 +274,7 @@ class Method extends Callable, Virtualizable, Attributable, @method {
273274
Type getParamsType() {
274275
exists(Parameter last | last = this.getParameter(this.getNumberOfParameters() - 1) |
275276
last.isParams() and
276-
result = last.getType().(ArrayType).getElementType()
277+
result = last.getType().(ParamsCollectionType).getElementType()
277278
)
278279
}
279280

csharp/ql/lib/semmle/code/csharp/Variable.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ class Parameter extends LocalScopeVariable, Attributable, TopLevelExprParent, @p
157157
predicate isOutOrRef() { this.isOut() or this.isRef() }
158158

159159
/**
160-
* Holds if this parameter is a parameter array. For example, `args`
160+
* Holds if this parameter is a parameter collection. For example, `args`
161161
* is a parameter array in
162162
*
163163
* ```csharp

csharp/ql/lib/semmle/code/csharp/commons/Collections.qll

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import csharp
44
import semmle.code.csharp.frameworks.system.Collections
5+
private import semmle.code.csharp.frameworks.System
6+
private import semmle.code.csharp.frameworks.system.collections.Generic
57

68
private string modifyMethodName() {
79
result =
@@ -67,6 +69,42 @@ class CollectionType extends RefType {
6769
}
6870
}
6971

72+
/**
73+
* A collection type that can be used as a `params` parameter type.
74+
*/
75+
abstract private class ParamsCollectionTypeImpl extends ValueOrRefType {
76+
/**
77+
* Gets the element type of this collection, for example `int` in `IEnumerable<int>`.
78+
*/
79+
abstract Type getElementType();
80+
}
81+
82+
private class ParamsArrayType extends ParamsCollectionTypeImpl instanceof ArrayType {
83+
override Type getElementType() { result = ArrayType.super.getElementType() }
84+
}
85+
86+
private class ParamsConstructedCollectionTypes extends ParamsCollectionTypeImpl {
87+
private ConstructedType base;
88+
89+
ParamsConstructedCollectionTypes() {
90+
exists(UnboundGenericType unboundbase |
91+
base = this.getABaseType*() and unboundbase = base.getUnboundGeneric()
92+
|
93+
unboundbase instanceof SystemCollectionsGenericIEnumerableTInterface or
94+
unboundbase instanceof SystemCollectionsGenericICollectionInterface or
95+
unboundbase instanceof SystemCollectionsGenericIListTInterface or
96+
unboundbase instanceof SystemCollectionsGenericIReadOnlyCollectionTInterface or
97+
unboundbase instanceof SystemCollectionsGenericIReadOnlyListTInterface or
98+
unboundbase instanceof SystemSpanStruct or
99+
unboundbase instanceof SystemReadOnlySpanStruct
100+
)
101+
}
102+
103+
override Type getElementType() { result = base.getTypeArgument(0) }
104+
}
105+
106+
final class ParamsCollectionType = ParamsCollectionTypeImpl;
107+
70108
/** Holds if `t` is a collection type. */
71109
predicate isCollectionType(ValueOrRefType t) {
72110
t.getABaseType*() instanceof SystemCollectionsIEnumerableInterface and

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
774774

775775
/**
776776
* Holds if `arg` is a `params` argument of `c`, for parameter `p`, and `arg` will
777-
* be wrapped in an array by the C# compiler.
777+
* be wrapped in an collection by the C# compiler.
778778
*/
779779
private predicate isParamsArg(Call c, Expr arg, Parameter p) {
780780
exists(Callable target, int numArgs |
@@ -1645,7 +1645,7 @@ private module ArgumentNodes {
16451645
}
16461646

16471647
/**
1648-
* A data-flow node that represents the implicit array creation in a call to a
1648+
* A data-flow node that represents the implicit collection creation in a call to a
16491649
* callable with a `params` parameter. For example, there is an implicit array
16501650
* creation `new [] { "a", "b", "c" }` in
16511651
*
@@ -1684,7 +1684,7 @@ private module ArgumentNodes {
16841684

16851685
override Location getLocationImpl() { result = callCfn.getLocation() }
16861686

1687-
override string toStringImpl() { result = "[implicit array creation] " + callCfn }
1687+
override string toStringImpl() { result = "[implicit collection creation] " + callCfn }
16881688
}
16891689

16901690
private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNodeImpl {

csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import csharp
9+
private import semmle.code.csharp.commons.Collections
910
private import RuntimeCallable
1011

1112
/** A call. */
@@ -1137,7 +1138,7 @@ private module Internal {
11371138
if p.isParams()
11381139
then (
11391140
j >= i and
1140-
paramType = p.getType().(ArrayType).getElementType()
1141+
paramType = p.getType().(ParamsCollectionType).getElementType()
11411142
) else (
11421143
i = j and
11431144
paramType = p.getType()

csharp/ql/lib/semmle/code/csharp/frameworks/Format.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import csharp
6+
private import semmle.code.csharp.commons.Collections
67
private import semmle.code.csharp.frameworks.System
78
private import semmle.code.csharp.frameworks.system.Text
89

@@ -106,7 +107,9 @@ class StringFormatItemParameter extends Parameter {
106107
}
107108

108109
private Type getParameterType(Parameter p) {
109-
if p.isParams() then result = p.getType().(ArrayType).getElementType() else result = p.getType()
110+
if p.isParams()
111+
then result = p.getType().(ParamsCollectionType).getElementType()
112+
else result = p.getType()
110113
}
111114

112115
/** Regex for a valid insert. */

csharp/ql/lib/semmle/code/csharp/frameworks/System.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,3 +755,19 @@ class SystemNotImplementedExceptionClass extends SystemClass {
755755
class SystemDateTimeStruct extends SystemStruct {
756756
SystemDateTimeStruct() { this.hasName("DateTime") }
757757
}
758+
759+
/** The `System.Span<T>` struct. */
760+
class SystemSpanStruct extends SystemUnboundGenericStruct {
761+
SystemSpanStruct() {
762+
this.hasName("Span`1") and
763+
this.getNumberOfTypeParameters() = 1
764+
}
765+
}
766+
767+
/** The `System.ReadOnlySpan<T>` struct. */
768+
class SystemReadOnlySpanStruct extends SystemUnboundGenericStruct {
769+
SystemReadOnlySpanStruct() {
770+
this.hasName("ReadOnlySpan`1") and
771+
this.getNumberOfTypeParameters() = 1
772+
}
773+
}

csharp/ql/lib/semmle/code/csharp/frameworks/system/collections/Generic.qll

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,21 @@ class SystemCollectionsGenericIDictionaryInterface extends SystemCollectionsGene
158158
this.getNumberOfTypeParameters() = 2
159159
}
160160
}
161+
162+
/** The ``System.Collections.Generic.IReadOnlyCollection`1`` interface. */
163+
class SystemCollectionsGenericIReadOnlyCollectionTInterface extends SystemCollectionsGenericUnboundGenericInterface
164+
{
165+
SystemCollectionsGenericIReadOnlyCollectionTInterface() {
166+
this.hasName("IReadOnlyCollection`1") and
167+
this.getNumberOfTypeParameters() = 1
168+
}
169+
}
170+
171+
/** The ``System.Collections.Generic.IReadOnlyList`1`` interface. */
172+
class SystemCollectionsGenericIReadOnlyListTInterface extends SystemCollectionsGenericUnboundGenericInterface
173+
{
174+
SystemCollectionsGenericIReadOnlyListTInterface() {
175+
this.hasName("IReadOnlyList`1") and
176+
this.getNumberOfTypeParameters() = 1
177+
}
178+
}

0 commit comments

Comments
 (0)