JS: Implement getAnUnderlyingType().

This commit is contained in:
Asger F
2019-04-16 11:42:50 +01:00
parent 8458a64642
commit 454fff1398
5 changed files with 40 additions and 24 deletions

View File

@@ -3,7 +3,6 @@
*/
import Customizations
import semmle.javascript.Aliases
import semmle.javascript.AMD
import semmle.javascript.AST

View File

@@ -173,24 +173,24 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
result = getAReturnStmt().getExpr()
}
/**
/**
* Gets a return from a function which has undefined value (that is, implicit
* returns and returns without expressions).
*
* Functions can have undefined returns in a few different ways:
*
*
* 1. An explicit return statement with no expression (the statement `return;`)
*
*
* 2. An implicit return resulting from an expression executing as the last thing
* in the function. For example, the test in a final `if` statement:
*
*
* ```
* function foo() {
* ...
* if (test) { return 1; }
* }
* ```
*
*
* Some things look like they might return undefined but actually don't because
* the containing functioning doesn't return at all. For instance, `throw`
* statements prevent the containing function from returning, so they don't count
@@ -199,13 +199,12 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
* exclude yields entirely. Likewise, we exclude generator functions from
* consideration, as well as asynchronous functions, since calls to both produce
* something distinct from what's explicitly returned by the function.
*
*
* Despite the fact that yield expressions are invalid outside of generators, we
* include them anyway just to ensure that we're not relying on a perfect analysis
* of a function to be a generator, and instead are looking also explicitly at the
* return sites.
*/
ConcreteControlFlowNode getAnUndefinedReturn() {
not this.getBody() instanceof Expr and
not this.isGenerator() and
@@ -216,7 +215,7 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
result.getContainer() = this and
result.isAFinalNode()
}
/**
* Gets the function whose `this` binding a `this` expression in this function refers to,
* which is the nearest enclosing non-arrow function.

View File

@@ -154,21 +154,17 @@ class JSDocVoidTypeExpr extends @jsdoc_void_type_expr, JSDocTypeExpr {
class JSDocNamedTypeExpr extends @jsdoc_named_type_expr, JSDocTypeExpr {
/** Gets the name of the type the expression refers to. */
string getName() { result = toString() }
override predicate isString() {
getName() = "string"
}
override predicate isString() { getName() = "string" }
override predicate isStringy() {
exists(string name | name = getName() |
name = "string" or
name = "String"
)
}
override predicate isNumber() {
getName() = "number"
}
override predicate isNumber() { getName() = "number" }
override predicate isNumbery() {
exists(string name | name = getName() |
@@ -182,9 +178,7 @@ class JSDocNamedTypeExpr extends @jsdoc_named_type_expr, JSDocTypeExpr {
)
}
override predicate isBoolean() {
getName() = "boolean"
}
override predicate isBoolean() { getName() = "boolean" }
override predicate isBooleany() {
getName() = "boolean" or
@@ -192,9 +186,7 @@ class JSDocNamedTypeExpr extends @jsdoc_named_type_expr, JSDocTypeExpr {
getName() = "bool"
}
override predicate isRawFunction() {
getName() = "Function"
}
override predicate isRawFunction() { getName() = "Function" }
}
/**
@@ -228,6 +220,8 @@ class JSDocNullableTypeExpr extends @jsdoc_nullable_type_expr, JSDocTypeExpr {
/** Holds if the `?` operator of this type expression is written in prefix notation. */
predicate isPrefix() { jsdoc_prefix_qualifier(this) }
override JSDocTypeExpr getAnUnderlyingType() { result = getTypeExpr().getAnUnderlyingType() }
}
/**
@@ -239,6 +233,8 @@ class JSDocNonNullableTypeExpr extends @jsdoc_non_nullable_type_expr, JSDocTypeE
/** Holds if the `!` operator of this type expression is written in prefix notation. */
predicate isPrefix() { jsdoc_prefix_qualifier(this) }
override JSDocTypeExpr getAnUnderlyingType() { result = getTypeExpr().getAnUnderlyingType() }
}
/**
@@ -277,6 +273,8 @@ class JSDocArrayTypeExpr extends @jsdoc_array_type_expr, JSDocTypeExpr {
class JSDocUnionTypeExpr extends @jsdoc_union_type_expr, JSDocTypeExpr {
/** Gets one of the type alternatives of this union type. */
JSDocTypeExpr getAnAlternative() { result = getChild(_) }
override JSDocTypeExpr getAnUnderlyingType() { result = getAnAlternative().getAnUnderlyingType() }
}
/**
@@ -305,6 +303,8 @@ class JSDocFunctionTypeExpr extends @jsdoc_function_type_expr, JSDocTypeExpr {
class JSDocOptionalParameterTypeExpr extends @jsdoc_optional_type_expr, JSDocTypeExpr {
/** Gets the underlying type of this optional type. */
JSDocTypeExpr getUnderlyingType() { result = getChild(0) }
override JSDocTypeExpr getAnUnderlyingType() { result = getUnderlyingType().getAnUnderlyingType() }
}
/**

View File

@@ -67,4 +67,12 @@ class TypeAnnotation extends @type_annotation {
/** Holds if this is the `const` keyword, occurring in a type assertion such as `x as const`. */
predicate isConstKeyword() { none() }
/**
* Repeatedly unfolds unions, intersections, parentheses, and nullability/readonly modifiers and gets any of the underlying types,
* or this type itself if it cannot be unfolded.
*
* Note that this only unfolds the syntax of the type annotation. Type aliases are not followed to their definition.
*/
TypeAnnotation getAnUnderlyingType() { result = this }
}

View File

@@ -786,6 +786,8 @@ class UnionTypeExpr extends @uniontypeexpr, TypeExpr {
/** Gets the number of types in the union. This is always at least two. */
int getNumElementType() { result = count(getAnElementType()) }
override TypeExpr getAnUnderlyingType() { result = getAnElementType().getAnUnderlyingType() }
}
/**
@@ -813,6 +815,8 @@ class IntersectionTypeExpr extends @intersectiontypeexpr, TypeExpr {
/** Gets the number of operands to the intersection type. This is always at least two. */
int getNumElementType() { result = count(getAnElementType()) }
override TypeExpr getAnUnderlyingType() { result = getAnElementType().getAnUnderlyingType() }
}
/**
@@ -823,6 +827,8 @@ class ParenthesizedTypeExpr extends @parenthesizedtypeexpr, TypeExpr {
TypeExpr getElementType() { result = getChildTypeExpr(0) }
override TypeExpr stripParens() { result = getElementType().stripParens() }
override TypeExpr getAnUnderlyingType() { result = getElementType().getAnUnderlyingType() }
}
/**
@@ -902,6 +908,8 @@ class IsTypeExpr extends @istypeexpr, TypeExpr {
class OptionalTypeExpr extends @optionaltypeexpr, TypeExpr {
/** Gets the type `T` in `T?` */
TypeExpr getElementType() { result = getChildTypeExpr(0) }
override TypeExpr getAnUnderlyingType() { result = getElementType().getAnUnderlyingType() }
}
/**
@@ -921,6 +929,8 @@ class RestTypeExpr extends @resttypeexpr, TypeExpr {
class ReadonlyTypeExpr extends @readonlytypeexpr, TypeExpr {
/** Gets the type `T` in `readonly T`. */
TypeExpr getElementType() { result = getChildTypeExpr(0) }
override TypeExpr getAnUnderlyingType() { result = getElementType().getAnUnderlyingType() }
}
/**