Skip to content

Commit faf663a

Browse files
committed
C#: Flow summary for Nullable<T>
1 parent 77abff7 commit faf663a

File tree

3 files changed

+84
-1
lines changed

3 files changed

+84
-1
lines changed

csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ abstract class LibraryTypeDataFlow extends Type {
339339
* Holds if data may flow from `source` to `sink` when calling callable `c`.
340340
*
341341
* `sourceAp` describes the contents of `source` that flows to `sink`
342-
* (if any), and `sinkContent` describes the contents of `sink` that it
342+
* (if any), and `sinkAp` describes the contents of `sink` that it
343343
* flows to (if any).
344344
*/
345345
pragma[nomagic]
@@ -728,6 +728,49 @@ class SystemLazyFlow extends LibraryTypeDataFlow, SystemLazyClass {
728728
}
729729
}
730730

731+
/** Data flow for `System.Nullable<>`. */
732+
class SystemNullableFlow extends LibraryTypeDataFlow, SystemNullableStruct {
733+
override predicate callableFlow(
734+
CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp,
735+
SourceDeclarationCallable c, boolean preservesValue
736+
) {
737+
preservesValue = true and
738+
c.(Constructor).getDeclaringType() = this and
739+
source = getFlowSourceArg(c, 0, sourceAp) and
740+
sourceAp = AccessPath::empty() and
741+
sink = TCallableFlowSinkReturn() and
742+
sinkAp = AccessPath::property(this.getValueProperty())
743+
or
744+
preservesValue = true and
745+
c = this.getAGetValueOrDefaultMethod() and
746+
source = TCallableFlowSourceQualifier() and
747+
sourceAp = AccessPath::property(this.getValueProperty()) and
748+
sink = TCallableFlowSinkReturn() and
749+
sinkAp = AccessPath::empty()
750+
or
751+
preservesValue = false and
752+
c = this.getHasValueProperty().getGetter() and
753+
source = TCallableFlowSourceQualifier() and
754+
sourceAp = AccessPath::property(this.getValueProperty()) and
755+
sink = TCallableFlowSinkReturn() and
756+
sinkAp = AccessPath::empty()
757+
or
758+
preservesValue = true and
759+
c = this.getAGetValueOrDefaultMethod() and
760+
source = getFlowSourceArg(c, 0, _) and
761+
sourceAp = AccessPath::empty() and
762+
sink = TCallableFlowSinkReturn() and
763+
sinkAp = AccessPath::empty()
764+
or
765+
preservesValue = false and
766+
c = this.getValueProperty().getGetter() and
767+
source = TCallableFlowSourceQualifier() and
768+
sourceAp = AccessPath::empty() and
769+
sink = TCallableFlowSinkReturn() and
770+
sinkAp = AccessPath::empty()
771+
}
772+
}
773+
731774
/** Data flow for `System.Collections.IEnumerable` (and sub types). */
732775
class IEnumerableFlow extends LibraryTypeDataFlow, RefType {
733776
IEnumerableFlow() { this.getABaseType*() instanceof SystemCollectionsIEnumerableInterface }

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ class SystemUnboundGenericClass extends UnboundGenericClass {
2121
SystemUnboundGenericClass() { this.getNamespace() instanceof SystemNamespace }
2222
}
2323

24+
/** An unbound generic struct in the `System` namespace. */
25+
class SystemUnboundGenericStruct extends UnboundGenericStruct {
26+
SystemUnboundGenericStruct() { this.getNamespace() instanceof SystemNamespace }
27+
}
28+
2429
/** An interface in the `System` namespace. */
2530
class SystemInterface extends Interface {
2631
SystemInterface() { this.getNamespace() instanceof SystemNamespace }
@@ -215,6 +220,35 @@ class SystemLazyClass extends SystemUnboundGenericClass {
215220
}
216221
}
217222

223+
/** The `System.Nullable<T>` struct. */
224+
class SystemNullableStruct extends SystemUnboundGenericStruct {
225+
SystemNullableStruct() {
226+
this.hasName("Nullable<>") and
227+
this.getNumberOfTypeParameters() = 1
228+
}
229+
230+
/** Gets the `Value` property. */
231+
Property getValueProperty() {
232+
result.getDeclaringType() = this and
233+
result.hasName("Value") and
234+
result.getType() = getTypeParameter(0)
235+
}
236+
237+
/** Gets the `HasValue` property. */
238+
Property getHasValueProperty() {
239+
result.getDeclaringType() = this and
240+
result.hasName("HasValue") and
241+
result.getType() instanceof BoolType
242+
}
243+
244+
/** Gets a `GetValueOrDefault()` method. */
245+
Method getAGetValueOrDefaultMethod() {
246+
result.getDeclaringType() = this and
247+
result.hasName("GetValueOrDefault") and
248+
result.getReturnType() = getTypeParameter(0)
249+
}
250+
}
251+
218252
/** The `System.NullReferenceException` class. */
219253
class SystemNullReferenceExceptionClass extends SystemClass {
220254
SystemNullReferenceExceptionClass() { this.hasName("NullReferenceException") }

csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,8 @@ callableFlow
494494
| System.Net.WebUtility.HtmlEncode(string) | argument 0 -> return | false |
495495
| System.Net.WebUtility.HtmlEncode(string, TextWriter) | argument 0 -> return | false |
496496
| System.Net.WebUtility.UrlEncode(string) | argument 0 -> return | false |
497+
| System.Nullable<>.GetValueOrDefault(T) | argument 0 -> return | true |
498+
| System.Nullable<>.get_Value() | qualifier -> return | false |
497499
| System.Security.Cryptography.CryptoStream.BeginRead(Byte[], int, int, AsyncCallback, object) | qualifier -> argument 0 | false |
498500
| System.Security.Cryptography.CryptoStream.BeginWrite(Byte[], int, int, AsyncCallback, object) | argument 0 -> qualifier | false |
499501
| System.Security.Cryptography.CryptoStream.Read(Byte[], int, int) | qualifier -> argument 0 | false |
@@ -1848,6 +1850,10 @@ callableFlowAccessPath
18481850
| System.Net.NetworkInformation.IPAddressCollection.Add(IPAddress) | argument 0 [<empty>] -> qualifier [[]] | true |
18491851
| System.Net.NetworkInformation.IPAddressCollection.CopyTo(IPAddress[], int) | qualifier [[]] -> argument 0 [[]] | true |
18501852
| System.Net.NetworkInformation.IPAddressCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true |
1853+
| System.Nullable<>.GetValueOrDefault() | qualifier [Value] -> return [<empty>] | true |
1854+
| System.Nullable<>.GetValueOrDefault(T) | qualifier [Value] -> return [<empty>] | true |
1855+
| System.Nullable<>.Nullable(T) | argument 0 [<empty>] -> return [Value] | true |
1856+
| System.Nullable<>.get_HasValue() | qualifier [Value] -> return [<empty>] | false |
18511857
| System.Resources.ResourceReader.GetEnumerator() | qualifier [[]] -> return [Current] | true |
18521858
| System.Resources.ResourceSet.GetEnumerator() | qualifier [[]] -> return [Current] | true |
18531859
| System.Runtime.CompilerServices.ConditionalWeakTable<,>.GetEnumerator() | qualifier [[]] -> return [Current] | true |

0 commit comments

Comments
 (0)