66
77private import codeql.ruby.AST
88private import codeql.ruby.ast.internal.AST
9+ private import codeql.ruby.ast.internal.Control
910private import codeql.ruby.controlflow.ControlFlowGraph
1011private import ControlFlowGraphImpl
1112private import NonReturning
@@ -27,6 +28,10 @@ private newtype TCompletion =
2728 outer instanceof NonNestedNormalCompletion and
2829 nestLevel = 0
2930 or
31+ inner instanceof TBooleanCompletion and
32+ outer instanceof TMatchingCompletion and
33+ nestLevel = 0
34+ or
3035 inner instanceof NormalCompletion and
3136 nestedEnsureCompletion ( outer , nestLevel )
3237 }
@@ -81,8 +86,9 @@ private predicate mayRaise(Call c) {
8186
8287/** A completion of a statement or an expression. */
8388abstract class Completion extends TCompletion {
84- /** Holds if this completion is valid for node `n`. */
85- predicate isValidFor ( AstNode n ) {
89+ private predicate isValidForSpecific ( AstNode n ) {
90+ exists ( AstNode other | n = other .getDesugared ( ) and this .isValidForSpecific ( other ) )
91+ or
8692 this = n .( NonReturningCall ) .getACompletion ( )
8793 or
8894 completionIsValidForStmt ( n , this )
@@ -98,15 +104,26 @@ abstract class Completion extends TCompletion {
98104 mustHaveMatchingCompletion ( n ) and
99105 this = TMatchingCompletion ( _)
100106 or
101- n = any ( RescueModifierExpr parent ) .getBody ( ) and this = TRaiseCompletion ( )
107+ n = any ( RescueModifierExpr parent ) .getBody ( ) and
108+ this = [ TSimpleCompletion ( ) .( TCompletion ) , TRaiseCompletion ( ) ]
102109 or
103- mayRaise ( n ) and
104- this = TRaiseCompletion ( )
110+ (
111+ mayRaise ( n )
112+ or
113+ n instanceof CaseMatch and not exists ( n .( CaseExpr ) .getElseBranch ( ) )
114+ ) and
115+ (
116+ this = TRaiseCompletion ( )
117+ or
118+ this = TSimpleCompletion ( ) and not n instanceof NonReturningCall
119+ )
120+ }
121+
122+ /** Holds if this completion is valid for node `n`. */
123+ predicate isValidFor ( AstNode n ) {
124+ this .isValidForSpecific ( n )
105125 or
106- not n instanceof NonReturningCall and
107- not completionIsValidForStmt ( n , _) and
108- not mustHaveBooleanCompletion ( n ) and
109- not mustHaveMatchingCompletion ( n ) and
126+ not any ( Completion c ) .isValidForSpecific ( n ) and
110127 this = TSimpleCompletion ( )
111128 }
112129
@@ -172,6 +189,8 @@ private predicate inBooleanContext(AstNode n) {
172189 or
173190 n = any ( ConditionalLoop parent ) .getCondition ( )
174191 or
192+ n = any ( InClause parent ) .getCondition ( )
193+ or
175194 exists ( LogicalAndExpr parent |
176195 n = parent .getLeftOperand ( )
177196 or
@@ -218,6 +237,10 @@ private predicate inMatchingContext(AstNode n) {
218237 w .getPattern ( _) = n
219238 )
220239 or
240+ n instanceof CasePattern
241+ or
242+ n = any ( VariableReferencePattern p ) .getVariableAccess ( )
243+ or
221244 n .( Trees:: DefaultValueParameterTree ) .hasDefaultValue ( )
222245}
223246
@@ -241,7 +264,7 @@ class SimpleCompletion extends NonNestedNormalCompletion, TSimpleCompletion {
241264 * the successor. Either a Boolean completion (`BooleanCompletion`), or a matching
242265 * completion (`MatchingCompletion`).
243266 */
244- abstract class ConditionalCompletion extends NonNestedNormalCompletion {
267+ abstract class ConditionalCompletion extends NormalCompletion {
245268 boolean value ;
246269
247270 bindingset [ value]
@@ -255,7 +278,7 @@ abstract class ConditionalCompletion extends NonNestedNormalCompletion {
255278 * A completion that represents evaluation of an expression
256279 * with a Boolean value.
257280 */
258- class BooleanCompletion extends ConditionalCompletion , TBooleanCompletion {
281+ class BooleanCompletion extends ConditionalCompletion , NonNestedNormalCompletion , TBooleanCompletion {
259282 BooleanCompletion ( ) { this = TBooleanCompletion ( value ) }
260283
261284 /** Gets the dual Boolean completion. */
@@ -280,10 +303,16 @@ class FalseCompletion extends BooleanCompletion {
280303 * A completion that represents evaluation of a matching test, for example
281304 * a test in a `rescue` statement.
282305 */
283- class MatchingCompletion extends ConditionalCompletion , TMatchingCompletion {
284- MatchingCompletion ( ) { this = TMatchingCompletion ( value ) }
306+ class MatchingCompletion extends ConditionalCompletion {
307+ MatchingCompletion ( ) {
308+ this = TMatchingCompletion ( value )
309+ or
310+ this = TNestedCompletion ( _, TMatchingCompletion ( value ) , _)
311+ }
285312
286- override MatchingSuccessor getAMatchingSuccessorType ( ) { result .getValue ( ) = value }
313+ override ConditionalSuccessor getAMatchingSuccessorType ( ) {
314+ this = TMatchingCompletion ( result .( MatchingSuccessor ) .getValue ( ) )
315+ }
287316
288317 override string toString ( ) { if value = true then result = "match" else result = "no-match" }
289318}
@@ -440,7 +469,9 @@ abstract class NestedCompletion extends Completion, TNestedCompletion {
440469 NestedCompletion ( ) { this = TNestedCompletion ( inner , outer , nestLevel ) }
441470
442471 /** Gets a completion that is compatible with the inner completion. */
443- abstract Completion getAnInnerCompatibleCompletion ( ) ;
472+ Completion getAnInnerCompatibleCompletion ( ) {
473+ result .getOuterCompletion ( ) = this .getInnerCompletion ( )
474+ }
444475
445476 /** Gets the level of this nested completion. */
446477 final int getNestLevel ( ) { result = nestLevel }
@@ -483,9 +514,39 @@ class NestedEnsureCompletion extends NestedCompletion {
483514
484515 override Completion getOuterCompletion ( ) { result = outer }
485516
486- override Completion getAnInnerCompatibleCompletion ( ) {
487- result .getOuterCompletion ( ) = this .getInnerCompletion ( )
517+ override SuccessorType getAMatchingSuccessorType ( ) { none ( ) }
518+ }
519+
520+ /**
521+ * A completion used for conditions in pattern matching:
522+ *
523+ * ```rb
524+ * in x if x == 5 then puts "five"
525+ * in x unless x == 4 then puts "not four"
526+ * ```
527+ *
528+ * The outer (Matching) completion indicates whether there is a match, and
529+ * the inner (Boolean) completion indicates what the condition evaluated
530+ * to.
531+ *
532+ * For the condition `x == 5` above, `TNestedCompletion(true, true, 0)` and
533+ * `TNestedCompletion(false, false, 0)` are both valid completions, while
534+ * `TNestedCompletion(true, false, 0)` and `TNestedCompletion(false, true, 0)`
535+ * are valid completions for `x == 4`.
536+ */
537+ class NestedMatchingCompletion extends NestedCompletion , MatchingCompletion {
538+ NestedMatchingCompletion ( ) {
539+ inner instanceof TBooleanCompletion and
540+ outer instanceof TMatchingCompletion
488541 }
489542
490- override SuccessorType getAMatchingSuccessorType ( ) { none ( ) }
543+ override BooleanCompletion getInnerCompletion ( ) { result = inner }
544+
545+ override MatchingCompletion getOuterCompletion ( ) { result = outer }
546+
547+ override BooleanSuccessor getAMatchingSuccessorType ( ) {
548+ result .getValue ( ) = this .getInnerCompletion ( ) .getValue ( )
549+ }
550+
551+ override string toString ( ) { result = NestedCompletion .super .toString ( ) }
491552}
0 commit comments