forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVariables.qll
More file actions
961 lines (812 loc) · 28.5 KB
/
Copy pathVariables.qll
File metadata and controls
961 lines (812 loc) · 28.5 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
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
/** Provides classes for modeling program variables. */
import javascript
/** A scope in which variables can be declared. */
class Scope extends @scope {
/** Gets a textual representation of this element. */
string toString() { none() }
/** Gets the scope in which this scope is nested, if any. */
Scope getOuterScope() { scopenesting(this, result) }
/** Gets a scope nested in this one, if any. */
Scope getAnInnerScope() { result.getOuterScope() = this }
/** Gets the program element this scope is associated with, if any. */
ASTNode getScopeElement() { scopenodes(result, this) }
/** Gets the location of the program element this scope is associated with, if any. */
Location getLocation() { result = this.getScopeElement().getLocation() }
/** Gets a variable declared in this scope. */
Variable getAVariable() { result.getScope() = this }
/** Gets the variable with the given name declared in this scope. */
Variable getVariable(string name) {
result = this.getAVariable() and
result.getName() = name
}
}
/**
* A program element that induces a scope.
*/
class ScopeElement extends ASTNode {
Scope s;
ScopeElement() { this = s.getScopeElement() }
/** Gets the scope induced by this element. */
Scope getScope() { result = s }
}
/** The global scope. */
class GlobalScope extends Scope, @global_scope {
override string toString() { result = "global scope" }
}
/** A local scope, that is, a scope that is not the global scope. */
class LocalScope extends Scope {
LocalScope() { not this instanceof GlobalScope }
}
/**
* A scope induced by a Node.js or ES2015 module
*/
class ModuleScope extends Scope, @module_scope {
/** Gets the module that induces this scope. */
Module getModule() { result = this.getScopeElement() }
override string toString() { result = "module scope" }
}
/** A scope induced by a function. */
class FunctionScope extends Scope, @function_scope {
/** Gets the function that induces this scope. */
Function getFunction() { result = this.getScopeElement() }
override string toString() { result = "function scope" }
}
/** A scope induced by a catch clause. */
class CatchScope extends Scope, @catch_scope {
/** Gets the catch clause that induces this scope. */
CatchClause getCatchClause() { result = this.getScopeElement() }
override string toString() { result = "catch scope" }
}
/** A scope induced by a block of statements. */
class BlockScope extends Scope, @block_scope {
/** Gets the block of statements that induces this scope. */
BlockStmt getBlock() { result = this.getScopeElement() }
override string toString() { result = "block scope" }
}
/** A scope induced by a `for` statement. */
class ForScope extends Scope, @for_scope {
/** Gets the `for` statement that induces this scope. */
ForStmt getLoop() { result = this.getScopeElement() }
override string toString() { result = "for scope" }
}
/** A scope induced by a `for`-`in` or `for`-`of` statement. */
class ForInScope extends Scope, @for_in_scope {
/** Gets the `for`-`in` or `for`-`of` statement that induces this scope. */
EnhancedForLoop getLoop() { result = this.getScopeElement() }
override string toString() { result = "for-in scope" }
}
/** A scope induced by a comprehension block. */
class ComprehensionBlockScope extends Scope, @comprehension_block_scope {
/** Gets the comprehension block that induces this scope. */
ComprehensionBlock getComprehensionBlock() { result = this.getScopeElement() }
override string toString() { result = "comprehension block scope" }
}
/**
* The lexical scope induced by a TypeScript namespace declaration.
*
* This scope is specific to a single syntactic declaration of a namespace,
* and currently does not include variables exported from other declarations
* of the same namespace.
*/
class NamespaceScope extends Scope, @namespace_scope {
override string toString() { result = "namespace scope" }
}
/** A variable declared in a scope. */
class Variable extends @variable, LexicalName {
/** Gets the name of this variable. */
override string getName() { variables(this, result, _) }
/** Gets the scope this variable is declared in. */
override Scope getScope() { variables(this, _, result) }
/** Holds if this is a global variable. */
predicate isGlobal() { this.getScope() instanceof GlobalScope }
/**
* Holds if this is a variable exported from a TypeScript namespace.
*
* Note that such variables are also considered local for the time being.
*/
predicate isNamespaceExport() {
this.getScope() instanceof NamespaceScope and
exists(ExportNamedDeclaration decl | decl.getADecl().getVariable() = this)
}
/**
* Holds if this is a local variable.
*
* Parameters and `arguments` variables are considered to be local.
*/
predicate isLocal() { not this.isGlobal() }
/** Holds if this variable is a parameter. */
predicate isParameter() { exists(Parameter p | p.getAVariable() = this) }
/** Gets a reference to this variable. */
VarRef getAReference() { result.getVariable() = this }
/** Gets an access to this variable. */
VarAccess getAnAccess() { result.getVariable() = this }
/** Gets a declaration declaring this variable, if any. */
VarDecl getADeclaration() { result.getVariable() = this }
/** Gets a declaration statement declaring this variable, if any. */
DeclStmt getADeclarationStatement() {
result.getADecl().getBindingPattern().getAVariable() = this
}
/** Gets a definition for this variable. */
VarDef getADefinition() { result.getAVariable() = this }
/** Gets an expression that is directly stored in this variable. */
Expr getAnAssignedExpr() {
// result is an expression that this variable is initialized to
exists(VariableDeclarator vd | vd.getBindingPattern().(VarDecl).getVariable() = this |
result = vd.getInit()
)
or
// if this variable represents a function binding, return the function
exists(FunctionExpr fn | fn.getVariable() = this | result = fn)
or
// there is an assignment to this variable
exists(Assignment assgn | assgn.getLhs() = this.getAnAccess() and assgn.getRhs() = result)
}
/**
* Holds if this variable is captured in the closure of a nested function.
*
* Global variables are always considered to be captured.
*/
predicate isCaptured() {
this instanceof GlobalVariable or
this.getAnAccess().getContainer().getFunctionBoundary() !=
this.(LocalVariable).getDeclaringContainer().getFunctionBoundary()
}
/** Holds if there is a declaration of this variable in `tl`. */
predicate declaredIn(TopLevel tl) { this.getADeclaration().getTopLevel() = tl }
/** Gets a textual representation of this element. */
override string toString() { result = this.getName() }
override DeclarationSpace getDeclarationSpace() { result = "variable" }
}
/** An `arguments` variable of a function. */
class ArgumentsVariable extends Variable {
ArgumentsVariable() { is_arguments_object(this) }
override FunctionScope getScope() { result = Variable.super.getScope() }
/** Gets the function declaring this 'arguments' variable. */
Function getFunction() { result = this.getScope().getFunction() }
}
/**
* An identifier that refers to a variable, either in a declaration or in a variable access.
*
* Examples:
*
* ```
* function f(o) { // `f` and `o` are variable references
* var w = 0, { x : y, z } = o; // `w`, `y`, `z` and `o` are variable references; `x` is not
* o = null; // `o` is a variable reference; `null` is not
* }
* ```
*/
class VarRef extends @varref, Identifier, BindingPattern, LexicalRef {
/** Gets the variable this identifier refers to. */
override Variable getVariable() { none() } // Overriden in VarAccess and VarDecl
override string getName() { result = Identifier.super.getName() }
override VarRef getABindingVarRef() { result = this }
override predicate isImpure() { none() }
override Expr getUnderlyingReference() { result = this }
override string getAPrimaryQlClass() { result = "VarRef" }
}
/**
* An identifier that refers to a variable in a non-declaring position.
*
* Examples:
*
* ```
* function f(o) {
* var w = 0, { x : y, z } = o; // `o` is a variable access
* o = null; // `o` is a variable access
* }
* ```
*/
class VarAccess extends @varaccess, VarRef, LexicalAccess {
/**
* Gets the variable this identifier refers to.
*
* When analyzing TypeScript code, a variable may spuriously be resolved as a
* global due to incomplete modeling of exported variables in namespaces.
*/
override Variable getVariable() { bind(this, result) }
override predicate isLValue() {
exists(Assignment assgn | assgn.getTarget() = this)
or
exists(UpdateExpr upd | upd.getOperand().getUnderlyingReference() = this)
or
exists(EnhancedForLoop efl | efl.getIterator() = this)
or
exists(BindingPattern p | this = p.getABindingVarRef() and p.isLValue())
}
override Variable getAVariable() { result = this.getVariable() }
}
/**
* An identifier that occurs in a named export declaration.
*
* Example:
*
* ```
* export { A };
* ```
*
* Such an identifier may refer to a variable, type, or namespace, or a combination of these.
*/
class ExportVarAccess extends VarAccess, @export_varaccess {
/** Gets the type being exported, if this identifier refers to a type. */
LocalTypeName getLocalTypeName() { result.getAnAccess() = this }
/** Gets the namespace being exported, if this identifier refers to a namespace. */
LocalNamespaceName getLocalNamespaceName() { result.getAnAccess() = this }
}
/** A global variable. */
class GlobalVariable extends Variable {
GlobalVariable() { this.isGlobal() }
}
/** A local variable or a parameter. */
class LocalVariable extends Variable {
LocalVariable() { this.isLocal() }
/**
* Gets the function or toplevel in which this variable is declared;
* `arguments` variables are taken to be implicitly declared in the function
* to which they belong.
*
* Note that for a function expression `function f() { ... }` the variable
* `f` is declared in the function itself, while for a function statement
* it is declared in the enclosing container.
*/
StmtContainer getDeclaringContainer() {
this = result.getScope().getAVariable()
or
exists(VarDecl d | d = this.getADeclaration() |
if d = any(FunctionDeclStmt fds).getIdentifier()
then
exists(FunctionDeclStmt fds | d = fds.getIdentifier() |
result = fds.getEnclosingContainer()
)
else result = d.getContainer()
)
}
/**
* Gets the location of a declaration of this variable.
*
* If the variable has one or more declarations, the location of the first declaration is used.
* If the variable has no declaration, the entry point of its declaring container is used.
*/
Location getLocation() {
result =
min(Location loc |
loc = this.getADeclaration().getLocation()
|
loc order by loc.getStartLine(), loc.getStartColumn()
)
or
not exists(this.getADeclaration()) and
result = this.getDeclaringContainer().getEntry().getLocation()
}
}
/** A local variable that is not captured. */
class PurelyLocalVariable extends LocalVariable {
PurelyLocalVariable() { not this.isCaptured() }
}
/**
* An identifier that refers to a global variable.
*
* Examples:
*
* ```
* NaN
* undefined
* ```
*/
class GlobalVarAccess extends VarAccess {
GlobalVarAccess() { this.getVariable().isGlobal() }
}
/**
* A binding pattern, that is, either an identifier or a destructuring pattern.
*
* Examples:
*
* ```
* function f(x, { y: z }, ...rest) { // `x`, `{ y: z }`, `z` and `rest` are binding patterns
* var [ a, b ] = rest; // `[ a, b ]`, `a` and `b` are binding patterns
* var c; // `c` is a binding pattern
* }
* ```
*
* Binding patterns can appear on the left hand side of assignments or in
* variable or parameter declarations.
*/
class BindingPattern extends @pattern, Expr {
/** Gets the name of this binding pattern if it is an identifier. */
string getName() { none() }
/** Gets the variable this binding pattern refers to if it is an identifier. */
Variable getVariable() { none() }
/** Gets a variable reference in binding position within this pattern. */
VarRef getABindingVarRef() { none() }
/** Gets a variable bound by this pattern. */
Variable getAVariable() { result = this.getABindingVarRef().getVariable() }
/** Holds if this pattern appears in an l-value position. */
predicate isLValue() { any() }
/**
* Returns the type annotation for this variable or pattern, if any.
*
* Only the outermost part of a binding pattern can have a type annotation.
* For instance, in the declaration,
*<pre>
* var {x}: Point
* </pre>
* the variable `x` has no type annotation, whereas the pattern `{x}` has the type
* annotation `Point`.
*/
TypeAnnotation getTypeAnnotation() {
exists(VariableDeclarator decl | decl.getBindingPattern() = this |
result = decl.getTypeAnnotation()
)
// note: Parameter overrides this to handle the parameter case
}
}
private class TDestructuringPattern = @array_pattern or @object_pattern;
/**
* A destructuring pattern, that is, either an array pattern or an object pattern.
*
* Examples:
*
* ```
* function f(x, { y: z }, ...rest) { // `{ y: z }` is a destructuring pattern
* var [ a, b ] = rest; // `[ a, b ]` is a destructuring pattern
* var c;
* }
* ```
*/
class DestructuringPattern extends TDestructuringPattern, BindingPattern {
/** Gets the rest pattern of this destructuring pattern, if any. */
Expr getRest() { none() } // Overridden in subtypes.
}
/**
* An identifier that declares a variable.
*
* Examples:
*
* ```
* function f(o) { // `f` and `o` are variable declarations
* var w = 0, { x : y, z } = o; // `w`, `y` and `z` are variable declarations
* o = null;
* }
*/
class VarDecl extends @var_decl, VarRef, LexicalDecl {
override Variable getVariable() { decl(this, result) }
override predicate isLValue() {
exists(VariableDeclarator vd | vd.getBindingPattern() = this |
exists(vd.getInit()) or
exists(EnhancedForLoop efl | this = efl.getIterator())
)
or
exists(BindingPattern p | this = p.getABindingVarRef() and p.isLValue())
}
override string getAPrimaryQlClass() { result = "VarDecl" }
}
/**
* An identifier that declares a global variable.
*
* Example:
*
* ```
* var m; // `m` is a global variable declaration if this is a top-level statement
* // (and not in a module)
* ```
*/
class GlobalVarDecl extends VarDecl {
GlobalVarDecl() { this.getVariable() instanceof GlobalVariable }
}
/**
* An array pattern.
*
* Example:
*
* ```
* function f(x, { y: z }, ...rest) {
* var [ a, b ] = rest; // `[ a, b ]` is an array pattern
* var c;
* }
* ```
*/
class ArrayPattern extends DestructuringPattern, @array_pattern {
/** Gets the `i`th element of this array pattern. */
Expr getElement(int i) {
i >= 0 and
result = this.getChildExpr(i)
}
/** Gets an element of this array pattern. */
Expr getAnElement() { exists(int i | i >= -1 | result = this.getChildExpr(i)) }
/** Gets the default expression for the `i`th element of this array pattern, if any. */
Expr getDefault(int i) {
i in [0 .. this.getSize() - 1] and
result = this.getChildExpr(-2 - i)
}
/** Holds if the `i`th element of this array pattern has a default expression. */
predicate hasDefault(int i) { exists(this.getDefault(i)) }
/** Gets the rest pattern of this array pattern, if any. */
override Expr getRest() { result = this.getChildExpr(-1) }
/** Holds if this array pattern has a rest pattern. */
predicate hasRest() { exists(this.getRest()) }
/** Gets the number of elements in this array pattern, not including any rest pattern. */
int getSize() { array_size(this, result) }
/** Holds if the `i`th element of this array pattern is omitted. */
predicate elementIsOmitted(int i) {
i in [0 .. this.getSize() - 1] and
not exists(this.getElement(i))
}
/** Holds if this array pattern has an omitted element. */
predicate hasOmittedElement() { this.elementIsOmitted(_) }
override predicate isImpure() { this.getAnElement().isImpure() }
override VarRef getABindingVarRef() {
result = this.getAnElement().(BindingPattern).getABindingVarRef()
}
override string getAPrimaryQlClass() { result = "ArrayPattern" }
}
/**
* An object pattern.
*
* Example:
*
* ```
* function f(x, { y: z }, ...rest) { // `{ y: z }` is an object pattern
* var [ a, b ] = rest;
* var c;
* }
* ```
*/
class ObjectPattern extends DestructuringPattern, @object_pattern {
/** Gets the `i`th property pattern in this object pattern. */
PropertyPattern getPropertyPattern(int i) { properties(result, this, i, _, _) }
/** Gets a property pattern in this object pattern. */
PropertyPattern getAPropertyPattern() { result = this.getPropertyPattern(_) }
/** Gets the number of property patterns in this object pattern. */
int getNumProperty() { result = count(this.getAPropertyPattern()) }
/** Gets the property pattern with the given name, if any. */
PropertyPattern getPropertyPatternByName(string name) {
result = this.getAPropertyPattern() and
result.getName() = name
}
/** Gets the rest property pattern of this object pattern, if any. */
override Expr getRest() { result = this.getChildExpr(-1) }
override predicate isImpure() { this.getAPropertyPattern().isImpure() }
override VarRef getABindingVarRef() {
result = this.getAPropertyPattern().getValuePattern().(BindingPattern).getABindingVarRef() or
result = this.getRest().(BindingPattern).getABindingVarRef()
}
override string getAPrimaryQlClass() { result = "ObjectPattern" }
}
/**
* A property pattern in an object pattern.
*
* Examples:
*
* ```
* function f(x, { y: z }, ...rest) { // `y: z` is a property pattern
* var [ a, b ] = rest;
* var c;
* }
* ```
*/
class PropertyPattern extends @property, ASTNode {
PropertyPattern() {
// filter out ordinary properties
exists(ObjectPattern obj | properties(this, obj, _, _, _))
}
/** Holds if the name of this property pattern is computed. */
predicate isComputed() { is_computed(this) }
/** Gets the expression specifying the name of the matched property. */
Expr getNameExpr() { result = this.getChildExpr(0) }
/** Gets the expression the matched property value is assigned to. */
Expr getValuePattern() { result = this.getChildExpr(1) }
/** Gets the default value of this property pattern, if any. */
Expr getDefault() { result = this.getChildExpr(2) }
/** Holds if this property pattern is a shorthand pattern. */
predicate isShorthand() {
this.getNameExpr().getLocation() = this.getValuePattern().getLocation()
}
/** Gets the name of the property matched by this pattern. */
string getName() {
not this.isComputed() and
result = this.getNameExpr().(Identifier).getName()
or
result = this.getNameExpr().(Literal).getValue()
}
/** Gets the object pattern this property pattern belongs to. */
ObjectPattern getObjectPattern() { properties(this, result, _, _, _) }
/** Holds if this pattern is impure, that is, if its evaluation could have side effects. */
predicate isImpure() {
this.isComputed() and this.getNameExpr().isImpure()
or
this.getValuePattern().isImpure()
}
override string toString() { properties(this, _, _, _, result) }
override ControlFlowNode getFirstControlFlowNode() {
result = this.getNameExpr().getFirstControlFlowNode()
}
override string getAPrimaryQlClass() { result = "PropertyPattern" }
}
/**
* A variable declarator declaring a local or global variable.
*
* Examples:
*
* ```
* var
* x, // variable declarator
* y = z; // variable declarator
* ```
*/
class VariableDeclarator extends Expr, @var_declarator {
/** Gets the pattern specifying the declared variable(s). */
BindingPattern getBindingPattern() { result = this.getChildExpr(0) }
/** Gets the expression specifying the initial value of the declared variable(s), if any. */
Expr getInit() { result = this.getChildExpr(1) }
/** Gets the type annotation for the declared variable or binding pattern. */
TypeAnnotation getTypeAnnotation() {
result = this.getChildTypeExpr(2)
or
result = this.getDeclStmt().getDocumentation().getATagByTitle("type").getType()
}
/** Holds if this is a TypeScript variable marked as definitely assigned with the `!` operator. */
predicate hasDefiniteAssignmentAssertion() { has_definite_assignment_assertion(this) }
/** Gets the declaration statement this declarator belongs to, if any. */
DeclStmt getDeclStmt() { this = result.getADecl() }
override ControlFlowNode getFirstControlFlowNode() {
result = this.getBindingPattern().getFirstControlFlowNode()
}
override string getAPrimaryQlClass() { result = "VariableDeclarator" }
}
/**
* For internal use, holding the decorators of a function parameter.
*/
private class DecoratorList extends Expr, @decorator_list {
Decorator getDecorator(int i) { result = this.getChildExpr(i) }
override ControlFlowNode getFirstControlFlowNode() {
if exists(this.getDecorator(0))
then result = this.getDecorator(0).getFirstControlFlowNode()
else result = this
}
}
/**
* A program element that declares parameters, that is, either a function or
* a catch clause.
*
* Examples:
*
* ```
* function f(x, y) { // a parameterized element
* try {
* g();
* } catch(e) { // a parameterized element
* }
* }
* ```
*/
class Parameterized extends @parameterized, Documentable {
/** Gets a parameter declared by this element. */
Parameter getAParameter() { this = result.getParent() }
/** Gets the number of parameters declared by this element. */
int getNumParameter() { result = count(this.getAParameter()) }
/** Gets a variable of the given name that is a parameter of this element. */
Variable getParameterVariable(string name) {
result = this.getAParameter().getAVariable() and
result.getName() = name
}
}
/**
* A parameter declaration in a function or catch clause.
*
* Examples:
*
* ```
* function f(x, { y: z }, ...rest) { // `x`, `{ y: z }` and `rest` are parameter declarations
* var [ a, b ] = rest;
* var c;
* try {
* x.m();
* } catch(e) {} // `e` is a parameter declaration
* }
* ```
*/
class Parameter extends BindingPattern {
Parameter() {
exists(Parameterized p, int n |
this = p.getChildExpr(n) and
n >= 0
)
}
/**
* Gets the index of this parameter within the parameter list of its
* declaring entity.
*/
int getIndex() { exists(Parameterized p | this = p.getChildExpr(result)) }
/** Gets the element declaring this parameter. */
override Parameterized getParent() { result = super.getParent() }
/** Gets the default expression for this parameter, if any. */
Expr getDefault() {
exists(Function f, int n | this = f.getParameter(n) | result = f.getChildExpr(-(4 * n + 5)))
}
/** Gets the type annotation for this parameter, if any. */
override TypeAnnotation getTypeAnnotation() {
exists(Function f, int n | this = f.getParameter(n) | result = f.getChildTypeExpr(-(4 * n + 6)))
or
result = this.getJSDocTag().getType()
}
/** Holds if this parameter is a rest parameter. */
predicate isRestParameter() {
exists(Function f, int n | this = f.getParameter(n) |
n = f.getNumParameter() - 1 and
f.hasRestParameter()
)
}
private DecoratorList getDecoratorList() {
exists(Function f, int n | this = f.getParameter(n) | result = f.getChildExpr(-(4 * n + 8)))
}
/** Gets the `i`th decorator applied to this parameter. */
Decorator getDecorator(int i) { result = this.getDecoratorList().getDecorator(i) }
/** Gets a decorator applied to this parameter. */
Decorator getADecorator() { result = this.getDecorator(_) }
/** Gets the number of decorators applied to this parameter. */
int getNumDecorator() { result = count(this.getADecorator()) }
override predicate isLValue() { any() }
/**
* Gets the JSDoc tag describing this parameter, if any.
*/
JSDocTag getJSDocTag() {
none() // overridden in SimpleParameter
}
/**
* Holds if this is a parameter declared optional with the `?` token.
*
* Note that this does not hold for rest parameters, and does not in general
* hold for parameters with defaults.
*
* For example, `x`, is declared optional below:
* ```
* function f(x?: number) {}
* ```
*/
predicate isDeclaredOptional() { is_optional_parameter_declaration(this) }
override string getAPrimaryQlClass() { result = "Parameter" }
}
/**
* A parameter declaration that is not an object or array pattern.
*
* Examples:
*
* ```
* function f(x, { y: z }, ...rest) { // `x` and `rest` are simple parameter declarations
* var [ a, b ] = rest;
* var c;
* }
* ```
*/
class SimpleParameter extends Parameter, VarDecl {
override predicate isLValue() { Parameter.super.isLValue() }
/**
* Gets a use of this parameter that refers to its initial value as
* passed in from the caller.
*/
VarUse getAnInitialUse() {
exists(SsaDefinition ssa |
ssa.getAContributingVarDef() = this and
result = ssa.getVariable().getAUse()
)
}
override JSDocParamTag getJSDocTag() {
exists(Function fun |
this = fun.getAParameter() and
result = fun.getDocumentation().getATag()
) and
// Avoid joining on name
exists(string tagName, string paramName |
tagName = result.getName() and
paramName = this.getName() and
tagName <= paramName and
paramName <= tagName
)
}
override string getAPrimaryQlClass() { result = "SimpleParameter" }
}
/**
* A constructor parameter that induces a field in its class.
*
* Example:
*
* ```
* class C {
* constructor(
* public x: number // constructor parameter
* ) {}
* }
* ```
*/
class FieldParameter extends SimpleParameter {
FieldParameter() { exists(ParameterField field | field.getParameter() = this) }
/** Gets the field induced by this parameter. */
ParameterField getField() { result.getParameter() = this }
override string getAPrimaryQlClass() { result = "FieldParameter" }
}
/**
* A string representing one of the three TypeScript declaration spaces: `variable`, `type`, or `namespace`.
*/
class DeclarationSpace extends string {
DeclarationSpace() { this = "variable" or this = "type" or this = "namespace" }
}
/**
* A name that is declared in a particular scope.
*
* This can be a variable or a local name for a TypeScript type or namespace.
*
* Examples:
*
* ```
* // `id`, `x` and `String` are lexical names
* function id(x: String) : any {
* return x;
* }
* ```
*/
class LexicalName extends @lexical_name {
/** Gets the scope in which this name was declared. */
abstract Scope getScope();
/** Gets the name of this variable, type or namespace. */
abstract string getName();
/** Gets a string representation of this element. */
abstract string toString();
/**
* Gets the declaration space this name belongs to.
*
* This can be either `variable`, `type`, or `namespace`.
*/
abstract DeclarationSpace getDeclarationSpace();
}
/**
* An identifier that refers to a variable, type, or namespace, or a combination of these.
*
* Example:
*
* ```
* function id(x: String) : any { // `id`, `x` and `String` are lexical references
* return x; // `x` is a lexical reference
* }
* ```
*/
class LexicalRef extends Identifier, @lexical_ref {
/**
* Gets any of the names referenced by this identifier.
*
* Note that each identifier may reference up to one lexical name per declaration space.
* For example, a class name declares both a type and a variable.
*/
LexicalName getALexicalName() {
bind(this, result) or
decl(this, result) or
typebind(this, result) or
typedecl(this, result) or
namespacebind(this, result) or
namespacedecl(this, result)
}
}
/**
* An identifier that declares a variable, type, or namespace, or a combination of these.
*
* Example:
*
* ```
* function id(x: number) : any { // `id` and `x` are lexical declarations
* return x;
* }
* ```
*/
class LexicalDecl extends LexicalRef, @lexical_decl { }
/**
* An identifier that refers to a variable, type, or namespace, or a combination of these,
* in a non-declaring position.
*
* Example:
*
* ```
* function id(x: String) : any { // `String` is a lexical access
* return x; // `x` is a lexical access
* }
* ```
*/
class LexicalAccess extends LexicalRef, @lexical_access { }