JS: Preserve information about 'defer' keyword

This commit is contained in:
Asger F
2025-09-05 11:19:04 +02:00
parent 76ca1a576f
commit 215602c963
12 changed files with 71 additions and 14 deletions

View File

@@ -26,11 +26,11 @@ public class ImportDeclaration extends Statement implements INodeWithSymbol {
private int symbol = -1;
private boolean hasTypeKeyword;
private ImportPhaseModifier phaseModifier;
public ImportDeclaration(
SourceLocation loc, List<ImportSpecifier> specifiers, Literal source, Expression attributes) {
this(loc, specifiers, source, attributes, false);
this(loc, specifiers, source, attributes, ImportPhaseModifier.NONE);
}
public ImportDeclaration(
@@ -38,12 +38,12 @@ public class ImportDeclaration extends Statement implements INodeWithSymbol {
List<ImportSpecifier> specifiers,
Literal source,
Expression attributes,
boolean hasTypeKeyword) {
ImportPhaseModifier phaseModifier) {
super("ImportDeclaration", loc);
this.specifiers = specifiers;
this.source = source;
this.attributes = attributes;
this.hasTypeKeyword = hasTypeKeyword;
this.phaseModifier = phaseModifier;
}
public Literal getSource() {
@@ -79,6 +79,15 @@ public class ImportDeclaration extends Statement implements INodeWithSymbol {
/** Returns true if this is an <code>import type</code> declaration. */
public boolean hasTypeKeyword() {
return hasTypeKeyword;
return phaseModifier == ImportPhaseModifier.TYPE;
}
/** Returns true if this is an <code>import defer</code> declaration. */
public boolean hasDeferKeyword() {
return phaseModifier == ImportPhaseModifier.DEFER;
}
public ImportPhaseModifier getPhaseModifier() {
return phaseModifier;
}
}

View File

@@ -0,0 +1,10 @@
package com.semmle.js.ast;
/**
* A keyword that may appear on import declarations.
*/
public enum ImportPhaseModifier {
NONE,
DEFER,
TYPE,
}

View File

@@ -564,7 +564,7 @@ public class NodeCopier implements Visitor<Void, INode> {
copy(nd.getSpecifiers()),
copy(nd.getSource()),
copy(nd.getAttributes()),
nd.hasTypeKeyword());
nd.getPhaseModifier());
}
@Override

View File

@@ -1833,6 +1833,9 @@ public class ASTExtractor {
if (nd.hasTypeKeyword()) {
trapwriter.addTuple("has_type_keyword", lbl);
}
if (nd.hasDeferKeyword()) {
trapwriter.addTuple("has_defer_keyword", lbl);
}
return lbl;
}

View File

@@ -52,6 +52,7 @@ import com.semmle.js.ast.IfStatement;
import com.semmle.js.ast.ImportDeclaration;
import com.semmle.js.ast.ImportDefaultSpecifier;
import com.semmle.js.ast.ImportNamespaceSpecifier;
import com.semmle.js.ast.ImportPhaseModifier;
import com.semmle.js.ast.ImportSpecifier;
import com.semmle.js.ast.InvokeExpression;
import com.semmle.js.ast.LabeledStatement;
@@ -1404,7 +1405,7 @@ public class TypeScriptASTConverter {
Literal src = tryConvertChild(node, "moduleSpecifier", Literal.class);
Expression attributes = convertChild(node, "attributes");
List<ImportSpecifier> specifiers = new ArrayList<>();
boolean hasTypeKeyword = false;
ImportPhaseModifier phaseModifier = ImportPhaseModifier.NONE;
if (hasChild(node, "importClause")) {
JsonObject importClause = node.get("importClause").getAsJsonObject();
if (hasChild(importClause, "name")) {
@@ -1418,10 +1419,22 @@ public class TypeScriptASTConverter {
specifiers.addAll(convertChildren(namedBindings, "elements"));
}
}
hasTypeKeyword = importClause.get("isTypeOnly").getAsBoolean();
if (hasChild(importClause, "phaseModifier")) {
String name = metadata.getSyntaxKindName(importClause.get("phaseModifier").getAsInt());
switch (name) {
case "DeferKeyword": {
phaseModifier = ImportPhaseModifier.DEFER;
break;
}
case "TypeKeyword": {
phaseModifier = ImportPhaseModifier.TYPE;
break;
}
}
}
}
ImportDeclaration importDecl =
new ImportDeclaration(loc, specifiers, src, attributes, hasTypeKeyword);
new ImportDeclaration(loc, specifiers, src, attributes, phaseModifier);
attachSymbolInformation(importDecl, node);
return importDecl;
}