@@ -232,36 +232,57 @@ private Expr getQualifier(QualifiableExpr e) {
232232 result = e .getChildExpr ( - 1 )
233233}
234234
235+ pragma [ noinline]
236+ private predicate typePatternMustHaveMatchingCompletion (
237+ TypePatternExpr tpe , Type t , Type strippedType
238+ ) {
239+ exists ( Expr e , Expr stripped | mustHaveMatchingCompletion ( e , tpe ) |
240+ stripped = e .stripCasts ( ) and
241+ t = tpe .getCheckedType ( ) and
242+ strippedType = stripped .getType ( ) and
243+ not t .containsTypeParameters ( ) and
244+ not strippedType .containsTypeParameters ( )
245+ )
246+ }
247+
248+ pragma [ noinline]
249+ private Type typePatternCommonSubTypeLeft ( Type t ) {
250+ typePatternMustHaveMatchingCompletion ( _, t , _) and
251+ result .isImplicitlyConvertibleTo ( t ) and
252+ not result instanceof DynamicType
253+ }
254+
255+ pragma [ noinline]
256+ private Type typePatternCommonSubTypeRight ( Type strippedType ) {
257+ typePatternMustHaveMatchingCompletion ( _, _, strippedType ) and
258+ result .isImplicitlyConvertibleTo ( strippedType ) and
259+ not result instanceof DynamicType
260+ }
261+
262+ pragma [ noinline]
263+ private predicate typePatternCommonSubType ( Type t , Type strippedType ) {
264+ typePatternCommonSubTypeLeft ( t ) = typePatternCommonSubTypeRight ( strippedType )
265+ }
266+
235267/**
236- * Holds if expression `e ` constantly matches (`value = true`) or constantly
237- * non-matches (`value = false`).
268+ * Holds if pattern expression `pe ` constantly matches (`value = true`) or
269+ * constantly non-matches (`value = false`).
238270 */
239- private predicate isMatchingConstant ( Expr e , boolean value ) {
240- exists ( Switch s | mustHaveMatchingCompletion ( s , e ) |
241- exists ( Expr stripped | stripped = s .getExpr ( ) .stripCasts ( ) |
242- exists ( Case c , string strippedValue |
243- c = s .getACase ( ) and
244- e = c .getPattern ( ) and
245- strippedValue = stripped .getValue ( )
246- |
247- if strippedValue = e .getValue ( ) then value = true else value = false
248- )
249- or
250- exists ( Case c , TypePatternExpr tpe , Type t , Type strippedType | c = s .getACase ( ) |
251- tpe = c .getPattern ( ) and
252- e = tpe and
253- t = tpe .getCheckedType ( ) and
254- strippedType = stripped .getType ( ) and
255- not t .isImplicitlyConvertibleTo ( strippedType ) and
256- not t instanceof Interface and
257- not t .containsTypeParameters ( ) and
258- not strippedType .containsTypeParameters ( ) and
259- value = false
260- )
261- )
262- or
263- e instanceof DiscardPatternExpr and
264- value = true
271+ private predicate isMatchingConstant ( PatternExpr pe , boolean value ) {
272+ exists ( Expr e , string exprValue , string patternValue |
273+ mustHaveMatchingCompletion ( e , pe ) and
274+ exprValue = e .stripCasts ( ) .getValue ( ) and
275+ patternValue = pe .getValue ( ) and
276+ if exprValue = patternValue then value = true else value = false
277+ )
278+ or
279+ pe instanceof DiscardPatternExpr and
280+ value = true
281+ or
282+ exists ( Type t , Type strippedType |
283+ typePatternMustHaveMatchingCompletion ( pe , t , strippedType ) and
284+ not typePatternCommonSubType ( t , strippedType ) and
285+ value = false
265286 )
266287}
267288
@@ -518,7 +539,20 @@ predicate switchMatching(Switch s, Case c, PatternExpr pe) {
518539 pe = c .getPattern ( )
519540}
520541
521- private predicate mustHaveMatchingCompletion ( Switch s , PatternExpr pe ) { switchMatching ( s , _, pe ) }
542+ /**
543+ * Holds if `pe` must have a matching completion, and `e` is the expression
544+ * that is being matched.
545+ */
546+ private predicate mustHaveMatchingCompletion ( Expr e , PatternExpr pe ) {
547+ exists ( Switch s |
548+ switchMatching ( s , _, pe ) and
549+ e = s .getExpr ( )
550+ )
551+ or
552+ pe = any ( IsExpr ie | inBooleanContext ( ie ) and e = ie .getExpr ( ) ) .getPattern ( )
553+ or
554+ pe = any ( UnaryPatternExpr upe | mustHaveMatchingCompletion ( e , upe ) ) .getPattern ( )
555+ }
522556
523557/**
524558 * Holds if a normal completion of `cfe` must be a matching completion. Thats is,
@@ -565,7 +599,13 @@ class SimpleCompletion extends NonNestedNormalCompletion, TSimpleCompletion {
565599 * completion (`NullnessCompletion`), a matching completion (`MatchingCompletion`),
566600 * or an emptiness completion (`EmptinessCompletion`).
567601 */
568- abstract class ConditionalCompletion extends NonNestedNormalCompletion { }
602+ abstract class ConditionalCompletion extends NonNestedNormalCompletion {
603+ /** Gets the Boolean value of this completion. */
604+ abstract boolean getValue ( ) ;
605+
606+ /** Gets the dual completion. */
607+ abstract ConditionalCompletion getDual ( ) ;
608+ }
569609
570610/**
571611 * A completion that represents evaluation of an expression
@@ -576,10 +616,9 @@ class BooleanCompletion extends ConditionalCompletion {
576616
577617 BooleanCompletion ( ) { this = TBooleanCompletion ( value ) }
578618
579- /** Gets the Boolean value of this completion. */
580- boolean getValue ( ) { result = value }
619+ override boolean getValue ( ) { result = value }
581620
582- BooleanCompletion getDual ( ) { result = TBooleanCompletion ( value .booleanNot ( ) ) }
621+ override BooleanCompletion getDual ( ) { result = TBooleanCompletion ( value .booleanNot ( ) ) }
583622
584623 override BooleanSuccessor getAMatchingSuccessorType ( ) { result .getValue ( ) = value }
585624
@@ -611,6 +650,10 @@ class NullnessCompletion extends ConditionalCompletion, TNullnessCompletion {
611650 /** Holds if the last sub expression of this expression evaluates to a non-`null` value. */
612651 predicate isNonNull ( ) { value = false }
613652
653+ override boolean getValue ( ) { result = value }
654+
655+ override NullnessCompletion getDual ( ) { result = TNullnessCompletion ( value .booleanNot ( ) ) }
656+
614657 override NullnessSuccessor getAMatchingSuccessorType ( ) { result .getValue ( ) = value }
615658
616659 override string toString ( ) { if this .isNull ( ) then result = "null" else result = "non-null" }
@@ -631,6 +674,10 @@ class MatchingCompletion extends ConditionalCompletion, TMatchingCompletion {
631674 /** Holds if there is not a match. */
632675 predicate isNonMatch ( ) { value = false }
633676
677+ override boolean getValue ( ) { result = value }
678+
679+ override MatchingCompletion getDual ( ) { result = TMatchingCompletion ( value .booleanNot ( ) ) }
680+
634681 override MatchingSuccessor getAMatchingSuccessorType ( ) { result .getValue ( ) = value }
635682
636683 override string toString ( ) { if this .isMatch ( ) then result = "match" else result = "no-match" }
@@ -648,6 +695,10 @@ class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion {
648695 /** Holds if the emptiness test evaluates to `true`. */
649696 predicate isEmpty ( ) { value = true }
650697
698+ override boolean getValue ( ) { result = value }
699+
700+ override EmptinessCompletion getDual ( ) { result = TEmptinessCompletion ( value .booleanNot ( ) ) }
701+
651702 override EmptinessSuccessor getAMatchingSuccessorType ( ) { result .getValue ( ) = value }
652703
653704 override string toString ( ) { if this .isEmpty ( ) then result = "empty" else result = "non-empty" }
0 commit comments