-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathAssignable.qll
More file actions
762 lines (660 loc) · 24.1 KB
/
Assignable.qll
File metadata and controls
762 lines (660 loc) · 24.1 KB
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
/**
* Provides classes for working with assignables.
*/
import csharp
private import semmle.code.csharp.dataflow.internal.SsaImpl as SsaImpl
/**
* An assignable, that is, an element that can be assigned to. Either a
* variable (`Variable`), a property (`Property`), an indexer (`Indexer`),
* or an event (`Event`).
*/
class Assignable extends Declaration, @assignable {
/** Gets the type of this assignable. */
Type getType() { none() }
/** Gets the annotated type of this assignable. */
final AnnotatedType getAnnotatedType() { result.appliesTo(this) }
/** Gets an access to this assignable. */
AssignableAccess getAnAccess() { result.getTarget() = this }
/** Gets an expression assigned to this assignable, if any. */
Expr getAnAssignedValue() {
result = any(AssignableDefinition def | def.getTarget() = this).getSource()
}
}
/**
* An assignable that is also a member. Either a field (`Field`), a
* property (`Property`), an indexer (`Indexer`), or an event (`Event`).
*/
class AssignableMember extends Member, Assignable, Attributable {
override AssignableMemberAccess getAnAccess() { result = Assignable.super.getAnAccess() }
override string toString() { result = Assignable.super.toString() }
}
/**
* An access to an assignable that is also a member. Either a field access
* (`FieldAccess`), a property access (`PropertyAccess`), an indexer access
* (`IndexerAccess`), or an event access (`EventAccess`).
*/
class AssignableMemberAccess extends MemberAccess, AssignableAccess {
override AssignableMember getTarget() { result = AssignableAccess.super.getTarget() }
}
private predicate nameOfChild(NameOfExpr noe, Expr child) {
child = noe
or
exists(Expr mid | nameOfChild(noe, mid) | child = mid.getAChildExpr())
}
/**
* An access to an assignable that reads the underlying value. Either a
* variable read (`VariableRead`), a property read (`PropertyRead`), an
* indexer read (`IndexerRead`), or an event read (`EventRead`).
*
* For example, the last occurrence of `Length` in
*
* ```csharp
* class C {
* int Length;
*
* int GetLength() {
* return Length;
* }
* }
* ```
*/
class AssignableRead extends AssignableAccess {
AssignableRead() {
(
not this instanceof AssignableWrite
or
this = any(AssignableDefinitions::MutationDefinition def).getTargetAccess()
or
this.isRefArgument()
or
this = any(AssignableDefinitions::AddressOfDefinition def).getTargetAccess()
or
this = any(AssignableDefinitions::AssignOperationDefinition def).getTargetAccess()
) and
not nameOfChild(_, this)
}
pragma[noinline]
private ControlFlowNode getAnAdjacentReadSameVar() {
SsaImpl::adjacentReadPairSameVar(_, this.getAControlFlowNode(), result)
}
/**
* Gets a next read of the same underlying assignable. That is, a read
* that can be reached from this read without passing through any other reads,
* and which is guaranteed to read the same value. Example:
*
* ```csharp
* int Field;
*
* void SetField(int i) {
* this.Field = i;
* Use(this.Field);
* if (i > 0)
* this.Field = i - 1;
* else if (i < 0)
* SetField(1);
* Use(this.Field);
* Use(this.Field);
* }
* ```
*
* - The read of `i` on line 6 is next to the read on line 4.
* - The reads of `i` on lines 7 and 8 are next to the read on line 6.
* - The read of `this.Field` on line 11 is next to the read on line 10.
*/
pragma[nomagic]
AssignableRead getANextRead() {
forex(ControlFlowNode cfn | cfn = result.getAControlFlowNode() |
cfn = this.getAnAdjacentReadSameVar()
)
}
}
/**
* An access to an assignable that updates the underlying value. Either a
* variable write (`VariableWrite`), a property write (`PropertyWrite`),
* an indexer write (`IndexerWrite`), or an event write (`EventWrite`).
*
* For example, the last occurrence of `Length` in
*
* ```csharp
* class C {
* int Length;
*
* void SetLength(int length) {
* Length = length;
* }
* }
* ```
*/
class AssignableWrite extends AssignableAccess {
AssignableWrite() { exists(AssignableDefinition def | def.getTargetAccess() = this) }
}
/**
* A `ref` argument in a call.
*
* All predicates in this class deliberately do not use the `Call` class, or any
* subclass thereof, as that results in too conservative negative recursion
* compilation errors.
*/
private class RefArg extends AssignableAccess {
private Expr call;
private int position;
RefArg() {
this.isRefArgument() and
this = call.getChildExpr(position) and
(
call instanceof @method_invocation_expr
or
call instanceof @delegate_invocation_expr
or
call instanceof @local_function_invocation_expr
or
call instanceof @object_creation_expr
)
}
pragma[noinline]
Parameter getAParameter(string name) {
exists(Callable callable | result = callable.getAParameter() |
expr_call(call, callable) and
result.getName() = name
)
}
/** Gets the parameter that this argument corresponds to. */
private Parameter getParameter() {
exists(string name | result = this.getAParameter(name) |
// Appears in the positional part of the call
result.getPosition() = position and
not exists(this.getExplicitArgumentName())
or
// Appears in the named part of the call
name = this.getExplicitArgumentName()
)
}
private Callable getUnboundDeclarationTarget(Parameter p) {
p = this.getParameter().getUnboundDeclaration() and
result.getAParameter() = p
}
/**
* Holds if the assignment to this `ref` argument via parameter `p` is
* analyzable. That is, the target callable is non-overridable and from
* source.
*/
predicate isAnalyzable(Parameter p) {
exists(Callable callable | callable = this.getUnboundDeclarationTarget(p) |
not callable.(Overridable).isOverridableOrImplementable() and
callable.hasBody()
)
}
/** Gets an assignment to analyzable parameter `p`. */
AssignableDefinition getAnAnalyzableRefDef(Parameter p) {
this.isAnalyzable(p) and
result.getTarget() = p and
not result = TImplicitParameterDefinition(_)
}
/**
* Holds if this `ref` assignment is *not* analyzable. Equivalent with
* `not this.isAnalyzable(_)`, but avoids negative recursion.
*/
private predicate isNonAnalyzable() {
call instanceof @delegate_invocation_expr
or
exists(Callable callable | callable = this.getUnboundDeclarationTarget(_) |
callable.(Virtualizable).isOverridableOrImplementable() or
not callable.hasBody()
)
}
/** Holds if this `ref` access is a potential assignment. */
predicate isPotentialAssignment() {
this.isNonAnalyzable() or
exists(this.getAnAnalyzableRefDef(_))
}
}
/** INTERNAL: Do not use. */
module AssignableInternal {
private predicate tupleAssignmentDefinition(AssignExpr ae, Expr leaf) {
exists(TupleExpr te |
ae.getLValue() = te and
te.getAnArgument+() = leaf and
// `leaf` is either an assignable access or a local variable declaration
not leaf instanceof TupleExpr
)
}
/**
* Holds if `ae` is a tuple assignment, and `left` is a sub expression
* on the left-hand side of the assignment, with corresponding
* right-hand side `right`.
*/
private predicate tupleAssignmentPair(AssignExpr ae, Expr left, Expr right) {
tupleAssignmentDefinition(ae, _) and
left = ae.getLValue() and
right = ae.getRValue()
or
exists(TupleExpr l, TupleExpr r, int i | tupleAssignmentPair(ae, l, r) |
left = l.getArgument(i) and
right = r.getArgument(i)
)
}
// Not defined by dispatch in order to avoid too conservative negative recursion error
Expr getExpr(AssignableDefinition def) {
def = TAssignmentDefinition(result)
or
def = TTupleAssignmentDefinition(result, _)
or
def = TOutRefDefinition(any(AssignableAccess aa | result = aa.getParent()))
or
def = TMutationDefinition(result)
or
def = TLocalVariableDefinition(result)
or
def = TAddressOfDefinition(result)
or
def = TPatternDefinition(result)
or
def = TAssignOperationDefinition(result)
}
/** A local variable declaration at the top-level of a pattern. */
class TopLevelPatternDecl extends LocalVariableDeclExpr {
private PatternMatch pm;
TopLevelPatternDecl() { this = pm.getPattern().(BindingPatternExpr).getVariableDeclExpr() }
PatternMatch getMatch() { result = pm }
}
cached
private module Cached {
cached
newtype TAssignableDefinition =
TAssignmentDefinition(Assignment a) {
not a.getLValue() instanceof TupleExpr and
not a instanceof AssignCallOperation and
not a instanceof AssignCoalesceExpr
} or
TTupleAssignmentDefinition(AssignExpr ae, Expr leaf) { tupleAssignmentDefinition(ae, leaf) } or
TOutRefDefinition(AssignableAccess aa) {
aa.isOutArgument()
or
aa.(RefArg).isPotentialAssignment()
} or
TMutationDefinition(MutatorOperation mo) or
TLocalVariableDefinition(LocalVariableDeclExpr lvde) {
not lvde.hasInitializer() and
not exists(getTupleSource(TTupleAssignmentDefinition(_, lvde))) and
not lvde instanceof TopLevelPatternDecl and
not lvde.isOutArgument()
} or
TImplicitParameterDefinition(Parameter p) {
exists(Callable c | p = c.getAParameter() |
c.hasBody()
or
// Same as `c.(Constructor).hasInitializer()`, but avoids negative recursion warning
c.getAChildExpr() instanceof @constructor_init_expr
)
} or
TAddressOfDefinition(AddressOfExpr aoe) or
TPatternDefinition(TopLevelPatternDecl tlpd) or
TAssignOperationDefinition(AssignOperation ao) {
ao instanceof AssignCallOperation or
ao instanceof AssignCoalesceExpr
}
/**
* Gets the source expression assigned in tuple definition `def`, if any.
*/
cached
Expr getTupleSource(TTupleAssignmentDefinition def) {
exists(AssignExpr ae, Expr leaf | def = TTupleAssignmentDefinition(ae, leaf) |
tupleAssignmentPair(ae, leaf, result)
)
}
// Not defined by dispatch in order to avoid too conservative negative recursion error
cached
Assignable getTarget(AssignableDefinition def) {
result = def.getTargetAccess().getTarget()
or
exists(Expr leaf | def = TTupleAssignmentDefinition(_, leaf) |
result = leaf.(LocalVariableDeclExpr).getVariable()
)
or
def = any(AssignableDefinitions::ImplicitParameterDefinition p | result = p.getParameter())
or
def =
any(AssignableDefinitions::LocalVariableDefinition decl |
result = decl.getDeclaration().getVariable()
)
or
def =
any(AssignableDefinitions::PatternDefinition pd | result = pd.getDeclaration().getVariable())
or
def = any(AssignableDefinitions::InitializerDefinition init | result = init.getAssignable())
}
// Not defined by dispatch in order to avoid too conservative negative recursion error
cached
AssignableAccess getTargetAccess(AssignableDefinition def) {
def = TAssignmentDefinition(any(Assignment a | a.getLValue() = result))
or
def = TTupleAssignmentDefinition(_, result)
or
def = TOutRefDefinition(result)
or
def = TMutationDefinition(any(MutatorOperation mo | mo.getOperand() = result))
or
def = TAddressOfDefinition(any(AddressOfExpr aoe | aoe.getOperand() = result))
or
def = TAssignOperationDefinition(any(AssignOperation ao | ao.getLeftOperand() = result))
}
/**
* Gets the argument for the implicit `value` parameter in accessor access
* `a`, if any.
*/
cached
Expr getAccessorCallValueArgument(AccessorCall ac) {
exists(AssignExpr ae | tupleAssignmentDefinition(ae, ac) |
tupleAssignmentPair(ae, ac, result)
)
or
exists(Assignment ass | ac = ass.getLValue() |
result = ass.getRValue() and
not ass instanceof AssignOperation
)
or
exists(AssignOperation ao | ac = ao.getLeftOperand() | result = ao)
}
}
import Cached
}
private import AssignableInternal
/**
* An assignable definition.
*
* Either a direct non-tuple assignment (`AssignableDefinitions::AssignmentDefinition`),
* a direct tuple assignment (`AssignableDefinitions::TupleAssignmentDefinition`),
* an indirect `out`/`ref` assignment (`AssignableDefinitions::OutRefDefinition`),
* a mutation update (`AssignableDefinitions::MutationDefinition`), a local variable
* declaration without an initializer (`AssignableDefinitions::LocalVariableDefinition`),
* an implicit parameter definition (`AssignableDefinitions::ImplicitParameterDefinition`),
* an address-of definition (`AssignableDefinitions::AddressOfDefinition`), a pattern
* definition (`AssignableDefinitions::PatternDefinition`), or a compound assignment
* operation definition (`AssignableDefinitions::AssignOperationDefinition`)
*/
class AssignableDefinition extends TAssignableDefinition {
/**
* DEPRECATED: Use `this.getExpr().getAControlFlowNode()` instead.
*
* Gets a control flow node that updates the targeted assignable when
* reached.
*
* Multiple definitions may relate to the same control flow node. For example,
* the definitions of `x` and `y` in `M(out x, out y)` and `(x, y) = (0, 1)`
* relate to the same call to `M` and assignment node, respectively.
*/
deprecated ControlFlowNode getAControlFlowNode() { result = this.getExpr().getAControlFlowNode() }
/**
* Gets the underlying expression that updates the targeted assignable when
* reached, if any.
*
* Not all definitions have an associated expression, for example implicit
* parameter definitions.
*/
final Expr getExpr() { result = getExpr(this) }
/**
* Gets the underlying element associated with this definition. This is either
* an expression or a parameter.
*/
Element getElement() { result = this.getExpr() }
/** Gets the enclosing callable of this definition. */
Callable getEnclosingCallable() { result = this.getExpr().getEnclosingCallable() }
/**
* Gets the assigned expression, if any. For example, the expression assigned
* in `x = 0` is `0`. The value may not always exists, for example in assignments
* via `out`/`ref` parameters.
*/
Expr getSource() { none() }
/** Gets the assignable being defined. */
final Assignable getTarget() { result = getTarget(this) }
/**
* Gets the access used in the definition of the underlying assignable,
* if any. Local variable declarations and implicit parameter definitions
* are the only definitions without associated accesses.
*/
final AssignableAccess getTargetAccess() { result = getTargetAccess(this) }
/**
* Holds if this definition is guaranteed to update the targeted assignable.
* The only potentially uncertain definitions are `ref` assignments.
*/
predicate isCertain() { any() }
/**
* Gets a first read of the same underlying assignable. That is, a read
* that can be reached from this definition without passing through any other
* reads, and which is guaranteed to read the value assigned in this
* definition. Example:
*
* ```csharp
* int Field;
*
* void SetField(int i) {
* this.Field = i;
* Use(this.Field);
* if (i > 0)
* this.Field = i - 1;
* else if (i < 0)
* SetField(1);
* Use(this.Field);
* Use(this.Field);
* }
* ```
*
* - The read of `i` on line 4 is first read of the implicit parameter definition
* on line 3.
* - The read of `this.Field` on line 5 is a first read of the definition on line 4.
*
* Subsequent reads can be found by following the steps defined by
* `AssignableRead.getANextRead()`.
*/
pragma[nomagic]
AssignableRead getAFirstRead() {
forex(ControlFlowNode cfn | cfn = result.getAControlFlowNode() |
exists(Ssa::ExplicitDefinition def | result = def.getAFirstReadAtNode(cfn) |
this = def.getADefinition()
)
or
exists(Ssa::ImplicitParameterDefinition def | result = def.getAFirstReadAtNode(cfn) |
this.(AssignableDefinitions::ImplicitParameterDefinition).getParameter() =
def.getParameter()
)
)
}
/** Gets a textual representation of this assignable definition. */
string toString() { none() }
/** Gets the location of this assignable definition. */
Location getLocation() { result = this.getExpr().getLocation() }
}
/** Provides different types of `AssignableDefinition`s. */
module AssignableDefinitions {
/**
* A non-tuple definition by direct assignment, for example `x = 0`.
*/
class AssignmentDefinition extends AssignableDefinition, TAssignmentDefinition {
Assignment a;
AssignmentDefinition() { this = TAssignmentDefinition(a) }
/** Gets the underlying assignment. */
Assignment getAssignment() { result = a }
override Expr getSource() {
result = a.getRValue() and
not a instanceof AddOrRemoveEventExpr
}
override string toString() { result = a.toString() }
}
/**
* A tuple definition by direct assignment, for example the definition of `x`
* in `(x, y) = (0, 1)`.
*/
class TupleAssignmentDefinition extends AssignableDefinition, TTupleAssignmentDefinition {
AssignExpr ae;
Expr leaf;
TupleAssignmentDefinition() { this = TTupleAssignmentDefinition(ae, leaf) }
/** Gets the underlying assignment. */
AssignExpr getAssignment() { result = ae }
/** Gets the leaf expression. */
Expr getLeaf() { result = leaf }
/**
* Gets the evaluation order of this definition among the other definitions
* in the compound tuple assignment. For example, in `(x, (y, z)) = ...` the
* orders of the definitions of `x`, `y`, and `z` are 0, 1, and 2, respectively.
*/
int getEvaluationOrder() {
leaf =
rank[result + 1](Expr leaf0 |
exists(TTupleAssignmentDefinition(ae, leaf0))
|
leaf0 order by leaf0.getLocation().getStartLine(), leaf0.getLocation().getStartColumn()
)
}
override Expr getSource() {
result = getTupleSource(this) // need not exist
}
override string toString() { result = ae.toString() }
}
/** Holds if a node in basic block `bb` assigns to `ref` parameter `p` via definition `def`. */
private predicate basicBlockRefParamDef(BasicBlock bb, Parameter p, AssignableDefinition def) {
def = any(RefArg arg).getAnAnalyzableRefDef(p) and
bb.getANode() = def.getExpr().getAControlFlowNode()
}
/**
* Holds if `p` is an analyzable `ref` parameter and there is a path from the
* entry point of `p`'s callable to basic block `bb` without passing through
* any assignments to `p`.
*/
pragma[nomagic]
private predicate parameterReachesWithoutDef(Parameter p, BasicBlock bb) {
forall(AssignableDefinition def | basicBlockRefParamDef(bb, p, def) |
isUncertainRefCall(def.getTargetAccess())
) and
(
any(RefArg arg).isAnalyzable(p) and
p.getCallable().getEntryPoint() = bb.getFirstNode()
or
exists(BasicBlock mid | parameterReachesWithoutDef(p, mid) | bb = mid.getASuccessor())
)
}
/**
* Holds if the `ref` assignment to `arg` via call `c` is uncertain.
*/
// Not in the cached module `Cached`, as that would introduce a dependency
// on the CFG construction, and effectively collapse too many stages into one
cached
predicate isUncertainRefCall(RefArg arg) {
arg.isPotentialAssignment() and
exists(BasicBlock bb, Parameter p | arg.isAnalyzable(p) |
parameterReachesWithoutDef(p, bb) and
bb.getLastNode() = p.getCallable().getExitPoint()
)
}
/**
* A definition via an `out`/`ref` argument in a call, for example
* `M(out x, ref y)`.
*/
class OutRefDefinition extends AssignableDefinition, TOutRefDefinition {
AssignableAccess aa;
OutRefDefinition() { this = TOutRefDefinition(aa) }
/** Gets the underlying call. */
Call getCall() { result.getAnArgument() = aa }
private int getPosition() { aa = this.getCall().getArgument(result) }
/**
* Gets the index of this definition among the other definitions in the
* `out`/`ref` assignment. For example, in `M(out x, ref y)` the index of
* the definitions of `x` and `y` are 0 and 1, respectively.
*/
int getIndex() {
this =
rank[result + 1](OutRefDefinition def |
def.getCall() = this.getCall()
|
def order by def.getPosition()
)
}
override predicate isCertain() { not isUncertainRefCall(this.getTargetAccess()) }
override string toString() { result = aa.toString() }
override Location getLocation() { result = aa.getLocation() }
}
/**
* A definition by mutation, for example `x++`.
*/
class MutationDefinition extends AssignableDefinition, TMutationDefinition {
MutatorOperation mo;
MutationDefinition() { this = TMutationDefinition(mo) }
/** Gets the underlying mutator operation. */
MutatorOperation getMutatorOperation() { result = mo }
override string toString() { result = mo.toString() }
}
/**
* A local variable definition without an initializer, for example `int i`.
*/
class LocalVariableDefinition extends AssignableDefinition, TLocalVariableDefinition {
LocalVariableDeclExpr lvde;
LocalVariableDefinition() { this = TLocalVariableDefinition(lvde) }
/** Gets the underlying local variable declaration. */
LocalVariableDeclExpr getDeclaration() { result = lvde }
override string toString() { result = lvde.toString() }
}
/**
* An implicit parameter definition at the entry point of the
* associated callable.
*/
class ImplicitParameterDefinition extends AssignableDefinition, TImplicitParameterDefinition {
Parameter p;
ImplicitParameterDefinition() { this = TImplicitParameterDefinition(p) }
/** Gets the underlying parameter. */
Parameter getParameter() { result = p }
deprecated override ControlFlowNode getAControlFlowNode() {
result = p.getCallable().getEntryPoint()
}
override Parameter getElement() { result = p }
override Callable getEnclosingCallable() { result = p.getCallable() }
override string toString() { result = p.toString() }
override Location getLocation() { result = this.getTarget().getLocation() }
}
/**
* An indirect address-of definition, for example `&x`.
*/
class AddressOfDefinition extends AssignableDefinition, TAddressOfDefinition {
AddressOfExpr aoe;
AddressOfDefinition() { this = TAddressOfDefinition(aoe) }
/** Gets the underlying address-of expression. */
AddressOfExpr getAddressOf() { result = aoe }
override string toString() { result = aoe.toString() }
}
/**
* A local variable definition in a pattern, for example `x is int i`.
*/
class PatternDefinition extends AssignableDefinition, TPatternDefinition {
TopLevelPatternDecl tlpd;
PatternDefinition() { this = TPatternDefinition(tlpd) }
/** Gets the element matches against this pattern. */
PatternMatch getMatch() { result = tlpd.getMatch() }
/** Gets the underlying local variable declaration. */
LocalVariableDeclExpr getDeclaration() { result = tlpd }
override Expr getSource() { result = this.getMatch().getExpr() }
override string toString() { result = this.getDeclaration().toString() }
}
/**
* An initializer definition for a field or a property, for example
* line 2 in
*
* ```csharp
* class C {
* int Field = 0;
* }
* ```
*/
class InitializerDefinition extends AssignmentDefinition {
private Assignable fieldOrProp;
InitializerDefinition() { this.getAssignment().getParent() = fieldOrProp }
/** Gets the assignable (field or property) being initialized. */
Assignable getAssignable() { result = fieldOrProp }
}
/**
* A definition by a compound assignment operation, for example `x += y`.
*/
class AssignOperationDefinition extends AssignableDefinition, TAssignOperationDefinition {
AssignOperation ao;
AssignOperationDefinition() { this = TAssignOperationDefinition(ao) }
override Expr getSource() { result = ao }
override string toString() { result = ao.toString() }
}
}