Skip to content

Commit cf24c9f

Browse files
authored
Merge pull request #1804 from asger-semmle/template-literal-tag
Approved by esben-semmle
2 parents 23b74b5 + 45d4b83 commit cf24c9f

File tree

10 files changed

+72
-18
lines changed

10 files changed

+72
-18
lines changed

javascript/extractor/src/com/semmle/js/ast/NodeCopier.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import com.semmle.ts.ast.InterfaceTypeExpr;
3232
import com.semmle.ts.ast.IntersectionTypeExpr;
3333
import com.semmle.ts.ast.IsTypeExpr;
34-
import com.semmle.ts.ast.UnaryTypeExpr;
3534
import com.semmle.ts.ast.KeywordTypeExpr;
3635
import com.semmle.ts.ast.MappedTypeExpr;
3736
import com.semmle.ts.ast.NamespaceDeclaration;
@@ -44,6 +43,7 @@
4443
import com.semmle.ts.ast.TypeAssertion;
4544
import com.semmle.ts.ast.TypeParameter;
4645
import com.semmle.ts.ast.TypeofTypeExpr;
46+
import com.semmle.ts.ast.UnaryTypeExpr;
4747
import com.semmle.ts.ast.UnionTypeExpr;
4848
import java.util.ArrayList;
4949
import java.util.List;
@@ -413,7 +413,8 @@ public TemplateLiteral visit(TemplateLiteral nd, Void q) {
413413

414414
@Override
415415
public TaggedTemplateExpression visit(TaggedTemplateExpression nd, Void q) {
416-
return new TaggedTemplateExpression(visit(nd.getLoc()), copy(nd.getTag()), copy(nd.getQuasi()));
416+
return new TaggedTemplateExpression(
417+
visit(nd.getLoc()), copy(nd.getTag()), copy(nd.getQuasi()), copy(nd.getTypeArguments()));
417418
}
418419

419420
@Override

javascript/extractor/src/com/semmle/js/ast/TaggedTemplateExpression.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
package com.semmle.js.ast;
22

3+
import com.semmle.ts.ast.ITypeExpression;
4+
import java.util.Collections;
5+
import java.util.List;
6+
37
/** A tagged template expression. */
48
public class TaggedTemplateExpression extends Expression {
59
private final Expression tag;
610
private final TemplateLiteral quasi;
11+
private final List<ITypeExpression> typeArguments;
712

8-
public TaggedTemplateExpression(SourceLocation loc, Expression tag, TemplateLiteral quasi) {
13+
public TaggedTemplateExpression(
14+
SourceLocation loc,
15+
Expression tag,
16+
TemplateLiteral quasi,
17+
List<ITypeExpression> typeArguments) {
918
super("TaggedTemplateExpression", loc);
1019
this.tag = tag;
1120
this.quasi = quasi;
21+
this.typeArguments = typeArguments;
22+
}
23+
24+
public TaggedTemplateExpression(SourceLocation loc, Expression tag, TemplateLiteral quasi) {
25+
this(loc, tag, quasi, Collections.emptyList());
1226
}
1327

1428
@Override
@@ -25,4 +39,9 @@ public Expression getTag() {
2539
public TemplateLiteral getQuasi() {
2640
return quasi;
2741
}
42+
43+
/** The type arguments, or an empty list if there are none. */
44+
public List<ITypeExpression> getTypeArguments() {
45+
return typeArguments;
46+
}
2847
}

javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package com.semmle.js.extractor;
22

3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.List;
6+
import java.util.Set;
7+
import java.util.Stack;
8+
39
import com.semmle.js.ast.AClass;
410
import com.semmle.js.ast.AFunction;
511
import com.semmle.js.ast.AFunctionExpression;
@@ -125,7 +131,6 @@
125131
import com.semmle.ts.ast.InterfaceTypeExpr;
126132
import com.semmle.ts.ast.IntersectionTypeExpr;
127133
import com.semmle.ts.ast.IsTypeExpr;
128-
import com.semmle.ts.ast.UnaryTypeExpr;
129134
import com.semmle.ts.ast.KeywordTypeExpr;
130135
import com.semmle.ts.ast.MappedTypeExpr;
131136
import com.semmle.ts.ast.NamespaceDeclaration;
@@ -139,15 +144,11 @@
139144
import com.semmle.ts.ast.TypeExpression;
140145
import com.semmle.ts.ast.TypeParameter;
141146
import com.semmle.ts.ast.TypeofTypeExpr;
147+
import com.semmle.ts.ast.UnaryTypeExpr;
142148
import com.semmle.ts.ast.UnionTypeExpr;
143149
import com.semmle.util.collections.CollectionUtil;
144150
import com.semmle.util.trap.TrapWriter;
145151
import com.semmle.util.trap.TrapWriter.Label;
146-
import java.util.ArrayList;
147-
import java.util.Collections;
148-
import java.util.List;
149-
import java.util.Set;
150-
import java.util.Stack;
151152

152153
/** Extractor for AST-based information; invoked by the {@link JSExtractor}. */
153154
public class ASTExtractor {
@@ -1120,6 +1121,7 @@ public Label visit(TaggedTemplateExpression nd, Context c) {
11201121
Label key = super.visit(nd, c);
11211122
visit(nd.getTag(), key, 0);
11221123
visit(nd.getQuasi(), key, 1);
1124+
visitAll(nd.getTypeArguments(), key, IdContext.typeBind, 2);
11231125
return key;
11241126
}
11251127

javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
package com.semmle.js.parser;
22

3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.LinkedHashMap;
6+
import java.util.List;
7+
import java.util.Map;
8+
import java.util.regex.Matcher;
9+
import java.util.regex.Pattern;
10+
311
import com.google.gson.JsonArray;
412
import com.google.gson.JsonElement;
513
import com.google.gson.JsonNull;
@@ -126,7 +134,6 @@
126134
import com.semmle.ts.ast.InterfaceTypeExpr;
127135
import com.semmle.ts.ast.IntersectionTypeExpr;
128136
import com.semmle.ts.ast.IsTypeExpr;
129-
import com.semmle.ts.ast.UnaryTypeExpr;
130137
import com.semmle.ts.ast.KeywordTypeExpr;
131138
import com.semmle.ts.ast.MappedTypeExpr;
132139
import com.semmle.ts.ast.NamespaceDeclaration;
@@ -139,15 +146,9 @@
139146
import com.semmle.ts.ast.TypeAssertion;
140147
import com.semmle.ts.ast.TypeParameter;
141148
import com.semmle.ts.ast.TypeofTypeExpr;
149+
import com.semmle.ts.ast.UnaryTypeExpr;
142150
import com.semmle.ts.ast.UnionTypeExpr;
143151
import com.semmle.util.collections.CollectionUtil;
144-
import java.util.ArrayList;
145-
import java.util.Collections;
146-
import java.util.LinkedHashMap;
147-
import java.util.List;
148-
import java.util.Map;
149-
import java.util.regex.Matcher;
150-
import java.util.regex.Pattern;
151152

152153
/**
153154
* Utility class for converting a <a
@@ -2034,7 +2035,8 @@ private Node convertSwitchStatement(JsonObject node, SourceLocation loc) throws
20342035
private Node convertTaggedTemplateExpression(JsonObject node, SourceLocation loc)
20352036
throws ParseError {
20362037
return new TaggedTemplateExpression(
2037-
loc, convertChild(node, "tag"), convertChild(node, "template"));
2038+
loc, convertChild(node, "tag"), convertChild(node, "template"),
2039+
convertChildrenAsTypes(node, "typeArguments"));
20382040
}
20392041

20402042
private Node convertTemplateExpression(JsonObject node, SourceLocation loc) throws ParseError {

javascript/ql/src/semmle/javascript/Templates.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ class TaggedTemplateExpr extends Expr, @taggedtemplateexpr {
1818
/** Gets the tagged template itself. */
1919
TemplateLiteral getTemplate() { result = getChildExpr(1) }
2020

21+
/** Gets the `i`th type argument to the tag of this template literal. */
22+
TypeExpr getTypeArgument(int i) { i >= 0 and result = getChildTypeExpr(2 + i) }
23+
24+
/** Gets a type argument of the tag of this template literal. */
25+
TypeExpr getATypeArgument() { result = getTypeArgument(_) }
26+
27+
/** Gets the number of type arguments appearing on the tag of this template literal. */
28+
int getNumTypeArgument() { result = count(getATypeArgument()) }
29+
2130
override predicate isImpure() { any() }
2231
}
2332

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import javascript
2+
3+
query predicate test_TaggedTemplateLiteralTypeArgument(TaggedTemplateExpr expr, int i, TypeExpr arg) {
4+
arg = expr.getTypeArgument(i)
5+
}

javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,7 @@ test_ReturnTypes
257257
test_KeyofTypeExpr
258258
| tst.ts:49:16:49:30 | keyof Interface | tst.ts:49:22:49:30 | Interface |
259259
| tst.ts:113:26:113:35 | keyof Node | tst.ts:113:32:113:35 | Node |
260+
test_TaggedTemplateLiteralTypeArgument
261+
| tst.ts:139:37:139:58 | someTag ... `Hello` | 0 | tst.ts:139:45:139:50 | number |
262+
| tst.ts:140:37:140:66 | someTag ... `Hello` | 0 | tst.ts:140:45:140:50 | number |
263+
| tst.ts:140:37:140:66 | someTag ... `Hello` | 1 | tst.ts:140:53:140:58 | string |

javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tests.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ import RestTypeExpr
2727
import Containers
2828
import ReturnTypes
2929
import KeyofTypeExpr
30+
import TemplateLiterals

javascript/ql/test/library-tests/TypeScript/TypeAnnotations/tst.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,6 @@ let emptyTuple: [];
135135
let tupleWithRestElement: [number, ...string[]];
136136
let tupleWithOptionalAndRestElements: [number, string?, ...number[]];
137137
let unknownType: unknown;
138+
139+
let taggedTemplateLiteralTypeArg1 = someTag<number>`Hello`;
140+
let taggedTemplateLiteralTypeArg2 = someTag<number, string>`Hello`;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { SomeInterface } from 'somwhere1'; // OK
2+
import { AnotherInterface } from 'somwhere2'; // OK
3+
import { foo } from 'somewhere3'; // OK
4+
5+
let x = "world";
6+
7+
console.log(foo<SomeInterface>`Hello world`);
8+
console.log(foo<AnotherInterface>`Hello ${x}`);

0 commit comments

Comments
 (0)