TS: Support assertion types

This commit is contained in:
Asger F
2019-10-30 14:51:00 +00:00
parent 4e7b987fa3
commit b81931e402
12 changed files with 113 additions and 15 deletions

View File

@@ -719,7 +719,10 @@ public class NodeCopier implements Visitor<Void, INode> {
@Override
public INode visit(PredicateTypeExpr nd, Void c) {
return new PredicateTypeExpr(
visit(nd.getLoc()), copy(nd.getExpression()), copy(nd.getTypeExpr()));
visit(nd.getLoc()),
copy(nd.getExpression()),
copy(nd.getTypeExpr()),
nd.hasAssertsKeyword());
}
@Override

View File

@@ -1711,6 +1711,9 @@ public class ASTExtractor {
Label key = super.visit(nd, c);
visit(nd.getExpression(), key, 0, IdContext.varInTypeBind);
visit(nd.getTypeExpr(), key, 1, IdContext.typeBind);
if (nd.hasAssertsKeyword()) {
trapwriter.addTuple("hasAssertsKeyword", key);
}
return key;
}

View File

@@ -2200,7 +2200,10 @@ public class TypeScriptASTConverter {
private Node convertTypePredicate(JsonObject node, SourceLocation loc) throws ParseError {
return new PredicateTypeExpr(
loc, convertChildAsType(node, "parameterName"), convertChildAsType(node, "type"));
loc,
convertChildAsType(node, "parameterName"),
convertChildAsType(node, "type"),
node.has("assertsModifier"));
}
private Node convertTypeReference(JsonObject node, SourceLocation loc) throws ParseError {

View File

@@ -10,11 +10,17 @@ import com.semmle.js.ast.Visitor;
public class PredicateTypeExpr extends TypeExpression {
private final ITypeExpression expression;
private final ITypeExpression type;
private final boolean hasAssertsKeyword;
public PredicateTypeExpr(SourceLocation loc, ITypeExpression expression, ITypeExpression type) {
public PredicateTypeExpr(
SourceLocation loc,
ITypeExpression expression,
ITypeExpression type,
boolean hasAssertsKeyword) {
super("PredicateTypeExpr", loc);
this.expression = expression;
this.type = type;
this.hasAssertsKeyword = hasAssertsKeyword;
}
/** Returns the <tt>E</tt> in <tt>E is T</tt>. */
@@ -27,6 +33,10 @@ public class PredicateTypeExpr extends TypeExpression {
return type;
}
public boolean hasAssertsKeyword() {
return hasAssertsKeyword;
}
@Override
public <C, R> R accept(Visitor<C, R> v, C c) {
return v.visit(this, c);

View File

@@ -914,20 +914,49 @@ class TypeofTypeExpr extends @typeoftypeexpr, TypeExpr {
}
/**
* A type of form `E is T` where `E` is a parameter name or `this`, and `T` is a type.
* A function return type that refines the type of one of its parameters or `this`.
*
* This can only occur as the return type of a function type.
* Examples:
* ```js
* function f(x): x is string {}
* function f(x): asserts x is string {}
* function f(x): asserts x {}
* ```
*/
class IsTypeExpr extends @predicatetypeexpr, TypeExpr {
class PredicateTypeExpr extends @predicatetypeexpr, TypeExpr {
/**
* Gets the parameter name or `this` token `E` in `E is T`.
*/
VarTypeAccess getParameterName() { result = this.getChildTypeExpr(0) }
/**
* Gets the type `T` in `E is T`.
* Gets the type `T` in `E is T` or `asserts E is T`.
*
* Has no results for types of form `asserts E`.
*/
TypeExpr getPredicateType() { result = this.getChildTypeExpr(1) }
/**
* Holds if this is a type of form `asserts E is T` or `asserts E`.
*/
predicate hasAssertsKeyword() {
hasAssertsKeyword(this)
}
}
/**
* A function return type of form `x is T` or `asserts x is T`.
*
* Examples:
* ```js
* function f(x): x is string {}
* function f(x): asserts x is string {}
* ```
*/
class IsTypeExpr extends PredicateTypeExpr {
IsTypeExpr() {
exists(getPredicateType())
}
}
/**
@@ -966,15 +995,16 @@ class ReadonlyTypeExpr extends @readonlytypeexpr, TypeExpr {
*
* This can occur as
* - part of the operand to a `typeof` type, or
* - as the first operand to an `is` type.
* - as the first operand to a predicate type
*
* For example, it may occur as the `E` in these examples:
* ```
* var x : typeof E
* function f(...) : E is T {}
* function f(...) : asserts E {}
* ```
*
* In the latter case, this may also refer to the pseudo-variable `this`.
* In the latter two cases, this may also refer to the pseudo-variable `this`.
*/
class VarTypeAccess extends @vartypeaccess, TypeExpr { }

View File

@@ -637,6 +637,8 @@ case @type.kind of
@unionorintersectiontype = @uniontype | @intersectiontype;
@typevariabletype = @canonicaltypevariabletype | @lexicaltypevariabletype;
hasAssertsKeyword(int node: @predicatetypeexpr ref);
@typed_ast_node = @expr | @typeexpr | @function;
ast_node_type(
unique int node: @typed_ast_node ref,

View File

@@ -7918,6 +7918,17 @@
<dependencies/>
</relation>
<relation>
<name>hasAssertsKeyword</name>
<cardinality>66</cardinality>
<columnsizes>
<e>
<k>stmt</k>
<v>66</v>
</e>
</columnsizes>
<dependencies/>
</relation>
<relation>
<name>isAbstractMember</name>
<cardinality>66</cardinality>
<columnsizes>

View File

@@ -1,5 +0,0 @@
import javascript
query predicate test_IsTypeExpr(IsTypeExpr type, VarTypeAccess res0, TypeExpr res1) {
res0 = type.getParameterName() and res1 = type.getPredicateType()
}

View File

@@ -0,0 +1,13 @@
import javascript
query predicate test_IsTypeExpr(IsTypeExpr type, VarTypeAccess res0, TypeExpr res1) {
res0 = type.getParameterName() and res1 = type.getPredicateType()
}
query predicate test_PredicateTypeExpr(PredicateTypeExpr type, VarTypeAccess res0) {
res0 = type.getParameterName()
}
query predicate test_hasAssertsKeyword(PredicateTypeExpr type) {
type.hasAssertsKeyword()
}

View File

@@ -105,6 +105,9 @@ test_VariableTypes
| tst.ts:135:5:135:24 | tupleWithRestElement | tupleWithRestElement | tst.ts:135:27:135:47 | [number ... ring[]] |
| tst.ts:136:5:136:36 | tupleWi ... lements | tupleWithOptionalAndRestElements | tst.ts:136:39:136:68 | [number ... mber[]] |
| tst.ts:137:5:137:15 | unknownType | unknownType | tst.ts:137:18:137:24 | unknown |
| tst.ts:142:17:142:25 | condition | condition | tst.ts:142:28:142:30 | any |
| tst.ts:142:33:142:35 | msg | msg | tst.ts:142:39:142:44 | string |
| tst.ts:148:25:148:27 | val | val | tst.ts:148:30:148:32 | any |
test_QualifiedTypeAccess
| tst.ts:63:19:63:21 | N.I | tst.ts:63:19:63:19 | N | tst.ts:63:21:63:21 | I |
| tst.ts:64:20:64:24 | N.M.I | tst.ts:64:20:64:22 | N.M | tst.ts:64:24:64:24 | I |
@@ -148,6 +151,17 @@ test_IsTypeExpr
| tst.ts:76:21:76:32 | that is Leaf | tst.ts:76:21:76:24 | that | tst.ts:76:29:76:32 | Leaf |
| tst.ts:80:36:80:55 | x is Generic<Leaf[]> | tst.ts:80:36:80:36 | x | tst.ts:80:41:80:55 | Generic<Leaf[]> |
| tst.ts:81:38:81:50 | x is typeof x | tst.ts:81:38:81:38 | x | tst.ts:81:43:81:50 | typeof x |
| tst.ts:148:36:148:56 | asserts ... string | tst.ts:148:44:148:46 | val | tst.ts:148:51:148:56 | string |
test_PredicateTypeExpr
| tst.ts:75:17:75:28 | this is Leaf | tst.ts:75:17:75:20 | this |
| tst.ts:76:21:76:32 | that is Leaf | tst.ts:76:21:76:24 | that |
| tst.ts:80:36:80:55 | x is Generic<Leaf[]> | tst.ts:80:36:80:36 | x |
| tst.ts:81:38:81:50 | x is typeof x | tst.ts:81:38:81:38 | x |
| tst.ts:142:48:142:64 | asserts condition | tst.ts:142:56:142:64 | condition |
| tst.ts:148:36:148:56 | asserts ... string | tst.ts:148:44:148:46 | val |
test_hasAssertsKeyword
| tst.ts:142:48:142:64 | asserts condition |
| tst.ts:148:36:148:56 | asserts ... string |
test_ThisParameterTypes
| function hasThisParam | tst.ts:116:29:116:32 | void |
| method hasThisParam of interface InterfaceWithThisParam | tst.ts:119:22:119:43 | Interfa ... isParam |
@@ -254,6 +268,8 @@ test_ReturnTypes
| tst.ts:94:1:94:37 | functio ... rn x; } | function f1 | tst.ts:94:23:94:23 | S |
| tst.ts:95:1:95:53 | functio ... x,y]; } | function f2 | tst.ts:95:31:95:35 | [S,T] |
| tst.ts:96:1:96:52 | functio ... rn x; } | function f3 | tst.ts:96:38:96:38 | S |
| tst.ts:142:1:146:1 | functio ... )\\n }\\n} | function assert | tst.ts:142:48:142:64 | asserts condition |
| tst.ts:148:1:152:1 | functio ... ;\\n }\\n} | function assertIsString | tst.ts:148:36:148:56 | asserts ... string |
test_KeyofTypeExpr
| tst.ts:49:16:49:30 | keyof Interface | tst.ts:49:22:49:30 | Interface |
| tst.ts:113:26:113:35 | keyof Node | tst.ts:113:32:113:35 | Node |

View File

@@ -11,7 +11,7 @@ import GenericTypeExpr
import IntersectionTypeExpr
import FunctionTypeReturns
import InterfaceTypeExpr
import IsTypeExpr
import PredicateTypeExpr
import ThisParameterTypes
import ChildIndex
import TypeArguments

View File

@@ -138,3 +138,15 @@ let unknownType: unknown;
let taggedTemplateLiteralTypeArg1 = someTag<number>`Hello`;
let taggedTemplateLiteralTypeArg2 = someTag<number, string>`Hello`;
function assert(condition: any, msg?: string): asserts condition {
if (!condition) {
throw new AssertionError(msg)
}
}
function assertIsString(val: any): asserts val is string {
if (typeof val !== "string") {
throw new AssertionError("Not a string!");
}
}