@@ -112,19 +112,28 @@ private import AbstractValues
112112 * an expression that may evaluate to `null`.
113113 */
114114class DereferenceableExpr extends Expr {
115+ private boolean isNullableType ;
116+
115117 DereferenceableExpr ( ) {
116118 exists ( Expr e , Type t |
117119 // There is currently a bug in the extractor: the type of `x?.Length` is
118120 // incorrectly `int`, while it should have been `int?`. We apply
119121 // `getNullEquivParent()` as a workaround
120122 this = getNullEquivParent * ( e ) and
121123 t = e .getType ( ) |
122- t instanceof NullableType
124+ t instanceof NullableType and
125+ isNullableType = true
123126 or
124- t instanceof RefType
127+ t instanceof RefType and
128+ isNullableType = false
125129 )
126130 }
127131
132+ /** Holds if this expression has a nullable type `T?`. */
133+ predicate hasNullableType ( ) {
134+ isNullableType = true
135+ }
136+
128137 /**
129138 * Gets an expression that directly tests whether this expression is `null`.
130139 *
@@ -177,9 +186,22 @@ class DereferenceableExpr extends Expr {
177186 if ie .( IsConstantExpr ) .getConstant ( ) instanceof NullLiteral then
178187 // E.g. `x is null`
179188 isNull = branch
180- else
189+ else (
181190 // E.g. `x is string` or `x is ""`
182- ( branch = true and isNull = false )
191+ branch = true and isNull = false
192+ or
193+ // E.g. `x is string` where `x` has type `string`
194+ ie = any ( IsTypeExpr ite | ite .getCheckedType ( ) = ite .getExpr ( ) .getType ( ) ) and
195+ branch = false and
196+ isNull = true
197+ )
198+ )
199+ or
200+ this .hasNullableType ( ) and
201+ result = any ( PropertyAccess pa |
202+ pa .getQualifier ( ) = this and
203+ pa .getTarget ( ) .hasName ( "HasValue" ) and
204+ if branch = true then isNull = false else isNull = true
183205 )
184206 or
185207 isCustomNullCheck ( result , this , v , isNull )
@@ -307,19 +329,23 @@ class AccessOrCallExpr extends Expr {
307329}
308330
309331private Declaration getDeclarationTarget ( Expr e ) {
310- e = any ( AssignableRead ar | result = ar .getTarget ( ) ) or
332+ e = any ( AssignableAccess aa | result = aa .getTarget ( ) ) or
311333 result = e .( Call ) .getTarget ( )
312334}
313335
314336private Ssa:: Definition getAnSsaQualifier ( Expr e ) {
315- e = getATrackedRead ( result )
337+ e = getATrackedAccess ( result )
316338 or
317- not e = getATrackedRead ( _) and
339+ not e = getATrackedAccess ( _) and
318340 result = getAnSsaQualifier ( e .( QualifiableExpr ) .getQualifier ( ) )
319341}
320342
321- private AssignableRead getATrackedRead ( Ssa:: Definition def ) {
322- result = def .getARead ( ) and
343+ private AssignableAccess getATrackedAccess ( Ssa:: Definition def ) {
344+ (
345+ result = def .getARead ( )
346+ or
347+ result = def .( Ssa:: ExplicitDefinition ) .getADefinition ( ) .getTargetAccess ( )
348+ ) and
323349 not def instanceof Ssa:: ImplicitUntrackedDefinition
324350}
325351
@@ -384,6 +410,15 @@ class GuardedExpr extends AccessOrCallExpr {
384410 v = v0
385411 }
386412
413+ /**
414+ * Holds if this expression must have abstract value `v`. That is, this
415+ * expression is guarded by a structurally equal expression having abstract
416+ * value `v`.
417+ */
418+ predicate mustHaveValue ( AbstractValue v ) {
419+ exists ( Expr e | e = this .getAGuard ( e , v ) )
420+ }
421+
387422 /**
388423 * Holds if this expression is guarded by expression `cond`, which must
389424 * evaluate to `b`. The expression `sub` is a sub expression of `cond`
@@ -401,7 +436,7 @@ class GuardedExpr extends AccessOrCallExpr {
401436/** An expression guarded by a `null` check. */
402437class NullGuardedExpr extends GuardedExpr {
403438 NullGuardedExpr ( ) {
404- exists ( Expr e , NullValue v | e = this .getAGuard ( e , v ) | not v .isNull ( ) )
439+ this .mustHaveValue ( any ( NullValue v | not v .isNull ( ) ) )
405440 }
406441}
407442
@@ -420,11 +455,28 @@ module Internal {
420455
421456 /** Holds if expression `e` is a non-`null` value. */
422457 predicate nonNullValue ( Expr e ) {
423- e .stripCasts ( ) = any ( Expr s | s .hasValue ( ) and not s instanceof NullLiteral )
458+ e instanceof ObjectCreation
459+ or
460+ e instanceof ArrayCreation
461+ or
462+ e .hasValue ( ) and
463+ not e instanceof NullLiteral
464+ or
465+ e instanceof ThisAccess
466+ or
467+ e instanceof AddExpr and
468+ e .getType ( ) instanceof StringType
469+ }
470+
471+ /** Holds if expression `e2` is a non-`null` value whenever `e1` is. */
472+ predicate nonNullValueImplied ( Expr e1 , Expr e2 ) {
473+ e1 = e2 .( CastExpr ) .getExpr ( )
474+ or
475+ e1 = e2 .( AssignExpr ) .getRValue ( )
424476 }
425477
426478 /**
427- * Gets the parent expression of `e` which is `null` only if `e` is `null`,
479+ * Gets the parent expression of `e` which is `null` iff `e` is `null`,
428480 * if any. For example, `result = x?.y` and `e = x`, or `result = x + 1`
429481 * and `e = x`.
430482 */
@@ -433,6 +485,8 @@ module Internal {
433485 qe .getQualifier ( ) = e and
434486 qe .isConditional ( ) and
435487 (
488+ // The accessed declaration must have a value type in order
489+ // for `only if` to hold
436490 result .( FieldAccess ) .getTarget ( ) .getType ( ) instanceof ValueType
437491 or
438492 result .( Call ) .getTarget ( ) .getReturnType ( ) instanceof ValueType
@@ -444,11 +498,28 @@ module Internal {
444498 result = bao and
445499 bao .getAnOperand ( ) = e and
446500 bao .getAnOperand ( ) = o and
501+ // The other operand must be provably non-null in order
502+ // for `only if` to hold
447503 nonNullValue ( o ) and
448504 e != o
449505 )
450506 }
451507
508+ /**
509+ * Gets a child expression of `e` which is `null` only if `e` is `null`.
510+ */
511+ Expr getANullImplyingChild ( Expr e ) {
512+ e = any ( QualifiableExpr qe |
513+ qe .isConditional ( ) and
514+ result = qe .getQualifier ( )
515+ )
516+ or
517+ // In C#, `null + 1` has type `int?` with value `null`
518+ e = any ( BinaryArithmeticOperation bao |
519+ result = bao .getAnOperand ( )
520+ )
521+ }
522+
452523 /** An expression whose value may control the execution of another element. */
453524 class Guard extends Expr {
454525 private AbstractValue val ;
@@ -785,6 +856,23 @@ module Internal {
785856 v1 instanceof NullValue and
786857 v2 = v1
787858 or
859+ g1 instanceof DereferenceableExpr and
860+ g2 = getANullImplyingChild ( g1 ) and
861+ v1 = any ( NullValue nv | not nv .isNull ( ) ) and
862+ v2 = v1
863+ or
864+ g2 = g1 .( AssignExpr ) .getRValue ( ) and
865+ v1 = g1 .getAValue ( ) and
866+ v2 = v1
867+ or
868+ g2 = g1 .( Assignment ) .getLValue ( ) and
869+ v1 = g1 .getAValue ( ) and
870+ v2 = v1
871+ or
872+ g2 = g1 .( CastExpr ) .getExpr ( ) and
873+ v1 = g1 .getAValue ( ) and
874+ v2 = v1 .( NullValue )
875+ or
788876 exists ( PreSsa:: Definition def |
789877 def .getDefinition ( ) .getSource ( ) = g2 |
790878 g1 = def .getARead ( ) and
0 commit comments