add JS support the using keyword

This commit is contained in:
erik-krogh
2023-08-10 21:31:57 +02:00
parent dfc83d844a
commit a7d92b3473
13 changed files with 111 additions and 8 deletions

View File

@@ -51,6 +51,9 @@ import java.util.Set;
public class ESNextParser extends JSXParser {
public ESNextParser(Options options, String input, int startPos) {
super(options.allowImportExportEverywhere(true), input, startPos);
// recognise `using` as a keyword. See https://github.com/tc39/proposal-explicit-resource-management
this.keywords.add("using");
}
/*

View File

@@ -2737,7 +2737,7 @@ public class Parser {
return this.parseThrowStatement(startLoc);
} else if (starttype == TokenType._try) {
return this.parseTryStatement(startLoc);
} else if (starttype == TokenType._const || starttype == TokenType._var) {
} else if (starttype == TokenType._const || starttype == TokenType._var || starttype == TokenType._using) {
if (kind == null) kind = String.valueOf(this.value);
if (!declaration && !kind.equals("var")) this.unexpected();
return this.parseVarStatement(startLoc, kind);
@@ -2840,7 +2840,7 @@ public class Parser {
this.expect(TokenType.parenL);
if (this.type == TokenType.semi) return this.parseFor(startLoc, null);
boolean isLet = this.isLet();
if (this.type == TokenType._var || this.type == TokenType._const || isLet) {
if (this.type == TokenType._var || this.type == TokenType._const || isLet || this.type == TokenType._using) { // TODO: Add test for this.
Position initStartLoc = this.startLoc;
String kind = isLet ? "let" : String.valueOf(this.value);
this.next();
@@ -3122,7 +3122,7 @@ public class Parser {
Expression init = null;
if (this.eat(TokenType.eq)) {
init = this.parseMaybeAssign(isFor, null, null);
} else if (kind.equals("const")
} else if ((kind.equals("const") || kind.equals("using"))
&& !(this.type == TokenType._in
|| (this.options.ecmaVersion() >= 6 && this.isContextual("of")))) {
this.raiseRecoverable(

View File

@@ -180,6 +180,7 @@ public class TokenType {
_try = new TokenType(kw("try")),
_var = new TokenType(kw("var")),
_const = new TokenType(kw("const")),
_using = new TokenType(kw("using")),
_while = new TokenType(kw("while").isLoop()),
_with = new TokenType(kw("with")),
_new = new TokenType(kw("new").beforeExpr().startsExpr()),

View File

@@ -28,8 +28,8 @@ public class VariableDeclaration extends Statement {
}
/**
* The kind of this variable declaration statement; one of <code>"var"</code>, <code>"let"</code>
* or <code>"const"</code>.
* The kind of this variable declaration statement; one of <code>"var"</code>, <code>"let"</code>,
* <code>"const"</code>, or <code>"using"</code>.
*/
public String getKind() {
return kind;
@@ -42,6 +42,7 @@ public class VariableDeclaration extends Statement {
*/
public boolean isBlockScoped(ECMAVersion ecmaVersion) {
return "let".equals(kind)
|| "using".equals(kind)
|| ecmaVersion.compareTo(ECMAVersion.ECMA2015) >= 0 && "const".equals(kind);
}

View File

@@ -58,6 +58,7 @@ public class StmtKinds {
declKinds.put("var", 18);
declKinds.put("const", 22);
declKinds.put("let", 23);
declKinds.put("using", 40);
}
public static int getStmtKind(final Statement stmt) {

View File

@@ -246,13 +246,15 @@ private module PrintJavaScript {
}
/**
* Gets "var" or "const" or "let" depending on what type of declaration `decl` is.
* Gets "var" or "const" or "let" or "using" depending on what type of declaration `decl` is.
*/
private string getDeclarationKeyword(DeclStmt decl) {
decl instanceof VarDeclStmt and result = "var"
or
decl instanceof ConstDeclStmt and result = "const"
or
decl instanceof UsingDeclStmt and result = "using"
or
decl instanceof LetStmt and result = "let"
}
}

View File

@@ -1041,6 +1041,17 @@ class VarDeclStmt extends @var_decl_stmt, DeclStmt { }
*/
class ConstDeclStmt extends @const_decl_stmt, DeclStmt { }
/**
* A `using` declaration statement.
*
* Example:
*
* ```
* using file = new TextFile("file.txt");
* ```
*/
class UsingDeclStmt extends @using_decl_stmt, DeclStmt { }
/**
* A `let` declaration statement.
*

View File

@@ -162,9 +162,10 @@ case @stmt.kind of
| 37 = @external_module_declaration
| 38 = @export_as_namespace_declaration
| 39 = @global_augmentation_declaration
| 40 = @using_decl_stmt
;
@decl_stmt = @var_decl_stmt | @const_decl_stmt | @let_stmt | @legacy_let_stmt;
@decl_stmt = @var_decl_stmt | @const_decl_stmt | @let_stmt | @legacy_let_stmt | @using_decl_stmt;
@export_declaration = @export_all_declaration | @export_default_declaration | @export_named_declaration;

View File

@@ -13,8 +13,9 @@
import javascript
import semmle.javascript.RestrictedLocations
from ConstDeclStmt cds, VariableDeclarator decl, VarDef def, Variable v
from DeclStmt cds, VariableDeclarator decl, VarDef def, Variable v
where
(cds instanceof ConstDeclStmt or cds instanceof UsingDeclStmt) and
decl = cds.getADecl() and
def.getAVariable() = v and
decl.getBindingPattern().getAVariable() = v and

View File

@@ -39,6 +39,8 @@ predicate isSymbolicConstant(Variable v) {
exists(VarDef vd | vd = getSingleDef(v) |
vd.(VariableDeclarator).getDeclStmt() instanceof ConstDeclStmt
or
vd.(VariableDeclarator).getDeclStmt() instanceof UsingDeclStmt
or
isConstant(vd.getSource())
)
}

View File

@@ -0,0 +1,69 @@
nodes
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| tst.js:1:1:9:1 | [FunctionDeclStmt] functio ... } } | semmle.label | [FunctionDeclStmt] functio ... } } |
| tst.js:1:1:9:1 | [FunctionDeclStmt] functio ... } } | semmle.order | 1 |
| tst.js:1:10:1:10 | [VarDecl] g | semmle.label | [VarDecl] g |
| tst.js:1:14:9:1 | [BlockStmt] { u ... } } | semmle.label | [BlockStmt] { u ... } } |
| tst.js:2:5:2:33 | [DeclStmt] using stream = ... | semmle.label | [DeclStmt] using stream = ... |
| tst.js:2:11:2:16 | [VarDecl] stream | semmle.label | [VarDecl] stream |
| tst.js:2:11:2:32 | [VariableDeclarator] stream ... ource() | semmle.label | [VariableDeclarator] stream ... ource() |
| tst.js:2:20:2:30 | [VarRef] getResource | semmle.label | [VarRef] getResource |
| tst.js:2:20:2:32 | [CallExpr] getResource() | semmle.label | [CallExpr] getResource() |
| tst.js:4:5:4:7 | [VarRef] let | semmle.label | [VarRef] let |
| tst.js:4:5:4:19 | [CallExpr] let (test = 20) | semmle.label | [CallExpr] let (test = 20) |
| tst.js:4:5:4:20 | [ExprStmt] let (test = 20); | semmle.label | [ExprStmt] let (test = 20); |
| tst.js:4:10:4:13 | [VarRef] test | semmle.label | [VarRef] test |
| tst.js:4:10:4:18 | [AssignExpr] test = 20 | semmle.label | [AssignExpr] test = 20 |
| tst.js:4:17:4:18 | [Literal] 20 | semmle.label | [Literal] 20 |
| tst.js:6:5:8:5 | [ForStmt] for (us ... . } | semmle.label | [ForStmt] for (us ... . } |
| tst.js:6:10:6:38 | [DeclStmt] using stream2 = ... | semmle.label | [DeclStmt] using stream2 = ... |
| tst.js:6:16:6:22 | [VarDecl] stream2 | semmle.label | [VarDecl] stream2 |
| tst.js:6:16:6:38 | [VariableDeclarator] stream2 ... ource() | semmle.label | [VariableDeclarator] stream2 ... ource() |
| tst.js:6:26:6:36 | [VarRef] getResource | semmle.label | [VarRef] getResource |
| tst.js:6:26:6:38 | [CallExpr] getResource() | semmle.label | [CallExpr] getResource() |
| tst.js:6:45:8:5 | [BlockStmt] { ... . } | semmle.label | [BlockStmt] { ... . } |
edges
| file://:0:0:0:0 | (Arguments) | tst.js:4:10:4:18 | [AssignExpr] test = 20 | semmle.label | 0 |
| file://:0:0:0:0 | (Arguments) | tst.js:4:10:4:18 | [AssignExpr] test = 20 | semmle.order | 0 |
| tst.js:1:1:9:1 | [FunctionDeclStmt] functio ... } } | tst.js:1:10:1:10 | [VarDecl] g | semmle.label | 0 |
| tst.js:1:1:9:1 | [FunctionDeclStmt] functio ... } } | tst.js:1:10:1:10 | [VarDecl] g | semmle.order | 0 |
| tst.js:1:1:9:1 | [FunctionDeclStmt] functio ... } } | tst.js:1:14:9:1 | [BlockStmt] { u ... } } | semmle.label | 5 |
| tst.js:1:1:9:1 | [FunctionDeclStmt] functio ... } } | tst.js:1:14:9:1 | [BlockStmt] { u ... } } | semmle.order | 5 |
| tst.js:1:14:9:1 | [BlockStmt] { u ... } } | tst.js:2:5:2:33 | [DeclStmt] using stream = ... | semmle.label | 1 |
| tst.js:1:14:9:1 | [BlockStmt] { u ... } } | tst.js:2:5:2:33 | [DeclStmt] using stream = ... | semmle.order | 1 |
| tst.js:1:14:9:1 | [BlockStmt] { u ... } } | tst.js:4:5:4:20 | [ExprStmt] let (test = 20); | semmle.label | 2 |
| tst.js:1:14:9:1 | [BlockStmt] { u ... } } | tst.js:4:5:4:20 | [ExprStmt] let (test = 20); | semmle.order | 2 |
| tst.js:1:14:9:1 | [BlockStmt] { u ... } } | tst.js:6:5:8:5 | [ForStmt] for (us ... . } | semmle.label | 3 |
| tst.js:1:14:9:1 | [BlockStmt] { u ... } } | tst.js:6:5:8:5 | [ForStmt] for (us ... . } | semmle.order | 3 |
| tst.js:2:5:2:33 | [DeclStmt] using stream = ... | tst.js:2:11:2:32 | [VariableDeclarator] stream ... ource() | semmle.label | 1 |
| tst.js:2:5:2:33 | [DeclStmt] using stream = ... | tst.js:2:11:2:32 | [VariableDeclarator] stream ... ource() | semmle.order | 1 |
| tst.js:2:11:2:32 | [VariableDeclarator] stream ... ource() | tst.js:2:11:2:16 | [VarDecl] stream | semmle.label | 1 |
| tst.js:2:11:2:32 | [VariableDeclarator] stream ... ource() | tst.js:2:11:2:16 | [VarDecl] stream | semmle.order | 1 |
| tst.js:2:11:2:32 | [VariableDeclarator] stream ... ource() | tst.js:2:20:2:32 | [CallExpr] getResource() | semmle.label | 2 |
| tst.js:2:11:2:32 | [VariableDeclarator] stream ... ource() | tst.js:2:20:2:32 | [CallExpr] getResource() | semmle.order | 2 |
| tst.js:2:20:2:32 | [CallExpr] getResource() | tst.js:2:20:2:30 | [VarRef] getResource | semmle.label | 0 |
| tst.js:2:20:2:32 | [CallExpr] getResource() | tst.js:2:20:2:30 | [VarRef] getResource | semmle.order | 0 |
| tst.js:4:5:4:19 | [CallExpr] let (test = 20) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
| tst.js:4:5:4:19 | [CallExpr] let (test = 20) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
| tst.js:4:5:4:19 | [CallExpr] let (test = 20) | tst.js:4:5:4:7 | [VarRef] let | semmle.label | 0 |
| tst.js:4:5:4:19 | [CallExpr] let (test = 20) | tst.js:4:5:4:7 | [VarRef] let | semmle.order | 0 |
| tst.js:4:5:4:20 | [ExprStmt] let (test = 20); | tst.js:4:5:4:19 | [CallExpr] let (test = 20) | semmle.label | 1 |
| tst.js:4:5:4:20 | [ExprStmt] let (test = 20); | tst.js:4:5:4:19 | [CallExpr] let (test = 20) | semmle.order | 1 |
| tst.js:4:10:4:18 | [AssignExpr] test = 20 | tst.js:4:10:4:13 | [VarRef] test | semmle.label | 1 |
| tst.js:4:10:4:18 | [AssignExpr] test = 20 | tst.js:4:10:4:13 | [VarRef] test | semmle.order | 1 |
| tst.js:4:10:4:18 | [AssignExpr] test = 20 | tst.js:4:17:4:18 | [Literal] 20 | semmle.label | 2 |
| tst.js:4:10:4:18 | [AssignExpr] test = 20 | tst.js:4:17:4:18 | [Literal] 20 | semmle.order | 2 |
| tst.js:6:5:8:5 | [ForStmt] for (us ... . } | tst.js:6:10:6:38 | [DeclStmt] using stream2 = ... | semmle.label | 1 |
| tst.js:6:5:8:5 | [ForStmt] for (us ... . } | tst.js:6:10:6:38 | [DeclStmt] using stream2 = ... | semmle.order | 1 |
| tst.js:6:5:8:5 | [ForStmt] for (us ... . } | tst.js:6:45:8:5 | [BlockStmt] { ... . } | semmle.label | 2 |
| tst.js:6:5:8:5 | [ForStmt] for (us ... . } | tst.js:6:45:8:5 | [BlockStmt] { ... . } | semmle.order | 2 |
| tst.js:6:10:6:38 | [DeclStmt] using stream2 = ... | tst.js:6:16:6:38 | [VariableDeclarator] stream2 ... ource() | semmle.label | 1 |
| tst.js:6:10:6:38 | [DeclStmt] using stream2 = ... | tst.js:6:16:6:38 | [VariableDeclarator] stream2 ... ource() | semmle.order | 1 |
| tst.js:6:16:6:38 | [VariableDeclarator] stream2 ... ource() | tst.js:6:16:6:22 | [VarDecl] stream2 | semmle.label | 1 |
| tst.js:6:16:6:38 | [VariableDeclarator] stream2 ... ource() | tst.js:6:16:6:22 | [VarDecl] stream2 | semmle.order | 1 |
| tst.js:6:16:6:38 | [VariableDeclarator] stream2 ... ource() | tst.js:6:26:6:38 | [CallExpr] getResource() | semmle.label | 2 |
| tst.js:6:16:6:38 | [VariableDeclarator] stream2 ... ource() | tst.js:6:26:6:38 | [CallExpr] getResource() | semmle.order | 2 |
| tst.js:6:26:6:38 | [CallExpr] getResource() | tst.js:6:26:6:36 | [VarRef] getResource | semmle.label | 0 |
| tst.js:6:26:6:38 | [CallExpr] getResource() | tst.js:6:26:6:36 | [VarRef] getResource | semmle.order | 0 |
graphProperties
| semmle.graphKind | tree |

View File

@@ -0,0 +1,2 @@
import javascript
import semmle.javascript.PrintAst

View File

@@ -0,0 +1,9 @@
function g() {
using stream = getResource();
let (test = 20); // <- I didn't know this was a thing
for (using stream2 = getResource(); ; ) {
// ...
}
}