Files
codeql/javascript/ql/lib/semmle/javascript/Variables.qll
2025-11-13 09:45:56 +01:00

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 { }