6
6
7
7
private import codeql.ruby.AST
8
8
private import codeql.ruby.ast.internal.AST
9
+ private import codeql.ruby.ast.internal.Control
9
10
private import codeql.ruby.controlflow.ControlFlowGraph
10
11
private import ControlFlowGraphImpl
11
12
private import NonReturning
@@ -27,6 +28,10 @@ private newtype TCompletion =
27
28
outer instanceof NonNestedNormalCompletion and
28
29
nestLevel = 0
29
30
or
31
+ inner instanceof TBooleanCompletion and
32
+ outer instanceof TMatchingCompletion and
33
+ nestLevel = 0
34
+ or
30
35
inner instanceof NormalCompletion and
31
36
nestedEnsureCompletion ( outer , nestLevel )
32
37
}
@@ -81,8 +86,9 @@ private predicate mayRaise(Call c) {
81
86
82
87
/** A completion of a statement or an expression. */
83
88
abstract 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
86
92
this = n .( NonReturningCall ) .getACompletion ( )
87
93
or
88
94
completionIsValidForStmt ( n , this )
@@ -98,15 +104,26 @@ abstract class Completion extends TCompletion {
98
104
mustHaveMatchingCompletion ( n ) and
99
105
this = TMatchingCompletion ( _)
100
106
or
101
- n = any ( RescueModifierExpr parent ) .getBody ( ) and this = TRaiseCompletion ( )
107
+ n = any ( RescueModifierExpr parent ) .getBody ( ) and
108
+ this = [ TSimpleCompletion ( ) .( TCompletion ) , TRaiseCompletion ( ) ]
102
109
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 )
105
125
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
110
127
this = TSimpleCompletion ( )
111
128
}
112
129
@@ -172,6 +189,8 @@ private predicate inBooleanContext(AstNode n) {
172
189
or
173
190
n = any ( ConditionalLoop parent ) .getCondition ( )
174
191
or
192
+ n = any ( InClause parent ) .getCondition ( )
193
+ or
175
194
exists ( LogicalAndExpr parent |
176
195
n = parent .getLeftOperand ( )
177
196
or
@@ -218,6 +237,10 @@ private predicate inMatchingContext(AstNode n) {
218
237
w .getPattern ( _) = n
219
238
)
220
239
or
240
+ n instanceof CasePattern
241
+ or
242
+ n = any ( VariableReferencePattern p ) .getVariableAccess ( )
243
+ or
221
244
n .( Trees:: DefaultValueParameterTree ) .hasDefaultValue ( )
222
245
}
223
246
@@ -241,7 +264,7 @@ class SimpleCompletion extends NonNestedNormalCompletion, TSimpleCompletion {
241
264
* the successor. Either a Boolean completion (`BooleanCompletion`), or a matching
242
265
* completion (`MatchingCompletion`).
243
266
*/
244
- abstract class ConditionalCompletion extends NonNestedNormalCompletion {
267
+ abstract class ConditionalCompletion extends NormalCompletion {
245
268
boolean value ;
246
269
247
270
bindingset [ value]
@@ -255,7 +278,7 @@ abstract class ConditionalCompletion extends NonNestedNormalCompletion {
255
278
* A completion that represents evaluation of an expression
256
279
* with a Boolean value.
257
280
*/
258
- class BooleanCompletion extends ConditionalCompletion , TBooleanCompletion {
281
+ class BooleanCompletion extends ConditionalCompletion , NonNestedNormalCompletion , TBooleanCompletion {
259
282
BooleanCompletion ( ) { this = TBooleanCompletion ( value ) }
260
283
261
284
/** Gets the dual Boolean completion. */
@@ -280,10 +303,16 @@ class FalseCompletion extends BooleanCompletion {
280
303
* A completion that represents evaluation of a matching test, for example
281
304
* a test in a `rescue` statement.
282
305
*/
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
+ }
285
312
286
- override MatchingSuccessor getAMatchingSuccessorType ( ) { result .getValue ( ) = value }
313
+ override ConditionalSuccessor getAMatchingSuccessorType ( ) {
314
+ this = TMatchingCompletion ( result .( MatchingSuccessor ) .getValue ( ) )
315
+ }
287
316
288
317
override string toString ( ) { if value = true then result = "match" else result = "no-match" }
289
318
}
@@ -440,7 +469,9 @@ abstract class NestedCompletion extends Completion, TNestedCompletion {
440
469
NestedCompletion ( ) { this = TNestedCompletion ( inner , outer , nestLevel ) }
441
470
442
471
/** Gets a completion that is compatible with the inner completion. */
443
- abstract Completion getAnInnerCompatibleCompletion ( ) ;
472
+ Completion getAnInnerCompatibleCompletion ( ) {
473
+ result .getOuterCompletion ( ) = this .getInnerCompletion ( )
474
+ }
444
475
445
476
/** Gets the level of this nested completion. */
446
477
final int getNestLevel ( ) { result = nestLevel }
@@ -483,9 +514,39 @@ class NestedEnsureCompletion extends NestedCompletion {
483
514
484
515
override Completion getOuterCompletion ( ) { result = outer }
485
516
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
488
541
}
489
542
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 ( ) }
491
552
}
0 commit comments