Files
codeql/javascript/ql/lib/semmle/javascript/Classes.qll

1331 lines
38 KiB
Plaintext

/**
* Provides classes for working with ECMAScript 2015 classes.
*
* Class declarations and class expressions are modeled by (QL) classes `ClassDeclaration`
* and `ClassExpression`, respectively, which are both subclasses of `ClassDefinition`.
*/
overlay[local?]
module;
import javascript
/**
* An ECMAScript 2015/TypeScript class definition or a TypeScript interface definition,
* including both declarations and expressions.
*
* Examples:
*
* ```
* class Rectangle extends Shape {
* Rectangle(width, height) {
* this.width = width;
* this.height = height;
* }
*
* area() { return this.width * this.height; }
* }
*
* interface EventEmitter<T> {
* addListener(listener: (x: T) => void): void;
* }
* ```
*/
class ClassOrInterface extends @class_or_interface, TypeParameterized {
/** Gets the identifier naming the declared type, if any. */
Identifier getIdentifier() { none() } // Overridden in subtypes.
/**
* Gets the name of the defined class or interface, possibly inferred
* from the context if this is an anonymous class expression.
*
* Has no result if no name could be determined.
*/
string getName() {
result = this.getIdentifier().getName() // Overridden in ClassExpr
}
/** Gets a member declared in this class or interface. */
MemberDeclaration getAMember() { result.getDeclaringType() = this }
/** Gets the `i`th member declared in this class or interface. */
MemberDeclaration getMemberByIndex(int i) { properties(result, this, i, _, _) }
/** Gets the member with the given name declared in this class or interface. */
MemberDeclaration getMember(string name) {
result = this.getAMember() and
result.getName() = name
}
/** Gets a method declared in this class or interface. */
MethodDeclaration getAMethod() { result = this.getAMember() }
/**
* Gets the method with the given name declared in this class or interface.
*
* Note that for overloaded method signatures in TypeScript files, this returns every overload.
*/
MethodDeclaration getMethod(string name) { result = this.getMember(name) }
/** Gets an overloaded version of the method with the given name declared in this class or interface. */
MethodDeclaration getMethodOverload(string name, int overloadIndex) {
result = this.getMethod(name) and
overloadIndex = result.getOverloadIndex()
}
/** Gets a field declared in this class or interface. */
FieldDeclaration getAField() { result = this.getAMember() }
/** Gets the field with the given name declared in this class or interface. */
FieldDeclaration getField(string name) { result = this.getMember(name) }
/** Gets a call signature declared in this interface. */
CallSignature getACallSignature() { result = this.getAMember() }
/** Gets an index signature declared in this interface. */
IndexSignature getAnIndexSignature() { result = this.getAMember() }
/**
* Gets the expression denoting the super class of this class,
* or nothing if this is an interface or a class without an `extends` clause.
*/
Expr getSuperClass() { none() }
/**
* Gets the `n`th type from the `implements` clause of this class or `extends` clause of this interface,
* starting at 0.
*/
TypeExpr getSuperInterface(int n) { none() }
/**
* Gets any type from the `implements` clause of this class or `extends` clause of this interface.
*/
TypeExpr getASuperInterface() { result = this.getSuperInterface(_) }
/**
* Holds if this is an interface or a class declared with the `abstract` modifier.
*/
predicate isAbstract() { none() }
/**
* Gets a description of this class or interface.
*
* For named types such as `class C { ... }`, this is just the declared
* name. For classes assigned to variables, this is the name of the variable.
* If no meaningful name can be inferred, the result is "anonymous class" or
* "anonymous interface".
*/
override string describe() { none() } // Overridden in subtypes.
/**
* Gets the canonical name of this class or interface type.
*
* Anonymous classes and interfaces do not have a canonical name.
*/
overlay[global]
deprecated TypeName getTypeName() { result.getADefinition() = this }
/**
* Gets the ClassOrInterface corresponding to either a super type or an implemented interface.
*/
ClassOrInterface getASuperTypeDeclaration() {
this.getSuperClass().(VarAccess).getVariable().getADeclaration() = result.getIdentifier() or
this.getASuperInterface().(LocalTypeAccess).getLocalTypeName().getADeclaration() =
result.getIdentifier()
}
}
/**
* An ECMAScript 2015 or TypeScript class definition, that is, either a class declaration statement
* or a class expression.
*
* Examples:
*
* ```
* class Rectangle extends Shape { // class declaration statement
* constructor(width, height) {
* this.width = width;
* this.height = height;
* }
*
* area() { return this.width * this.height; }
* }
*
* let C =
* class { // class expression
* constructor() { this.x = 0; }
*
* bump() { return this.x++; }
* };
* ```
*/
class ClassDefinition extends @class_definition, ClassOrInterface, AST::ValueNode {
/** Gets the variable holding this class. */
Variable getVariable() { result = this.getIdentifier().getVariable() }
/** Gets the identifier naming the defined class, if any. */
override VarDecl getIdentifier() { result = this.getChildExpr(0) }
override TypeParameter getTypeParameter(int i) {
// AST indices for type parameters: -3, -6, -9, ...
exists(int astIndex | typeexprs(result, _, this, astIndex, _) |
astIndex <= -3 and astIndex % 3 = 0 and i = -(astIndex + 3) / 3
)
}
/** Gets the expression denoting the super class of the defined class, if any. */
override Expr getSuperClass() { result = this.getChildExpr(1) }
/** Gets the `i`th type from the `implements` clause of this class, starting at 0. */
override TypeExpr getSuperInterface(int i) {
// AST indices for super interfaces: -1, -4, -7, ...
exists(int astIndex | typeexprs(result, _, this, astIndex, _) |
astIndex <= -1 and astIndex % 3 = -1 and i = -(astIndex + 1) / 3
)
}
/** Gets any type from the `implements` clause of this class. */
override TypeExpr getASuperInterface() { result = ClassOrInterface.super.getASuperInterface() }
/**
* Gets the constructor of this class.
*
* Note that every class has a constructor: if no explicit constructor
* is declared, it has a synthetic default constructor.
*/
ConstructorDeclaration getConstructor() { result = this.getAMethod() }
/**
* Gets the `i`th decorator applied to this class.
*
* For example, the class `@A @B class C {}` has
* `@A` as its 0th decorator, and `@B` as its first decorator.
*/
Decorator getDecorator(int i) {
// AST indices for decorators: -2, -5, -8, ...
exists(int astIndex | exprs(result, _, this, astIndex, _) |
astIndex <= -2 and astIndex % 3 = -2 and i = -(astIndex + 2) / 3
)
}
/**
* Gets a decorator applied to this class, if any.
*
* For example, the class `@A @B class C {}` has
* decorators `@A` and `@B`.
*/
Decorator getADecorator() { result = this.getDecorator(_) }
/**
* Holds if this class has the `abstract` modifier.
*/
override predicate isAbstract() { is_abstract_class(this) }
override string describe() {
if exists(this.inferNameFromVarDef())
then result = this.inferNameFromVarDef()
else result = "anonymous class"
}
/**
* Gets a description of this class, based on its declared name or the name
* of the variable it is assigned to, if any.
*/
private string inferNameFromVarDef() {
// in ambiguous cases like `let C = class D {}`, prefer `D` to `C`
if exists(this.getIdentifier())
then result = "class " + this.getIdentifier().getName()
else
exists(VarDef vd | this = vd.getSource() |
result = "class " + vd.getTarget().(VarRef).getName()
)
}
/**
* Gets an instance method of this class with the given name.
*
* Note that constructors aren't considered instance methods.
*/
Function getInstanceMethod(string name) {
exists(MemberDefinition mem | mem = this.getMember(name) |
result = mem.getInit() and
not mem.isStatic() and
not mem instanceof ConstructorDefinition
)
}
/**
* Gets the definition of the super class of this class, if it can be determined.
*/
overlay[global]
ClassDefinition getSuperClassDefinition() {
result = this.getSuperClass().analyze().getAValue().(AbstractClass).getClass()
}
override string getAPrimaryQlClass() { result = "ClassDefinition" }
/**
* Gets a static initializer of this class, if any.
*/
BlockStmt getAStaticInitializerBlock() {
exists(StaticInitializer init | init.getDeclaringClass() = this | result = init.getBody())
}
}
/**
* A class declaration statement.
*
* Example:
*
* ```
* class Rectangle extends Shape {
* constructor(width, height) {
* this.width = width;
* this.height = height;
* }
*
* area() { return this.width * this.height; }
* }
* ```
*/
class ClassDeclStmt extends @class_decl_stmt, ClassDefinition, Stmt {
override ControlFlowNode getFirstControlFlowNode() {
if has_declare_keyword(this) then result = this else result = this.getIdentifier()
}
}
/**
* A class expression.
*
* Example:
*
* ```
* let C =
* class { // class expression
* constructor() { this.x = 0; }
*
* bump() { return this.x++; }
* };
* ```
*/
class ClassExpr extends @class_expr, ClassDefinition, Expr {
override string getName() {
result = ClassDefinition.super.getName()
or
not exists(this.getIdentifier()) and
(
exists(VarDef vd | this = vd.getSource() | result = vd.getTarget().(VarRef).getName())
or
exists(ValueProperty p |
this = p.getInit() and
result = p.getName()
)
or
exists(AssignExpr assign, PropAccess prop |
this = assign.getRhs().getUnderlyingValue() and
prop = assign.getLhs() and
result = prop.getPropertyName()
)
)
}
override predicate isImpure() { none() }
override ControlFlowNode getFirstControlFlowNode() {
if exists(this.getIdentifier())
then result = this.getIdentifier()
else
if exists(this.getSuperClass())
then result = this.getSuperClass().getFirstControlFlowNode()
else
if exists(this.getClassInitializedMember())
then
result =
min(ClassInitializedMember m |
m = this.getClassInitializedMember()
|
m order by m.getIndex()
)
else result = this
}
/** Returns a member that is initialized during the creation of this class. */
private ClassInitializedMember getClassInitializedMember() { result = this.getAMember() }
}
/**
* A class member that is initialized at class creation time (as opposed to instance creation time).
*/
private class ClassInitializedMember extends MemberDeclaration {
ClassInitializedMember() { this instanceof MethodDefinition or this.isStatic() }
int getIndex() { properties(this, _, result, _, _) }
override string getAPrimaryQlClass() { result = "ClassInitializedMember" }
}
/**
* A `super` expression.
*
* Example:
*
* ```
* super
* ```
*/
class SuperExpr extends @super_expr, Expr {
override predicate isImpure() { none() }
/**
* Gets the function whose `super` binding this expression refers to,
* which is the nearest enclosing non-arrow function.
*/
Function getBinder() { result = this.getEnclosingFunction().getThisBinder() }
override string getAPrimaryQlClass() { result = "SuperExpr" }
}
/**
* A `super(...)` call.
*
* Example:
*
* ```
* super(...args)
* ```
*/
class SuperCall extends CallExpr {
SuperCall() { this.getCallee().getUnderlyingValue() instanceof SuperExpr }
/**
* Gets the function whose `super` binding this call refers to,
* which is the nearest enclosing non-arrow function.
*/
Function getBinder() { result = this.getCallee().getUnderlyingValue().(SuperExpr).getBinder() }
}
/**
* A property access on `super`.
*
* Example:
*
* ```
* super.f
* ```
*/
class SuperPropAccess extends PropAccess {
SuperPropAccess() { this.getBase().getUnderlyingValue() instanceof SuperExpr }
}
/**
* A `new.target` expression.
*
* Example:
*
* ```
* new.target
* ```
*
* When a function `f` is invoked as `new f()`, then `new.target` inside
* `f` evaluates to `f` ; on the other hand, when `f` is invoked without
* `new`, it evaluates to `undefined`.
*
* See also ECMAScript 2015 Language Specification, Chapter 12.3.8.
*/
class NewTargetExpr extends @newtarget_expr, Expr {
override predicate isImpure() { none() }
override string getAPrimaryQlClass() { result = "NewTargetExpr" }
}
/**
* A scope induced by a named class expression or class expression with type parameters.
*/
class ClassExprScope extends @class_expr_scope, Scope {
override string toString() { result = "class expression scope" }
}
/**
* A scope induced by a class declaration with type parameters.
*/
class ClassDeclScope extends @class_decl_scope, Scope {
override string toString() { result = "class declaration scope" }
}
/**
* A member declaration in a class or interface, that is, either a method declaration or a field declaration.
*
* Examples:
*
* ```
* class Rectangle extends Shape {
* width; // field declaration
* height; // field declaration
*
* constructor(height, width) { // constructor declaration, which is also a method declaration
* this.height = height;
* this.width = width;
* }
*
* area() { // method declaration
* return this.width*this.height;
* }
* }
*
* interface EventEmitter<T> {
* addListener(listener: (x: T) => void): void; // method declaration
* }
* ```
*
* The subtype `MemberSignature` contains TypeScript members that are abstract, ambient, or
* overload signatures.
*
* The subtype `MemberDefinition` contains all members that are not signatures. In regular
* JavaScript, all members are definitions.
*
* There are also subtypes for working with specific kinds of members, such as `FieldDeclaration`,
* `FieldSignature`, and `FieldDefinition`, and similarly named subtypes for methods, constructors, and
* getters and setters.
*/
class MemberDeclaration extends @property, Documentable {
MemberDeclaration() {
// filter out property patterns and object properties and enum members
exists(ClassOrInterface cl | properties(this, cl, _, _, _))
}
/**
* Holds if this member is static.
*/
predicate isStatic() { is_static(this) }
/** Gets a boolean indicating if this member is static. */
boolean getStaticAsBool() { if this.isStatic() then result = true else result = false }
/**
* Holds if this member is abstract.
*
* Abstract members occur only in TypeScript.
*/
predicate isAbstract() { is_abstract_member(this) }
/**
* Holds if this member is public, either because it has no access modifier or
* because it is explicitly annotated as `public`.
*
* Class members not declared in a TypeScript file are always considered public.
*/
predicate isPublic() { not this.isPrivate() and not this.isProtected() }
/**
* Holds if this is a TypeScript member explicitly annotated with the `public` keyword.
*/
predicate hasPublicKeyword() { has_public_keyword(this) }
/**
* Holds if this member is considered private.
*
* This may occur in two cases:
* - it is a TypeScript member annotated with the `private` keyword, or
* - the member has a private name, such as `#foo`, referring to a private field in the class
*/
predicate isPrivate() { this.hasPrivateKeyword() or this.hasPrivateFieldName() }
/**
* Holds if this is a TypeScript member annotated with the `private` keyword.
*/
predicate hasPrivateKeyword() { has_private_keyword(this) }
/**
* Holds if this is a TypeScript member annotated with the `protected` keyword.
*/
predicate isProtected() { has_protected_keyword(this) }
/**
* Holds if the member has a private name, such as `#foo`, referring to a private field in the class.
*
* For example:
* ```js
* class Foo {
* #method() {}
* }
* ```
*/
predicate hasPrivateFieldName() { this.getNameExpr().(Label).getName().charAt(0) = "#" }
/**
* Gets the expression specifying the name of this member,
* or nothing if this is a call signature.
*/
Expr getNameExpr() { result = this.getChildExpr(0) }
/**
* Gets the expression specifying the initial value of the member;
* for methods and constructors this is always a function, for fields
* it may not be defined.
*/
Expr getInit() { result = this.getChildExpr(1) }
/** Gets the name of this member. */
string getName() {
result = this.getNameExpr().(Literal).getValue()
or
not this.isComputed() and result = this.getNameExpr().(Identifier).getName()
}
/** Holds if the name of this member is computed. */
predicate isComputed() { is_computed(this) }
/** Gets the class or interface this member belongs to. */
ClassOrInterface getDeclaringType() { properties(this, result, _, _, _) }
/** Gets the class this member belongs to, if any. */
ClassDefinition getDeclaringClass() { properties(this, result, _, _, _) }
/** Gets the index of this member within its enclosing type. */
int getMemberIndex() { properties(this, _, result, _, _) }
/** Holds if the name of this member is computed by an impure expression. */
overlay[global]
predicate hasImpureNameExpr() { this.isComputed() and this.getNameExpr().isImpure() }
/**
* Gets the `i`th decorator applied to this member.
*
* For example, a method of the form `@A @B m() { ... }` has
* `@A` as its 0th decorator and `@B` as its first decorator.
*/
Decorator getDecorator(int i) { result = this.getChildExpr(-(i + 1)) }
/**
* Gets a decorator applied to this member.
*
* For example, a method of the form `@A @B m() { ... }` has
* decorators `@A` and `@B`.
*/
Decorator getADecorator() { result = this.getDecorator(_) }
override string toString() { properties(this, _, _, _, result) }
override ControlFlowNode getFirstControlFlowNode() {
result = this.getNameExpr().getFirstControlFlowNode()
}
/**
* True if this is neither abstract, ambient, nor part of an overloaded method signature.
*/
predicate isConcrete() {
not this.isAbstract() and
not this.isAmbient() and
(this instanceof MethodDeclaration implies this.(MethodDeclaration).getBody().hasBody())
}
/**
* True if this is abstract, ambient, or an overload signature.
*/
predicate isSignature() { not this.isConcrete() }
override string getAPrimaryQlClass() { result = "MemberDeclaration" }
}
/**
* A concrete member of a class, that is, a non-abstract, non-ambient field or method with a body.
*
* Examples:
*
* ```
* class Rectangle extends Shape {
* width; // field declaration
* height; // field declaration
*
* constructor(height, width) { // constructor declaration, which is also a method declaration
* this.height = height;
* this.width = width;
* }
*
* area() { // method declaration
* return this.width*this.height;
* }
* }
* ```
*/
class MemberDefinition extends MemberDeclaration {
MemberDefinition() { this.isConcrete() }
}
/**
* A member signature declared in a class or interface, that is, an abstract or ambient field
* or method without a function body.
*
* Example:
*
* ```
* interface EventEmitter<T> {
* addListener(listener: (x: T) => void): void; // method signature
* }
* ```
*/
class MemberSignature extends MemberDeclaration {
MemberSignature() { this.isSignature() }
}
/**
* A method declaration in a class or interface, either a concrete definition or a signature without a body.
*
* Examples:
*
* ```
* abstract class Shape {
* abstract area() : number; // method declaration
* }
*
* class Rectangle extends Shape {
* height: number;
* width: number;
*
* constructor(height: number, width: number) { // constructor declaration, which is also a method declaration
* super();
* this.height = height;
* this.width = width;
* }
*
* area() { // method declaration
* return this.width*this.height;
* }
* }
* ```
*
* Note that TypeScript call signatures are not considered methods.
*/
class MethodDeclaration extends MemberDeclaration {
MethodDeclaration() { is_method(this) }
/**
* Gets the body of this method.
*/
FunctionExpr getBody() { result = this.getChildExpr(1) }
/**
* Holds if this method is overloaded, that is, there are multiple method
* signatures with its name declared in the enclosing type.
*/
predicate isOverloaded() {
not this instanceof ConstructorDeclaration and
hasOverloadedMethod(this.getDeclaringType(), this.getName())
or
this instanceof ConstructorDeclaration and
hasOverloadedConstructor(this.getDeclaringClass())
}
/**
* Gets the index of this method declaration among all the method declarations
* with this name.
*
* In the rare case of a class containing multiple concrete methods with the same name,
* the overload index is defined as if only one of them was concrete.
*/
int getOverloadIndex() {
exists(ClassOrInterface type, string name, boolean static |
this =
rank[result + 1](MethodDeclaration method, int i |
methodDeclaredInType(type, name, static, i, method)
|
method order by i
)
)
or
exists(ClassDefinition type |
this =
rank[result + 1](ConstructorDeclaration ctor, int i |
ctor = type.getMemberByIndex(i)
|
ctor order by i
)
)
}
}
/**
* Holds if the `index`th member of `type` is `method`, which has the given `name`.
*/
private predicate methodDeclaredInType(
ClassOrInterface type, string name, boolean static, int index, MethodDeclaration method
) {
not method instanceof ConstructorDeclaration and // distinguish methods named "constructor" from the constructor
type.getMemberByIndex(index) = method and
static = method.getStaticAsBool() and
method.getName() = name
}
/**
* Holds if `type` has an overloaded method named `name`.
*/
private predicate hasOverloadedMethod(ClassOrInterface type, string name) {
exists(MethodDeclaration method |
method = type.getMethod(name) and
not method instanceof ConstructorDeclaration and
method.getOverloadIndex() > 0
)
}
/** Holds if `type` has an overloaded constructor declaration. */
private predicate hasOverloadedConstructor(ClassDefinition type) {
type.getConstructor().getOverloadIndex() > 0
}
/** Holds if `type` has an overloaded function call signature. */
private predicate hasOverloadedFunctionCallSignature(ClassOrInterface type) {
type.getACallSignature().(FunctionCallSignature).getOverloadIndex() > 0
}
/** Holds if `type` has an overloaded constructor call signature. */
private predicate hasOverloadedConstructorCallSignature(ClassOrInterface type) {
type.getACallSignature().(ConstructorCallSignature).getOverloadIndex() > 0
}
/**
* A concrete method definition in a class.
*
* Examples:
*
* ```
* class Rectangle extends Shape {
* constructor(height, width) { // constructor definition, which is also a method definition
* this.height = height;
* this.width = width;
* }
*
* area() { // method definition
* return this.width*this.height;
* }
* }
* ```
*/
class MethodDefinition extends MethodDeclaration, MemberDefinition {
override string getAPrimaryQlClass() { result = "MethodDefinition" }
}
/**
* A method signature declared in a class or interface, that is, a method without a function body.
*
* Example:
*
* ```
* interface EventEmitter<T> {
* addListener(listener: (x: T) => void): void; // method signature
* }
* ```
*
* Note that TypeScript call signatures are not considered method signatures.
*/
class MethodSignature extends MethodDeclaration, MemberSignature {
override string getAPrimaryQlClass() { result = "MethodSignature" }
}
/**
* A constructor declaration in a class, either a concrete definition or a signature without a body.
*
* Example:
*
* ```
* class Rectangle extends Shape {
* constructor(height, width) { // constructor declaration
* this.height = height;
* this.width = width;
* }
*
* area() {
* return this.width*this.height;
* }
* }
* ```
*/
class ConstructorDeclaration extends MethodDeclaration {
ConstructorDeclaration() {
not this.isComputed() and
not this.isStatic() and
this.getName() = "constructor"
}
/** Holds if this is a synthetic default constructor. */
predicate isSynthetic() { this.getLocation().isEmpty() }
override string getAPrimaryQlClass() { result = "ConstructorDeclaration" }
}
/**
* The concrete constructor definition of a class, possibly a synthetic constructor if the class
* did not declare any constructors.
*
* Examples:
*
* ```
* class Rectangle extends Shape {
* constructor(height, width) { // constructor definition
* this.height = height;
* this.width = width;
* }
*
* area() {
* return this.width*this.height;
* }
* }
* ```
*/
class ConstructorDefinition extends ConstructorDeclaration, MethodDefinition {
override string getAPrimaryQlClass() { result = "ConstructorDefinition" }
}
/**
* A constructor signature declared in a class, that is, a constructor without a function body.
*
* ```
* declare class Rectangle {
* constructor(width: number, height: number); // constructor signature
* }
* ```
*/
class ConstructorSignature extends ConstructorDeclaration, MethodSignature {
override string getAPrimaryQlClass() { result = "ConstructorSignature" }
}
/**
* A function generated by the extractor to implement a synthetic default constructor.
*
* Example:
*
* ```
* class Rectangle extends Shape {
* // implicitly generated synthetic constructor:
* // constructor() { super(); }
*
* area() {
* return this.width*this.height;
* }
* }
* ```
*/
class SyntheticConstructor extends Function {
SyntheticConstructor() { this = any(ConstructorDeclaration cd | cd.isSynthetic() | cd.getBody()) }
}
/**
* An accessor method declaration in a class or interface, either a concrete definition or a signature without a body.
*
* Examples:
*
* ```
* class Rectangle extends Shape {
* constructor(height, width) {
* this.height = height;
* this.width = width;
* }
*
* get area() { // accessor method declaration
* return this.width*this.height;
* }
* }
* ```
*/
abstract class AccessorMethodDeclaration extends MethodDeclaration {
/** Get the corresponding getter (if this is a setter) or setter (if this is a getter). */
AccessorMethodDeclaration getOtherAccessor() {
getterSetterPair(this, result)
or
getterSetterPair(result, this)
}
}
/**
* A concrete accessor method definition in a class, that is, an accessor method with a function body.
*
* Examples:
*
* ```
* class Rectangle extends Shape {
* constructor(height, width) {
* this.height = height;
* this.width = width;
* }
*
* get area() { // accessor method declaration
* return this.width*this.height;
* }
* }
* ```
*/
abstract class AccessorMethodDefinition extends MethodDefinition, AccessorMethodDeclaration { }
/**
* An accessor method signature declared in a class or interface, that is, an accessor method without a function body.
*
* Example:
*
* ```
* abstract class Shape {
* abstract get area() : number; // accessor method signature
* }
* ```
*/
abstract class AccessorMethodSignature extends MethodSignature, AccessorMethodDeclaration { }
/**
* A getter method declaration in a class or interface, either a concrete definition or a signature without a function body.
*
* Examples:
*
* ```
* abstract class Shape {
* abstract get area() : number; // getter method signature
* }
*
* class Rectangle extends Shape {
* height: number;
* width: number;
*
* constructor(height: number, width: number) {
* super();
* this.height = height;
* this.width = width;
* }
*
* get area() { // getter method definition
* return this.width*this.height;
* }
* }
* ```
*/
class GetterMethodDeclaration extends AccessorMethodDeclaration, @property_getter {
override string getAPrimaryQlClass() { result = "GetterMethodDeclaration" }
/** Gets the correspinding setter declaration, if any. */
SetterMethodDeclaration getCorrespondingSetter() { getterSetterPair(this, result) }
}
/**
* A concrete getter method definition in a class, that is, a getter method with a function body.
*
* Examples:
*
* ```
* class Rectangle extends Shape {
* constructor(height, width) {
* this.height = height;
* this.width = width;
* }
*
* get area() { // getter method definition
* return this.width*this.height;
* }
* }
* ```
*/
class GetterMethodDefinition extends GetterMethodDeclaration, AccessorMethodDefinition {
override string getAPrimaryQlClass() { result = "GetterMethodDefinition" }
}
/**
* A getter method signature declared in a class or interface, that is, a getter method without a function body.
*
* Example:
*
* ```
* abstract class Shape {
* abstract get area() : number; // getter method signature
* }
* ```
*/
class GetterMethodSignature extends GetterMethodDeclaration, AccessorMethodSignature {
override string getAPrimaryQlClass() { result = "GetterMethodSignature" }
}
/**
* A setter method declaration in a class or interface, either a concrete definition or a signature without a body.
*
* Examples:
*
* ```
* abstract class Cell<T> {
* abstract set value(v: any); // setter method signature
* }
*
* class NumberCell extends Cell<number> {
* constructor(private _value: number) {
* super();
* }
*
* set value(v: any) { // setter method definition
* this._value = +v;
* }
* }
* ```
*/
class SetterMethodDeclaration extends AccessorMethodDeclaration, @property_setter {
override string getAPrimaryQlClass() { result = "SetterMethodDeclaration" }
/** Gets the correspinding getter declaration, if any. */
GetterMethodDeclaration getCorrespondingGetter() { getterSetterPair(result, this) }
}
/**
* A concrete setter method definition in a class, that is, a setter method with a function body
*
* Examples:
*
* ```
* class NumberCell extends Cell<number> {
* constructor(private _value: number) {
* super();
* }
*
* set value(v: any) { // setter method definition
* this._value = +v;
* }
* }
* ```
*/
class SetterMethodDefinition extends SetterMethodDeclaration, AccessorMethodDefinition {
override string getAPrimaryQlClass() { result = "SetterMethodDefinition" }
}
/**
* A setter method signature declared in a class or interface, that is, a setter method without a function body.
*
* Example:
*
* ```
* abstract class Cell<T> {
* abstract set value(v: any); // setter method signature
* }
* ```
*/
class SetterMethodSignature extends SetterMethodDeclaration, AccessorMethodSignature {
override string getAPrimaryQlClass() { result = "SetterMethodSignature" }
}
/**
* A field declaration in a class or interface, either a concrete definition or an abstract or ambient field signature.
*
* Examples:
*
* ```
* class Rectangle {
* height; // field declaration
* width; // field declaration
* }
*
* abstract class Counter {
* abstract value: number; // field signature
* }
* ```
*/
class FieldDeclaration extends MemberDeclaration, @field {
/** Gets the type annotation of this field, if any, such as `T` in `{ x: T }`. */
TypeAnnotation getTypeAnnotation() {
result = this.getChildTypeExpr(2)
or
result = this.getDocumentation().getATagByTitle("type").getType()
}
/** Holds if this is a TypeScript field annotated with the `readonly` keyword. */
predicate isReadonly() { has_readonly_keyword(this) }
/** Holds if this is a TypeScript field marked as optional with the `?` operator. */
predicate isOptional() { is_optional_member(this) }
/** Holds if this is a TypeScript field marked as definitely assigned with the `!` operator. */
predicate hasDefiniteAssignmentAssertion() { has_definite_assignment_assertion(this) }
override string getAPrimaryQlClass() { result = "FieldDeclaration" }
}
/**
* A concrete field definition in a class.
*
* Examples:
*
* ```
* class Rectangle {
* height; // field definition
* width; // field definition
* }
* ```
*/
class FieldDefinition extends FieldDeclaration, MemberDefinition { }
/**
* A field signature declared in a class or interface, that is, an abstract or ambient field declaration.
*
* Example:
*
* ```
* abstract class Counter {
* abstract value: number; // field signature
* }
* ```
*/
class FieldSignature extends FieldDeclaration, MemberSignature { }
/**
* A field induced by an initializing constructor parameter.
*
* Example:
*
* ```
* class C {
* constructor(public x: number) {} // `x` is a parameter field
* }
* ```
*/
class ParameterField extends FieldDeclaration, @parameter_field {
/** Gets the initializing constructor parameter from which this field was induced. */
Parameter getParameter() {
exists(FunctionExpr constructor, int index | parameter_fields(this, constructor, index) |
result = constructor.getParameter(index)
)
}
override Expr getNameExpr() { result = this.getParameter() }
override TypeAnnotation getTypeAnnotation() { result = this.getParameter().getTypeAnnotation() }
}
/**
* A static initializer in a class.
*/
class StaticInitializer extends MemberDefinition, @static_initializer {
/**
* Gets the body of the static initializer.
*/
BlockStmt getBody() { result.getParent() = this }
override Expr getNameExpr() { none() }
}
/**
* A call signature declared in an interface.
*
* Examples:
*
* ```
* interface I {
* (x: number): number; // function call signature
* new (x: string): Object; // constructor call signature
* }
* ```
*
* Call signatures are either function call signatures or constructor call signatures.
*/
class CallSignature extends @call_signature, MemberSignature {
FunctionExpr getBody() { result = this.getChildExpr(1) }
/** Gets the interface or function type that declares this call signature. */
override InterfaceDefinition getDeclaringType() {
result = MemberSignature.super.getDeclaringType()
}
}
/**
* A function call signature declared in an interface.
*
* Example:
*
* ```
* interface I {
* (x: number): string; // function call signature
* }
* ```
*/
class FunctionCallSignature extends @function_call_signature, CallSignature {
/** Gets the index of this function call signature among the function call signatures in the enclosing type. */
int getOverloadIndex() {
exists(ClassOrInterface type | type = this.getDeclaringType() |
this =
rank[result + 1](FunctionCallSignature sig, int i |
sig = type.getMemberByIndex(i)
|
sig order by i
)
)
}
/**
* Holds if this function call signature is overloaded, that is, there are multiple function call
* signatures declared in the enclosing type.
*/
predicate isOverloaded() { hasOverloadedFunctionCallSignature(this.getDeclaringType()) }
}
/**
* A constructor call signature declared in an interface.
*
* Example:
*
* ```
* interface I {
* new (x: string): Object; // constructor call signature
* }
* ```
*/
class ConstructorCallSignature extends @constructor_call_signature, CallSignature {
/** Gets the index of this constructor call signature among the constructor call signatures in the enclosing type. */
int getOverloadIndex() {
exists(ClassOrInterface type | type = this.getDeclaringType() |
this =
rank[result + 1](ConstructorCallSignature sig, int i |
sig = type.getMemberByIndex(i)
|
sig order by i
)
)
}
/**
* Holds if this constructor call signature is overloaded, that is, there are multiple constructor call
* signatures declared in the enclosing type.
*/
predicate isOverloaded() { hasOverloadedConstructorCallSignature(this.getDeclaringType()) }
}
/**
* An index signature declared in an interface.
*
* Example:
*
* ```
* interface I {
* [x: number]: number; // index signature
* }
* ```
*/
class IndexSignature extends @index_signature, MemberSignature {
FunctionExpr getBody() { result = this.getChildExpr(1) }
/** Gets the interface or function type that declares this index signature. */
override InterfaceDefinition getDeclaringType() {
result = MemberSignature.super.getDeclaringType()
}
override string getAPrimaryQlClass() { result = "IndexSignature" }
}
private boolean getStaticness(AccessorMethodDefinition member) {
member.isStatic() and result = true
or
not member.isStatic() and result = false
}
pragma[nomagic]
private AccessorMethodDefinition getAnAccessorFromClass(
ClassDefinition cls, string name, boolean static
) {
result = cls.getMember(name) and
static = getStaticness(result)
}
pragma[nomagic]
private predicate getterSetterPair(GetterMethodDeclaration getter, SetterMethodDeclaration setter) {
exists(ClassDefinition cls, string name, boolean static |
getter = getAnAccessorFromClass(cls, name, static) and
setter = getAnAccessorFromClass(cls, name, static)
)
}