mirror of
https://github.com/github/codeql.git
synced 2025-12-16 08:43:11 +01:00
1006 lines
30 KiB
Plaintext
1006 lines
30 KiB
Plaintext
/** Provides classes for modeling program variables. */
|
|
overlay[local]
|
|
module;
|
|
|
|
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
|
|
}
|
|
|
|
/** Gets the local type name with the given name declared in this scope. */
|
|
LocalTypeName getLocalTypeName(string name) {
|
|
result.getScope() = this 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. */
|
|
overlay[global]
|
|
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 variable is declared in the top-level of a module using a `declare` statement.
|
|
*
|
|
* For example:
|
|
* ```js
|
|
* declare var $: any;
|
|
* ```
|
|
*
|
|
* Such variables are generally treated as a global variables, except for type-checking related purposes.
|
|
*/
|
|
pragma[nomagic]
|
|
predicate isTopLevelWithAmbientDeclaration() {
|
|
this.getScope() instanceof ModuleScope and
|
|
forex(VarDecl decl | decl = this.getADeclaration() | decl.isAmbient())
|
|
}
|
|
|
|
/** Holds if this is a global variable. */
|
|
predicate isGlobal() {
|
|
this.getScope() instanceof GlobalScope or this.isTopLevelWithAmbientDeclaration()
|
|
}
|
|
|
|
/**
|
|
* 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() } // Overridden in VarAccess and VarDecl
|
|
|
|
override string getName() { result = Identifier.super.getName() }
|
|
|
|
override VarRef getABindingVarRef() { result = this }
|
|
|
|
overlay[global]
|
|
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(_) }
|
|
|
|
overlay[global]
|
|
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) }
|
|
|
|
overlay[global]
|
|
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. */
|
|
overlay[global]
|
|
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.
|
|
*/
|
|
overlay[global]
|
|
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" }
|
|
}
|
|
|
|
/** Module containing the `DeclarationSpace` constants. */
|
|
module DeclarationSpace {
|
|
/** Gets the declaration space for variables/values. */
|
|
DeclarationSpace variable() { result = "variable" }
|
|
|
|
/** Gets the declaration space for types. */
|
|
DeclarationSpace type() { result = "type" }
|
|
|
|
/** Gets the declaration space for namespaces. */
|
|
DeclarationSpace namespace() { result = "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 { }
|