Skip to content

Commit 160fc48

Browse files
authored
Merge pull request #2896 from asger-semmle/typescript-3.8
TS: Support Typescript 3.8
2 parents aadb148 + 01309d7 commit 160fc48

File tree

36 files changed

+2926
-28
lines changed

36 files changed

+2926
-28
lines changed

change-notes/1.24/analysis-javascript.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## General improvements
44

5+
* TypeScript 3.8 is now supported.
6+
57
* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
68

79
* Imports with the `.js` extension can now be resolved to a TypeScript file,

javascript/extractor/lib/typescript/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "typescript-parser-wrapper",
33
"private": true,
44
"dependencies": {
5-
"typescript": "3.7.5"
5+
"typescript": "3.8.2"
66
},
77
"scripts": {
88
"build": "tsc --project tsconfig.json",

javascript/extractor/lib/typescript/yarn.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,9 @@ tsutils@^2.12.1:
225225
dependencies:
226226
tslib "^1.8.1"
227227

228-
typescript@3.7.5:
229-
version "3.7.5"
230-
resolved "typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
228+
typescript@3.8.2:
229+
version "3.8.2"
230+
resolved typescript-3.8.2.tgz#91d6868aaead7da74f493c553aeff76c0c0b1d5a
231231

232232
wrappy@1:
233233
version "1.0.2"

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,21 @@ public class ExportNamedDeclaration extends ExportDeclaration {
1515
private final Statement declaration;
1616
private final List<ExportSpecifier> specifiers;
1717
private final Literal source;
18+
private final boolean hasTypeKeyword;
1819

1920
public ExportNamedDeclaration(
2021
SourceLocation loc, Statement declaration, List<ExportSpecifier> specifiers, Literal source) {
22+
this(loc, declaration, specifiers, source, false);
23+
}
24+
25+
public ExportNamedDeclaration(
26+
SourceLocation loc, Statement declaration, List<ExportSpecifier> specifiers, Literal source,
27+
boolean hasTypeKeyword) {
2128
super("ExportNamedDeclaration", loc);
2229
this.declaration = declaration;
2330
this.specifiers = specifiers;
2431
this.source = source;
32+
this.hasTypeKeyword = hasTypeKeyword;
2533
}
2634

2735
public Statement getDeclaration() {
@@ -48,4 +56,9 @@ public boolean hasSource() {
4856
public <C, R> R accept(Visitor<C, R> v, C c) {
4957
return v.visit(this, c);
5058
}
59+
60+
/** Returns true if this is an <code>export type</code> declaration. */
61+
public boolean hasTypeKeyword() {
62+
return hasTypeKeyword;
63+
}
5164
}

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

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

3-
import com.semmle.ts.ast.INodeWithSymbol;
43
import java.util.List;
54

5+
import com.semmle.ts.ast.INodeWithSymbol;
6+
67
/**
78
* An import declaration, which can be of one of the following forms:
89
*
@@ -24,10 +25,17 @@ public class ImportDeclaration extends Statement implements INodeWithSymbol {
2425

2526
private int symbol = -1;
2627

28+
private boolean hasTypeKeyword;
29+
2730
public ImportDeclaration(SourceLocation loc, List<ImportSpecifier> specifiers, Literal source) {
31+
this(loc, specifiers, source, false);
32+
}
33+
34+
public ImportDeclaration(SourceLocation loc, List<ImportSpecifier> specifiers, Literal source, boolean hasTypeKeyword) {
2835
super("ImportDeclaration", loc);
2936
this.specifiers = specifiers;
3037
this.source = source;
38+
this.hasTypeKeyword = hasTypeKeyword;
3139
}
3240

3341
public Literal getSource() {
@@ -52,4 +60,9 @@ public int getSymbol() {
5260
public void setSymbol(int symbol) {
5361
this.symbol = symbol;
5462
}
63+
64+
/** Returns true if this is an <code>import type</code> declaration. */
65+
public boolean hasTypeKeyword() {
66+
return hasTypeKeyword;
67+
}
5568
}

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

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,22 @@ public enum IdContext {
250250
/** An identifier that declares a variable and a namespace. */
251251
varAndNamespaceDecl,
252252

253+
/**
254+
* An identifier that occurs in a type-only import.
255+
*
256+
* These may declare a type and/or a namespace, but for compatibility with our AST,
257+
* must be emitted as a VarDecl (with no variable binding).
258+
*/
259+
typeOnlyImport,
260+
261+
/**
262+
* An identifier that occurs in a type-only export.
263+
*
264+
* These may refer to a type and/or a namespace, but for compatibility with our AST,
265+
* must be emitted as an ExportVarAccess (with no variable binding).
266+
*/
267+
typeOnlyExport,
268+
253269
/** An identifier that declares a variable, type, and namepsace. */
254270
varAndTypeAndNamespaceDecl,
255271

@@ -278,7 +294,8 @@ public enum IdContext {
278294
* True if this occurs as part of a type annotation, i.e. it is {@link #typeBind} or {@link
279295
* #typeDecl}, {@link #typeLabel}, {@link #varInTypeBind}, or {@link #namespaceBind}.
280296
*
281-
* <p>Does not hold for {@link #varAndTypeDecl}.
297+
* <p>Does not hold for {@link #varAndTypeDecl}, {@link #typeOnlyImport}, or @{link {@link #typeOnlyExport}
298+
* as these do not occur in type annotations.
282299
*/
283300
public boolean isInsideType() {
284301
return this == typeBind
@@ -488,6 +505,14 @@ public Label visit(Identifier nd, Context c) {
488505
addVariableBinding("decl", key, name);
489506
addNamespaceBinding("namespacedecl", key, name);
490507
break;
508+
case typeOnlyImport:
509+
addTypeBinding("typedecl", key, name);
510+
addNamespaceBinding("namespacedecl", key, name);
511+
break;
512+
case typeOnlyExport:
513+
addTypeBinding("typebind", key, name);
514+
addNamespaceBinding("namespacebind", key, name);
515+
break;
491516
case varAndTypeAndNamespaceDecl:
492517
addVariableBinding("decl", key, name);
493518
addTypeBinding("typedecl", key, name);
@@ -1538,7 +1563,14 @@ public Label visit(ExportNamedDeclaration nd, Context c) {
15381563
Label lbl = super.visit(nd, c);
15391564
visit(nd.getDeclaration(), lbl, -1);
15401565
visit(nd.getSource(), lbl, -2);
1541-
visitAll(nd.getSpecifiers(), lbl, nd.hasSource() ? IdContext.label : IdContext.export, 0);
1566+
IdContext childContext =
1567+
nd.hasSource() ? IdContext.label :
1568+
nd.hasTypeKeyword() ? IdContext.typeOnlyExport :
1569+
IdContext.export;
1570+
visitAll(nd.getSpecifiers(), lbl, childContext, 0);
1571+
if (nd.hasTypeKeyword()) {
1572+
trapwriter.addTuple("hasTypeKeyword", lbl);
1573+
}
15421574
return lbl;
15431575
}
15441576

@@ -1554,16 +1586,20 @@ public Label visit(ExportSpecifier nd, Context c) {
15541586
public Label visit(ImportDeclaration nd, Context c) {
15551587
Label lbl = super.visit(nd, c);
15561588
visit(nd.getSource(), lbl, -1);
1557-
visitAll(nd.getSpecifiers(), lbl);
1589+
IdContext childContext = nd.hasTypeKeyword() ? IdContext.typeOnlyImport : IdContext.varAndTypeAndNamespaceDecl;
1590+
visitAll(nd.getSpecifiers(), lbl, childContext, 0);
15581591
emitNodeSymbol(nd, lbl);
1592+
if (nd.hasTypeKeyword()) {
1593+
trapwriter.addTuple("hasTypeKeyword", lbl);
1594+
}
15591595
return lbl;
15601596
}
15611597

15621598
@Override
15631599
public Label visit(ImportSpecifier nd, Context c) {
15641600
Label lbl = super.visit(nd, c);
15651601
visit(nd.getImported(), lbl, 0, IdContext.label);
1566-
visit(nd.getLocal(), lbl, 1, IdContext.varAndTypeAndNamespaceDecl);
1602+
visit(nd.getLocal(), lbl, 1, c.idcontext);
15671603
return lbl;
15681604
}
15691605

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

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

3+
import java.util.EnumMap;
4+
import java.util.LinkedHashMap;
5+
import java.util.Map;
6+
37
import com.semmle.jcorn.TokenType;
48
import com.semmle.jcorn.jsx.JSXParser;
59
import com.semmle.js.ast.AssignmentExpression;
@@ -29,9 +33,6 @@
2933
import com.semmle.ts.ast.ExpressionWithTypeArguments;
3034
import com.semmle.ts.ast.TypeAssertion;
3135
import com.semmle.util.exception.CatastrophicError;
32-
import java.util.EnumMap;
33-
import java.util.LinkedHashMap;
34-
import java.util.Map;
3536

3637
/** Map from SpiderMonkey expression types to the numeric kinds used in the DB scheme. */
3738
public class ExprKinds {
@@ -154,6 +155,8 @@ public class ExprKinds {
154155
idKinds.put(IdContext.namespaceDecl, 78);
155156
idKinds.put(IdContext.varAndNamespaceDecl, 78);
156157
idKinds.put(IdContext.varAndTypeAndNamespaceDecl, 78);
158+
idKinds.put(IdContext.typeOnlyImport, 78);
159+
idKinds.put(IdContext.typeOnlyExport, 103);
157160
idKinds.put(IdContext.varBind, 79);
158161
idKinds.put(IdContext.export, 103);
159162
idKinds.put(IdContext.exportBase, 103);

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

Lines changed: 31 additions & 14 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;
@@ -34,6 +42,7 @@
3442
import com.semmle.js.ast.ExportDeclaration;
3543
import com.semmle.js.ast.ExportDefaultDeclaration;
3644
import com.semmle.js.ast.ExportNamedDeclaration;
45+
import com.semmle.js.ast.ExportNamespaceSpecifier;
3746
import com.semmle.js.ast.ExportSpecifier;
3847
import com.semmle.js.ast.Expression;
3948
import com.semmle.js.ast.ExpressionStatement;
@@ -144,13 +153,6 @@
144153
import com.semmle.ts.ast.UnionTypeExpr;
145154
import com.semmle.util.collections.CollectionUtil;
146155
import com.semmle.util.data.IntList;
147-
import java.util.ArrayList;
148-
import java.util.Collections;
149-
import java.util.LinkedHashMap;
150-
import java.util.List;
151-
import java.util.Map;
152-
import java.util.regex.Matcher;
153-
import java.util.regex.Pattern;
154156

155157
/**
156158
* Utility class for converting a <a
@@ -334,7 +336,11 @@ private Node convertNode(JsonObject node, String defaultKind) throws ParseError
334336
private Node convertNodeUntyped(JsonObject node, String defaultKind) throws ParseError {
335337
String kind = getKind(node);
336338
if (kind == null) kind = defaultKind;
337-
if (kind == null) kind = "Identifier";
339+
if (kind == null) {
340+
// Identifiers and PrivateIdentifiers do not have a "kind" property like other nodes.
341+
// Since we encode identifiers and private identifiers the same, default to Identifier.
342+
kind = "Identifier";
343+
}
338344
SourceLocation loc = getSourceLocation(node);
339345
switch (kind) {
340346
case "AnyKeyword":
@@ -441,6 +447,7 @@ private Node convertNodeUntyped(JsonObject node, String defaultKind) throws Pars
441447
case "FunctionType":
442448
return convertFunctionType(node, loc);
443449
case "Identifier":
450+
case "PrivateIdentifier":
444451
return convertIdentifier(node, loc);
445452
case "IfStatement":
446453
return convertIfStatement(node, loc);
@@ -507,6 +514,8 @@ private Node convertNodeUntyped(JsonObject node, String defaultKind) throws Pars
507514
return convertNamespaceDeclaration(node, loc);
508515
case "ModuleBlock":
509516
return convertModuleBlock(node, loc);
517+
case "NamespaceExport":
518+
return convertNamespaceExport(node, loc);
510519
case "NamespaceExportDeclaration":
511520
return convertNamespaceExportDeclaration(node, loc);
512521
case "NamespaceImport":
@@ -1170,11 +1179,12 @@ private Node convertExportAssignment(JsonObject node, SourceLocation loc) throws
11701179
private Node convertExportDeclaration(JsonObject node, SourceLocation loc) throws ParseError {
11711180
Literal source = tryConvertChild(node, "moduleSpecifier", Literal.class);
11721181
if (hasChild(node, "exportClause")) {
1173-
return new ExportNamedDeclaration(
1174-
loc,
1175-
null,
1176-
convertChildren(node.get("exportClause").getAsJsonObject(), "elements"),
1177-
source);
1182+
boolean hasTypeKeyword = node.get("isTypeOnly").getAsBoolean();
1183+
List<ExportSpecifier> specifiers =
1184+
hasKind(node.get("exportClause"), "NamespaceExport")
1185+
? Collections.singletonList(convertChild(node, "exportClause"))
1186+
: convertChildren(node.get("exportClause").getAsJsonObject(), "elements");
1187+
return new ExportNamedDeclaration(loc, null, specifiers, source, hasTypeKeyword);
11781188
} else {
11791189
return new ExportAllDeclaration(loc, source);
11801190
}
@@ -1187,6 +1197,11 @@ private Node convertExportSpecifier(JsonObject node, SourceLocation loc) throws
11871197
convertChild(node, "name"));
11881198
}
11891199

1200+
private Node convertNamespaceExport(JsonObject node, SourceLocation loc) throws ParseError {
1201+
// Convert the "* as ns" from an export declaration.
1202+
return new ExportNamespaceSpecifier(loc, convertChild(node, "name"));
1203+
}
1204+
11901205
private Node convertExpressionStatement(JsonObject node, SourceLocation loc) throws ParseError {
11911206
Expression expression = convertChild(node, "expression");
11921207
return new ExpressionStatement(loc, expression);
@@ -1354,6 +1369,7 @@ private Node convertImportClause(JsonObject node, SourceLocation loc) throws Par
13541369
private Node convertImportDeclaration(JsonObject node, SourceLocation loc) throws ParseError {
13551370
Literal src = tryConvertChild(node, "moduleSpecifier", Literal.class);
13561371
List<ImportSpecifier> specifiers = new ArrayList<>();
1372+
boolean hasTypeKeyword = false;
13571373
if (hasChild(node, "importClause")) {
13581374
JsonObject importClause = node.get("importClause").getAsJsonObject();
13591375
if (hasChild(importClause, "name")) {
@@ -1367,8 +1383,9 @@ private Node convertImportDeclaration(JsonObject node, SourceLocation loc) throw
13671383
specifiers.addAll(convertChildren(namedBindings, "elements"));
13681384
}
13691385
}
1386+
hasTypeKeyword = importClause.get("isTypeOnly").getAsBoolean();
13701387
}
1371-
ImportDeclaration importDecl = new ImportDeclaration(loc, specifiers, src);
1388+
ImportDeclaration importDecl = new ImportDeclaration(loc, specifiers, src, hasTypeKeyword);
13721389
attachSymbolInformation(importDecl, node);
13731390
return importDecl;
13741391
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class C {
2+
#foo;
3+
constructor() {
4+
this.#foo = 5;
5+
}
6+
}

0 commit comments

Comments
 (0)