forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathControlFlowGraphImpl.qll
1687 lines (1499 loc) · 58.1 KB
/
ControlFlowGraphImpl.qll
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* Provides an implementation for constructing control-flow graphs (CFGs) from
* abstract syntax trees (ASTs), using the shared library from `codeql.controlflow.Cfg`.
*/
import csharp
private import codeql.controlflow.Cfg as CfgShared
private import Completion
private import Splitting
private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.commons.Compilation
/** An element that defines a new CFG scope. */
class CfgScope extends Element, @top_level_exprorstmt_parent {
CfgScope() {
this.getFile().fromSource() and
(
this =
any(Callable c |
c.(Constructor).hasInitializer()
or
InitializerSplitting::constructorInitializes(c, _)
or
c.hasBody()
)
or
// For now, static initializer values have their own scope. Eventually, they
// should be treated like instance initializers.
this.(Assignable).(Modifiable).isStatic() and
expr_parent_top_level_adjusted2(_, _, this)
)
}
}
private class TAstNode = @callable or @control_flow_element;
private Element getAChild(Element p) {
result = p.getAChild() or
result = p.(AssignOperation).getExpandedAssignment()
}
/** An AST node. */
class AstNode extends Element, TAstNode {
AstNode() { this = getAChild*(any(@top_level_exprorstmt_parent p | not p instanceof Attribute)) }
int getId() { idOf(this, result) }
}
private predicate id(AstNode x, AstNode y) { x = y }
private predicate idOf(AstNode x, int y) = equivalenceRelation(id/2)(x, y)
private module CfgInput implements CfgShared::InputSig<Location> {
private import ControlFlowGraphImpl as Impl
private import Completion as Comp
private import SuccessorType as ST
private import semmle.code.csharp.Caching
class AstNode = Impl::AstNode;
class Completion = Comp::Completion;
predicate completionIsNormal(Completion c) { c instanceof Comp::NormalCompletion }
predicate completionIsSimple(Completion c) { c instanceof Comp::SimpleCompletion }
predicate completionIsValidFor(Completion c, AstNode e) { c.isValidFor(e) }
class CfgScope = Impl::CfgScope;
CfgScope getCfgScope(AstNode n) {
Stages::ControlFlowStage::forceCachingInSameStage() and
result = n.(ControlFlowElement).getEnclosingCallable()
}
predicate scopeFirst(CfgScope scope, AstNode first) { Impl::scopeFirst(scope, first) }
predicate scopeLast(CfgScope scope, AstNode last, Completion c) {
Impl::scopeLast(scope, last, c)
}
class SuccessorType = ST::SuccessorType;
SuccessorType getAMatchingSuccessorType(Completion c) { result = c.getAMatchingSuccessorType() }
predicate successorTypeIsSimple(SuccessorType t) {
t instanceof ST::SuccessorTypes::NormalSuccessor
}
predicate successorTypeIsCondition(SuccessorType t) {
t instanceof ST::SuccessorTypes::ConditionalSuccessor
}
predicate isAbnormalExitType(SuccessorType t) {
t instanceof ST::SuccessorTypes::ExceptionSuccessor or
t instanceof ST::SuccessorTypes::ExitSuccessor
}
int idOfAstNode(AstNode node) { result = node.getId() }
int idOfCfgScope(CfgScope node) { result = idOfAstNode(node) }
}
private module CfgSplittingInput implements CfgShared::SplittingInputSig<Location, CfgInput> {
private import Splitting as S
class SplitKindBase = S::TSplitKind;
class Split = S::Split;
}
private module ConditionalCompletionSplittingInput implements
CfgShared::ConditionalCompletionSplittingInputSig<Location, CfgInput, CfgSplittingInput>
{
import Splitting::ConditionalCompletionSplitting::ConditionalCompletionSplittingInput
}
import CfgShared::MakeWithSplitting<Location, CfgInput, CfgSplittingInput, ConditionalCompletionSplittingInput>
/**
* A compilation.
*
* Unlike the standard `Compilation` class, this class also supports buildless
* extraction.
*/
newtype CompilationExt =
TCompilation(Compilation c) { not extractionIsStandalone() } or
TBuildless() { extractionIsStandalone() }
/** Gets the compilation that source file `f` belongs to. */
CompilationExt getCompilation(File f) {
exists(Compilation c |
f = c.getAFileCompiled() and
result = TCompilation(c)
)
or
result = TBuildless()
}
/**
* The `expr_parent_top_level_adjusted()` relation restricted to exclude relations
* between properties and their getters' expression bodies in properties such as
* `int P => 0`.
*
* This is in order to only associate the expression body with one CFG scope, namely
* the getter (and not the declaration itself).
*/
private predicate expr_parent_top_level_adjusted2(
Expr child, int i, @top_level_exprorstmt_parent parent
) {
expr_parent_top_level_adjusted(child, i, parent) and
not exists(Getter g |
g.getDeclaration() = parent and
i = 0
)
}
/** Holds if `first` is first executed when entering `scope`. */
predicate scopeFirst(CfgScope scope, AstNode first) {
scope =
any(Callable c |
if exists(c.(Constructor).getInitializer())
then first(c.(Constructor).getInitializer(), first)
else
if InitializerSplitting::constructorInitializes(c, _)
then first(InitializerSplitting::constructorInitializeOrder(c, _, 0), first)
else first(c.getBody(), first)
)
or
expr_parent_top_level_adjusted2(any(Expr e | first(e, first)), _, scope) and
not scope instanceof Callable
}
/** Holds if `scope` is exited when `last` finishes with completion `c`. */
predicate scopeLast(CfgScope scope, AstNode last, Completion c) {
scope =
any(Callable callable |
last(callable.getBody(), last, c) and
not c instanceof GotoCompletion
or
last(InitializerSplitting::lastConstructorInitializer(scope, _), last, c) and
not callable.hasBody()
)
or
expr_parent_top_level_adjusted2(any(Expr e | last(e, last, c)), _, scope) and
not scope instanceof Callable
}
private class ConstructorTree extends ControlFlowTree instanceof Constructor {
final override predicate propagatesAbnormal(AstNode child) { none() }
final override predicate first(AstNode first) { none() }
final override predicate last(AstNode last, Completion c) { none() }
/** Gets the body of this constructor belonging to compilation `comp`. */
pragma[noinline]
AstNode getBody(CompilationExt comp) {
result = super.getBody() and
comp = getCompilation(result.getFile())
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
exists(CompilationExt comp, int i, AssignExpr ae |
ae = InitializerSplitting::constructorInitializeOrder(this, comp, i) and
last(ae, pred, c) and
c instanceof NormalCompletion
|
// Flow from one member initializer to the next
first(InitializerSplitting::constructorInitializeOrder(this, comp, i + 1), succ)
or
// Flow from last member initializer to constructor body
ae = InitializerSplitting::lastConstructorInitializer(this, comp) and
first(this.getBody(comp), succ)
)
}
}
abstract private class SwitchTree extends ControlFlowTree instanceof Switch {
override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Flow from last element of switch expression to first element of first case
last(super.getExpr(), pred, c) and
c instanceof NormalCompletion and
first(super.getCase(0), succ)
or
// Flow from last element of case pattern to next case
exists(Case case, int i | case = super.getCase(i) |
last(case.getPattern(), pred, c) and
c.(MatchingCompletion).isNonMatch() and
first(super.getCase(i + 1), succ)
)
or
// Flow from last element of condition to next case
exists(Case case, int i | case = super.getCase(i) |
last(case.getCondition(), pred, c) and
c instanceof FalseCompletion and
first(super.getCase(i + 1), succ)
)
}
}
abstract private class CaseTree extends ControlFlowTree instanceof Case {
final override predicate propagatesAbnormal(AstNode child) {
child in [super.getPattern().(ControlFlowElement), super.getCondition(), super.getBody()]
}
override predicate succ(AstNode pred, AstNode succ, Completion c) {
last(super.getPattern(), pred, c) and
c.(MatchingCompletion).isMatch() and
(
if exists(super.getCondition())
then
// Flow from the last element of pattern to the condition
first(super.getCondition(), succ)
else
// Flow from last element of pattern to first element of body
first(super.getBody(), succ)
)
or
// Flow from last element of condition to first element of body
last(super.getCondition(), pred, c) and
c instanceof TrueCompletion and
first(super.getBody(), succ)
}
}
module Expressions {
/** An expression that should not be included in the control flow graph. */
abstract private class NoNodeExpr extends Expr { }
private class SimpleNoNodeExpr extends NoNodeExpr {
SimpleNoNodeExpr() {
this instanceof TypeAccess and
not this instanceof TypeAccessPatternExpr
}
}
/** A write access that is not also a read access. */
private class WriteAccess extends AssignableWrite {
WriteAccess() {
// `x++` is both a read and write access
not this instanceof AssignableRead
}
}
private class WriteAccessNoNodeExpr extends WriteAccess, NoNodeExpr {
WriteAccessNoNodeExpr() {
// For example a write to a static field, `Foo.Bar = 0`.
forall(Expr e | e = this.getAChildExpr() | e instanceof NoNodeExpr)
}
}
private AstNode getExprChild0(Expr e, int i) {
not e instanceof NameOfExpr and
not e instanceof QualifiableExpr and
not e instanceof Assignment and
not e instanceof AnonymousFunctionExpr and
result = e.getChild(i)
or
e = any(ExtensionMethodCall emc | result = emc.getArgument(i))
or
e =
any(QualifiableExpr qe |
not qe instanceof ExtensionMethodCall and
result = qe.getChild(i)
)
or
e =
any(Assignment a |
// The left-hand side of an assignment is evaluated before the right-hand side
i = 0 and result = a.getLValue()
or
i = 1 and result = a.getRValue()
)
}
private AstNode getExprChild(Expr e, int i) {
result =
rank[i + 1](AstNode cfe, int j |
cfe = getExprChild0(e, j) and
not cfe instanceof NoNodeExpr
|
cfe order by j
)
}
private AstNode getLastExprChild(Expr e) {
exists(int last |
result = getExprChild(e, last) and
not exists(getExprChild(e, last + 1))
)
}
private class StandardExpr extends StandardPostOrderTree instanceof Expr {
StandardExpr() {
// The following expressions need special treatment
not this instanceof LogicalNotExpr and
not this instanceof LogicalAndExpr and
not this instanceof LogicalOrExpr and
not this instanceof NullCoalescingExpr and
not this instanceof ConditionalExpr and
not this instanceof AssignOperationWithExpandedAssignment and
not this instanceof ConditionallyQualifiedExpr and
not this instanceof ThrowExpr and
not this instanceof ObjectCreation and
not this instanceof ArrayCreation and
not this instanceof QualifiedWriteAccess and
not this instanceof AccessorWrite and
not this instanceof NoNodeExpr and
not this instanceof SwitchExpr and
not this instanceof SwitchCaseExpr and
not this instanceof ConstructorInitializer and
not this instanceof NotPatternExpr and
not this instanceof OrPatternExpr and
not this instanceof AndPatternExpr and
not this instanceof RecursivePatternExpr and
not this instanceof PositionalPatternExpr and
not this instanceof PropertyPatternExpr
}
final override AstNode getChildNode(int i) { result = getExprChild(this, i) }
}
/**
* A qualified write access. In a qualified write access, the access itself is
* not evaluated, only the qualifier and the indexer arguments (if any).
*/
private class QualifiedWriteAccess extends ControlFlowTree instanceof WriteAccess, QualifiableExpr
{
QualifiedWriteAccess() {
this.hasQualifier()
or
// Member initializers like
// ```csharp
// new Dictionary<int, string>() { [0] = "Zero", [1] = "One", [2] = "Two" }
// ```
// need special treatment, because the accesses `[0]`, `[1]`, and `[2]`
// have no qualifier.
this = any(MemberInitializer mi).getLValue()
}
final override predicate propagatesAbnormal(AstNode child) { child = getExprChild(this, _) }
final override predicate first(AstNode first) { first(getExprChild(this, 0), first) }
final override predicate last(AstNode last, Completion c) {
// Skip the access in a qualified write access
last(getLastExprChild(this), last, c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
exists(int i |
last(getExprChild(this, i), pred, c) and
c instanceof NormalCompletion and
first(getExprChild(this, i + 1), succ)
)
}
}
private class StatOrDynAccessorCall_ =
@dynamic_member_access_expr or @dynamic_element_access_expr or @call_access_expr;
/** A normal or a (potential) dynamic call to an accessor. */
private class StatOrDynAccessorCall extends Expr, StatOrDynAccessorCall_ { }
/**
* An expression that writes via an accessor call, for example `x.Prop = 0`,
* where `Prop` is a property.
*
* Accessor writes need special attention, because we need to model the fact
* that the accessor is called *after* the assigned value has been evaluated.
* In the example above, this means we want a CFG that looks like
*
* ```csharp
* x -> 0 -> set_Prop -> x.Prop = 0
* ```
*/
class AccessorWrite extends PostOrderTree instanceof Expr {
AssignableDefinition def;
AccessorWrite() {
def.getExpr() = this and
def.getTargetAccess().(WriteAccess) instanceof StatOrDynAccessorCall and
not this instanceof AssignOperationWithExpandedAssignment
}
/**
* Gets the `i`th accessor being called in this write. More than one call
* can happen in tuple assignments.
*/
StatOrDynAccessorCall getCall(int i) {
result =
rank[i + 1](AssignableDefinitions::TupleAssignmentDefinition tdef |
tdef.getExpr() = this and tdef.getTargetAccess() instanceof StatOrDynAccessorCall
|
tdef order by tdef.getEvaluationOrder()
).getTargetAccess()
or
i = 0 and
result = def.getTargetAccess() and
not def instanceof AssignableDefinitions::TupleAssignmentDefinition
}
final override predicate propagatesAbnormal(AstNode child) {
child = getExprChild(this, _)
or
child = this.getCall(_)
}
final override predicate first(AstNode first) { first(getExprChild(this, 0), first) }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Standard left-to-right evaluation
exists(int i |
last(getExprChild(this, i), pred, c) and
c instanceof NormalCompletion and
first(getExprChild(this, i + 1), succ)
)
or
// Flow from last element of last child to first accessor call
last(getLastExprChild(this), pred, c) and
succ = this.getCall(0) and
c instanceof NormalCompletion
or
// Flow from one call to the next
exists(int i | pred = this.getCall(i) |
succ = this.getCall(i + 1) and
c.isValidFor(pred) and
c instanceof NormalCompletion
)
or
// Post-order: flow from last call to element itself
exists(int last | last = max(int i | exists(this.getCall(i))) |
pred = this.getCall(last) and
succ = this and
c.isValidFor(pred) and
c instanceof NormalCompletion
)
}
}
private class LogicalNotExprTree extends PostOrderTree instanceof LogicalNotExpr {
private Expr operand;
LogicalNotExprTree() { operand = this.getOperand() }
final override predicate propagatesAbnormal(AstNode child) { child = operand }
final override predicate first(AstNode first) { first(operand, first) }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
succ = this and
last(operand, pred, c) and
c instanceof NormalCompletion
}
}
private class LogicalAndExprTree extends PostOrderTree instanceof LogicalAndExpr {
final override predicate propagatesAbnormal(AstNode child) {
child in [super.getLeftOperand(), super.getRightOperand()]
}
final override predicate first(AstNode first) { first(super.getLeftOperand(), first) }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Flow from last element of left operand to first element of right operand
last(super.getLeftOperand(), pred, c) and
c instanceof TrueCompletion and
first(super.getRightOperand(), succ)
or
// Post-order: flow from last element of left operand to element itself
last(super.getLeftOperand(), pred, c) and
c instanceof FalseCompletion and
succ = this
or
// Post-order: flow from last element of right operand to element itself
last(super.getRightOperand(), pred, c) and
c instanceof NormalCompletion and
succ = this
}
}
private class LogicalOrExprTree extends PostOrderTree instanceof LogicalOrExpr {
final override predicate propagatesAbnormal(AstNode child) {
child in [super.getLeftOperand(), super.getRightOperand()]
}
final override predicate first(AstNode first) { first(super.getLeftOperand(), first) }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Flow from last element of left operand to first element of right operand
last(super.getLeftOperand(), pred, c) and
c instanceof FalseCompletion and
first(super.getRightOperand(), succ)
or
// Post-order: flow from last element of left operand to element itself
last(super.getLeftOperand(), pred, c) and
c instanceof TrueCompletion and
succ = this
or
// Post-order: flow from last element of right operand to element itself
last(super.getRightOperand(), pred, c) and
c instanceof NormalCompletion and
succ = this
}
}
private class NullCoalescingExprTree extends PostOrderTree instanceof NullCoalescingExpr {
final override predicate propagatesAbnormal(AstNode child) {
child in [super.getLeftOperand(), super.getRightOperand()]
}
final override predicate first(AstNode first) { first(super.getLeftOperand(), first) }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Flow from last element of left operand to first element of right operand
last(super.getLeftOperand(), pred, c) and
c.(NullnessCompletion).isNull() and
first(super.getRightOperand(), succ)
or
// Post-order: flow from last element of left operand to element itself
last(super.getLeftOperand(), pred, c) and
succ = this and
c instanceof NormalCompletion and
not c.(NullnessCompletion).isNull()
or
// Post-order: flow from last element of right operand to element itself
last(super.getRightOperand(), pred, c) and
c instanceof NormalCompletion and
succ = this
}
}
private class ConditionalExprTree extends PostOrderTree instanceof ConditionalExpr {
final override predicate propagatesAbnormal(AstNode child) {
child in [super.getCondition(), super.getThen(), super.getElse()]
}
final override predicate first(AstNode first) { first(super.getCondition(), first) }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Flow from last element of condition to first element of then branch
last(super.getCondition(), pred, c) and
c instanceof TrueCompletion and
first(super.getThen(), succ)
or
// Flow from last element of condition to first element of else branch
last(super.getCondition(), pred, c) and
c instanceof FalseCompletion and
first(super.getElse(), succ)
or
// Post-order: flow from last element of a branch to element itself
last([super.getThen(), super.getElse()], pred, c) and
c instanceof NormalCompletion and
succ = this
}
}
/**
* An assignment operation that has an expanded version. We use the expanded
* version in the control flow graph in order to get better data flow / taint
* tracking.
*/
private class AssignOperationWithExpandedAssignment extends ControlFlowTree instanceof AssignOperation
{
private Expr expanded;
AssignOperationWithExpandedAssignment() { expanded = this.getExpandedAssignment() }
final override predicate first(AstNode first) { first(expanded, first) }
final override predicate last(AstNode last, Completion c) { last(expanded, last, c) }
final override predicate propagatesAbnormal(AstNode child) { none() }
final override predicate succ(AstNode pred, AstNode succ, Completion c) { none() }
}
/** A conditionally qualified expression. */
private class ConditionallyQualifiedExpr extends PostOrderTree instanceof QualifiableExpr {
private Expr qualifier;
ConditionallyQualifiedExpr() { this.isConditional() and qualifier = getExprChild(this, 0) }
final override predicate propagatesAbnormal(AstNode child) { child = qualifier }
final override predicate first(AstNode first) { first(qualifier, first) }
pragma[nomagic]
private predicate lastQualifier(AstNode last, Completion c) { last(qualifier, last, c) }
final override predicate last(AstNode last, Completion c) {
PostOrderTree.super.last(last, c)
or
// Qualifier exits with a `null` completion
this.lastQualifier(last, c) and
c.(NullnessCompletion).isNull()
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
exists(int i |
last(getExprChild(this, i), pred, c) and
c instanceof NormalCompletion and
if i = 0 then c.(NullnessCompletion).isNonNull() else any()
|
// Post-order: flow from last element of last child to element itself
i = max(int j | exists(getExprChild(this, j))) and
succ = this
or
// Standard left-to-right evaluation
first(getExprChild(this, i + 1), succ)
)
}
}
private class ThrowExprTree extends PostOrderTree instanceof ThrowExpr {
final override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
final override predicate first(AstNode first) { first(super.getExpr(), first) }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
last(super.getExpr(), pred, c) and
c instanceof NormalCompletion and
succ = this
}
}
private class ObjectCreationTree extends ControlFlowTree instanceof ObjectCreation {
private Expr getObjectCreationArgument(int i) {
i >= 0 and
if super.hasInitializer()
then result = getExprChild(this, i + 1)
else result = getExprChild(this, i)
}
final override predicate propagatesAbnormal(AstNode child) {
child = this.getObjectCreationArgument(_)
}
final override predicate first(AstNode first) {
first(this.getObjectCreationArgument(0), first)
or
not exists(this.getObjectCreationArgument(0)) and
first = this
}
final override predicate last(AstNode last, Completion c) {
// Post-order: element itself (when no initializer)
last = this and
not super.hasInitializer() and
c.isValidFor(this)
or
// Last element of initializer
last(super.getInitializer(), last, c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Flow from last element of argument `i` to first element of argument `i+1`
exists(int i | last(this.getObjectCreationArgument(i), pred, c) |
first(this.getObjectCreationArgument(i + 1), succ) and
c instanceof NormalCompletion
)
or
// Flow from last element of last argument to self
exists(int last | last = max(int i | exists(this.getObjectCreationArgument(i))) |
last(this.getObjectCreationArgument(last), pred, c) and
succ = this and
c instanceof NormalCompletion
)
or
// Flow from self to first element of initializer
pred = this and
first(super.getInitializer(), succ) and
c instanceof SimpleCompletion
}
}
private class ArrayCreationTree extends ControlFlowTree instanceof ArrayCreation {
final override predicate propagatesAbnormal(AstNode child) {
child = super.getALengthArgument()
}
final override predicate first(AstNode first) {
// First element of first length argument
first(super.getLengthArgument(0), first)
or
// No length argument: element itself
not exists(super.getLengthArgument(0)) and
first = this
}
final override predicate last(AstNode last, Completion c) {
// Post-order: element itself (when no initializer)
last = this and
not super.hasInitializer() and
c.isValidFor(this)
or
// Last element of initializer
last(super.getInitializer(), last, c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Flow from self to first element of initializer
pred = this and
first(super.getInitializer(), succ) and
c instanceof SimpleCompletion
or
exists(int i |
last(super.getLengthArgument(i), pred, c) and
c instanceof SimpleCompletion
|
// Flow from last length argument to self
i = max(int j | exists(super.getLengthArgument(j))) and
succ = this
or
// Flow from one length argument to the next
first(super.getLengthArgument(i + 1), succ)
)
}
}
private class SwitchExprTree extends PostOrderTree, SwitchTree instanceof SwitchExpr {
final override predicate propagatesAbnormal(AstNode child) {
SwitchTree.super.propagatesAbnormal(child)
or
child = super.getACase()
}
final override predicate first(AstNode first) { first(super.getExpr(), first) }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
SwitchTree.super.succ(pred, succ, c)
or
last(super.getACase(), pred, c) and
succ = this and
c instanceof NormalCompletion
}
}
private class SwitchCaseExprTree extends PostOrderTree, CaseTree instanceof SwitchCaseExpr {
final override predicate first(AstNode first) { first(super.getPattern(), first) }
pragma[noinline]
private predicate lastNoMatch(AstNode last, ConditionalCompletion cc) {
last([super.getPattern(), super.getCondition()], last, cc) and
(cc.(MatchingCompletion).isNonMatch() or cc instanceof FalseCompletion)
}
final override predicate last(AstNode last, Completion c) {
PostOrderTree.super.last(last, c)
or
// Last case exists with a non-match
exists(SwitchExpr se, int i, ConditionalCompletion cc |
this = se.getCase(i) and
not super.matchesAll() and
not exists(se.getCase(i + 1)) and
this.lastNoMatch(last, cc) and
c =
any(NestedCompletion nc |
nc.getNestLevel() = 0 and
nc.getInnerCompletion() = cc and
nc.getOuterCompletion()
.(ThrowCompletion)
.getExceptionClass()
.hasFullyQualifiedName("System", "InvalidOperationException")
)
)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
CaseTree.super.succ(pred, succ, c)
or
last(super.getBody(), pred, c) and
succ = this and
c instanceof NormalCompletion
}
}
private class ConstructorInitializerTree extends PostOrderTree instanceof ConstructorInitializer {
private ControlFlowTree getChildNode(int i) { result = getExprChild(this, i) }
final override predicate propagatesAbnormal(AstNode child) { child = this.getChildNode(_) }
final override predicate first(AstNode first) {
first(this.getChildNode(0), first)
or
not exists(this.getChildNode(0)) and
first = this
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Post-order: flow from last element of last child to element itself
exists(int lst |
lst = max(int i | exists(this.getChildNode(i))) and
last(this.getChildNode(lst), pred, c) and
succ = this and
c instanceof NormalCompletion
)
or
// Standard left-to-right evaluation
exists(int i |
last(this.getChildNode(i), pred, c) and
c instanceof NormalCompletion and
first(this.getChildNode(i + 1), succ)
)
or
exists(ConstructorTree con, CompilationExt comp |
last(this, pred, c) and
con = super.getConstructor() and
comp = getCompilation(this.getFile()) and
c instanceof NormalCompletion
|
// Flow from constructor initializer to first member initializer
first(InitializerSplitting::constructorInitializeOrder(con, comp, 0), succ)
or
// Flow from constructor initializer to first element of constructor body
not exists(InitializerSplitting::constructorInitializeOrder(con, comp, _)) and
first(con.getBody(comp), succ)
)
}
}
private class NotPatternExprTree extends PostOrderTree instanceof NotPatternExpr {
final override predicate propagatesAbnormal(AstNode child) { child = super.getPattern() }
final override predicate first(AstNode first) { first(super.getPattern(), first) }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
succ = this and
last(super.getPattern(), pred, c) and
c instanceof NormalCompletion
}
}
private class AndPatternExprTree extends PostOrderTree instanceof AndPatternExpr {
final override predicate propagatesAbnormal(AstNode child) { child = super.getAnOperand() }
final override predicate first(AstNode first) { first(super.getLeftOperand(), first) }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Flow from last element of left operand to first element of right operand
last(super.getLeftOperand(), pred, c) and
c.(MatchingCompletion).getValue() = true and
first(super.getRightOperand(), succ)
or
// Post-order: flow from last element of left operand to element itself
last(super.getLeftOperand(), pred, c) and
c.(MatchingCompletion).getValue() = false and
succ = this
or
// Post-order: flow from last element of right operand to element itself
last(super.getRightOperand(), pred, c) and
c instanceof MatchingCompletion and
succ = this
}
}
private class OrPatternExprTree extends PostOrderTree instanceof OrPatternExpr {
final override predicate propagatesAbnormal(AstNode child) { child = super.getAnOperand() }
final override predicate first(AstNode first) { first(super.getLeftOperand(), first) }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Flow from last element of left operand to first element of right operand
last(super.getLeftOperand(), pred, c) and
c.(MatchingCompletion).getValue() = false and
first(super.getRightOperand(), succ)
or
// Post-order: flow from last element of left operand to element itself
last(super.getLeftOperand(), pred, c) and
c.(MatchingCompletion).getValue() = true and
succ = this
or
// Post-order: flow from last element of right operand to element itself
last(super.getRightOperand(), pred, c) and
c instanceof MatchingCompletion and
succ = this
}
}
}
private class RecursivePatternExprTree extends PostOrderTree instanceof RecursivePatternExpr {
private Expr getTypeExpr() {
result = super.getVariableDeclExpr()
or
not exists(super.getVariableDeclExpr()) and
result = super.getTypeAccess()
}
private PatternExpr getChildPattern() {
result = super.getPositionalPatterns()
or
result = super.getPropertyPatterns()
}
final override predicate propagatesAbnormal(AstNode child) { child = this.getChildPattern() }
final override predicate first(AstNode first) {
first(this.getTypeExpr(), first)
or
not exists(this.getTypeExpr()) and
first(this.getChildPattern(), first)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Flow from type test to child pattern
last(this.getTypeExpr(), pred, c) and
first(this.getChildPattern(), succ) and
c.(MatchingCompletion).getValue() = true
or
// Flow from type test to self
last(this.getTypeExpr(), pred, c) and
succ = this and
c.(MatchingCompletion).getValue() = false
or
// Flow from child pattern to self
last(this.getChildPattern(), pred, c) and
succ = this and
c instanceof MatchingCompletion
}
}
private class PositionalPatternExprTree extends PreOrderTree instanceof PositionalPatternExpr {
final override predicate propagatesAbnormal(AstNode child) { child = super.getPattern(_) }
final override predicate last(AstNode last, Completion c) {
last = this and
c.(MatchingCompletion).getValue() = false
or
last(super.getPattern(_), last, c) and
c.(MatchingCompletion).getValue() = false
or
exists(int lst |
last(super.getPattern(lst), last, c) and
not exists(super.getPattern(lst + 1))
)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Flow from self to first pattern
pred = this and
c.(MatchingCompletion).getValue() = true and
first(super.getPattern(0), succ)
or
// Flow from one pattern to the next
exists(int i |
last(super.getPattern(i), pred, c) and
c.(MatchingCompletion).getValue() = true and
first(super.getPattern(i + 1), succ)
)
}
}
private class PropertyPatternExprExprTree extends PostOrderTree instanceof PropertyPatternExpr {
final override predicate propagatesAbnormal(AstNode child) { child = super.getPattern(_) }
final override predicate first(AstNode first) {
first(super.getPattern(0), first)