TS: Extract type args to tagged template exprs

This commit is contained in:
Asger F
2019-08-22 18:05:49 +01:00
parent 7698240484
commit 45d4b83fc8
10 changed files with 72 additions and 18 deletions

View File

@@ -31,7 +31,6 @@ import com.semmle.ts.ast.InterfaceDeclaration;
import com.semmle.ts.ast.InterfaceTypeExpr;
import com.semmle.ts.ast.IntersectionTypeExpr;
import com.semmle.ts.ast.IsTypeExpr;
import com.semmle.ts.ast.UnaryTypeExpr;
import com.semmle.ts.ast.KeywordTypeExpr;
import com.semmle.ts.ast.MappedTypeExpr;
import com.semmle.ts.ast.NamespaceDeclaration;
@@ -44,6 +43,7 @@ import com.semmle.ts.ast.TypeAliasDeclaration;
import com.semmle.ts.ast.TypeAssertion;
import com.semmle.ts.ast.TypeParameter;
import com.semmle.ts.ast.TypeofTypeExpr;
import com.semmle.ts.ast.UnaryTypeExpr;
import com.semmle.ts.ast.UnionTypeExpr;
import java.util.ArrayList;
import java.util.List;
@@ -413,7 +413,8 @@ public class NodeCopier implements Visitor<Void, INode> {
@Override
public TaggedTemplateExpression visit(TaggedTemplateExpression nd, Void q) {
return new TaggedTemplateExpression(visit(nd.getLoc()), copy(nd.getTag()), copy(nd.getQuasi()));
return new TaggedTemplateExpression(
visit(nd.getLoc()), copy(nd.getTag()), copy(nd.getQuasi()), copy(nd.getTypeArguments()));
}
@Override

View File

@@ -1,14 +1,28 @@
package com.semmle.js.ast;
import com.semmle.ts.ast.ITypeExpression;
import java.util.Collections;
import java.util.List;
/** A tagged template expression. */
public class TaggedTemplateExpression extends Expression {
private final Expression tag;
private final TemplateLiteral quasi;
private final List<ITypeExpression> typeArguments;
public TaggedTemplateExpression(SourceLocation loc, Expression tag, TemplateLiteral quasi) {
public TaggedTemplateExpression(
SourceLocation loc,
Expression tag,
TemplateLiteral quasi,
List<ITypeExpression> typeArguments) {
super("TaggedTemplateExpression", loc);
this.tag = tag;
this.quasi = quasi;
this.typeArguments = typeArguments;
}
public TaggedTemplateExpression(SourceLocation loc, Expression tag, TemplateLiteral quasi) {
this(loc, tag, quasi, Collections.emptyList());
}
@Override
@@ -25,4 +39,9 @@ public class TaggedTemplateExpression extends Expression {
public TemplateLiteral getQuasi() {
return quasi;
}
/** The type arguments, or an empty list if there are none. */
public List<ITypeExpression> getTypeArguments() {
return typeArguments;
}
}

View File

@@ -1,5 +1,11 @@
package com.semmle.js.extractor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import com.semmle.js.ast.AClass;
import com.semmle.js.ast.AFunction;
import com.semmle.js.ast.AFunctionExpression;
@@ -125,7 +131,6 @@ import com.semmle.ts.ast.InterfaceDeclaration;
import com.semmle.ts.ast.InterfaceTypeExpr;
import com.semmle.ts.ast.IntersectionTypeExpr;
import com.semmle.ts.ast.IsTypeExpr;
import com.semmle.ts.ast.UnaryTypeExpr;
import com.semmle.ts.ast.KeywordTypeExpr;
import com.semmle.ts.ast.MappedTypeExpr;
import com.semmle.ts.ast.NamespaceDeclaration;
@@ -139,15 +144,11 @@ import com.semmle.ts.ast.TypeAssertion;
import com.semmle.ts.ast.TypeExpression;
import com.semmle.ts.ast.TypeParameter;
import com.semmle.ts.ast.TypeofTypeExpr;
import com.semmle.ts.ast.UnaryTypeExpr;
import com.semmle.ts.ast.UnionTypeExpr;
import com.semmle.util.collections.CollectionUtil;
import com.semmle.util.trap.TrapWriter;
import com.semmle.util.trap.TrapWriter.Label;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.Stack;
/** Extractor for AST-based information; invoked by the {@link JSExtractor}. */
public class ASTExtractor {
@@ -1120,6 +1121,7 @@ public class ASTExtractor {
Label key = super.visit(nd, c);
visit(nd.getTag(), key, 0);
visit(nd.getQuasi(), key, 1);
visitAll(nd.getTypeArguments(), key, IdContext.typeBind, 2);
return key;
}

View File

@@ -1,5 +1,13 @@
package com.semmle.js.parser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
@@ -126,7 +134,6 @@ import com.semmle.ts.ast.InterfaceDeclaration;
import com.semmle.ts.ast.InterfaceTypeExpr;
import com.semmle.ts.ast.IntersectionTypeExpr;
import com.semmle.ts.ast.IsTypeExpr;
import com.semmle.ts.ast.UnaryTypeExpr;
import com.semmle.ts.ast.KeywordTypeExpr;
import com.semmle.ts.ast.MappedTypeExpr;
import com.semmle.ts.ast.NamespaceDeclaration;
@@ -139,15 +146,9 @@ import com.semmle.ts.ast.TypeAliasDeclaration;
import com.semmle.ts.ast.TypeAssertion;
import com.semmle.ts.ast.TypeParameter;
import com.semmle.ts.ast.TypeofTypeExpr;
import com.semmle.ts.ast.UnaryTypeExpr;
import com.semmle.ts.ast.UnionTypeExpr;
import com.semmle.util.collections.CollectionUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility class for converting a <a
@@ -2034,7 +2035,8 @@ public class TypeScriptASTConverter {
private Node convertTaggedTemplateExpression(JsonObject node, SourceLocation loc)
throws ParseError {
return new TaggedTemplateExpression(
loc, convertChild(node, "tag"), convertChild(node, "template"));
loc, convertChild(node, "tag"), convertChild(node, "template"),
convertChildrenAsTypes(node, "typeArguments"));
}
private Node convertTemplateExpression(JsonObject node, SourceLocation loc) throws ParseError {

View File

@@ -18,6 +18,15 @@ class TaggedTemplateExpr extends Expr, @taggedtemplateexpr {
/** Gets the tagged template itself. */
TemplateLiteral getTemplate() { result = getChildExpr(1) }
/** Gets the `i`th type argument to the tag of this template literal. */
TypeExpr getTypeArgument(int i) { i >= 0 and result = getChildTypeExpr(2 + i) }
/** Gets a type argument of the tag of this template literal. */
TypeExpr getATypeArgument() { result = getTypeArgument(_) }
/** Gets the number of type arguments appearing on the tag of this template literal. */
int getNumTypeArgument() { result = count(getATypeArgument()) }
override predicate isImpure() { any() }
}

View File

@@ -0,0 +1,5 @@
import javascript
query predicate test_TaggedTemplateLiteralTypeArgument(TaggedTemplateExpr expr, int i, TypeExpr arg) {
arg = expr.getTypeArgument(i)
}

View File

@@ -257,3 +257,7 @@ test_ReturnTypes
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 |
test_TaggedTemplateLiteralTypeArgument
| tst.ts:139:37:139:58 | someTag ... `Hello` | 0 | tst.ts:139:45:139:50 | number |
| tst.ts:140:37:140:66 | someTag ... `Hello` | 0 | tst.ts:140:45:140:50 | number |
| tst.ts:140:37:140:66 | someTag ... `Hello` | 1 | tst.ts:140:53:140:58 | string |

View File

@@ -27,3 +27,4 @@ import RestTypeExpr
import Containers
import ReturnTypes
import KeyofTypeExpr
import TemplateLiterals

View File

@@ -135,3 +135,6 @@ let emptyTuple: [];
let tupleWithRestElement: [number, ...string[]];
let tupleWithOptionalAndRestElements: [number, string?, ...number[]];
let unknownType: unknown;
let taggedTemplateLiteralTypeArg1 = someTag<number>`Hello`;
let taggedTemplateLiteralTypeArg2 = someTag<number, string>`Hello`;

View File

@@ -0,0 +1,8 @@
import { SomeInterface } from 'somwhere1'; // OK
import { AnotherInterface } from 'somwhere2'; // OK
import { foo } from 'somewhere3'; // OK
let x = "world";
console.log(foo<SomeInterface>`Hello world`);
console.log(foo<AnotherInterface>`Hello ${x}`);