Skip to content

Commit 4a885cb

Browse files
committed
TS: Expose optional parameters at syntax level
1 parent b6b8213 commit 4a885cb

15 files changed

Lines changed: 146 additions & 22 deletions

File tree

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.semmle.ts.ast.DecoratorList;
44
import com.semmle.ts.ast.ITypeExpression;
55
import com.semmle.ts.ast.TypeParameter;
6+
import com.semmle.util.data.IntList;
67
import java.util.ArrayList;
78
import java.util.List;
89

@@ -18,6 +19,9 @@ public class AFunction<B> {
1819
private final List<ITypeExpression> parameterTypes;
1920
private final ITypeExpression thisParameterType;
2021
private final List<DecoratorList> parameterDecorators;
22+
private final IntList optionalParmaeterIndices;
23+
24+
public static final IntList noOptionalParams = IntList.create(0, 0);
2125

2226
public AFunction(
2327
Identifier id,
@@ -29,7 +33,8 @@ public AFunction(
2933
List<ITypeExpression> parameterTypes,
3034
List<DecoratorList> parameterDecorators,
3135
ITypeExpression returnType,
32-
ITypeExpression thisParameterType) {
36+
ITypeExpression thisParameterType,
37+
IntList optionalParmaeterIndices) {
3338
this.id = id;
3439
this.params = new ArrayList<IPattern>(params.size());
3540
this.defaults = new ArrayList<Expression>(params.size());
@@ -42,6 +47,7 @@ public AFunction(
4247
this.returnType = returnType;
4348
this.thisParameterType = thisParameterType;
4449
this.parameterDecorators = parameterDecorators;
50+
this.optionalParmaeterIndices = optionalParmaeterIndices;
4551

4652
IPattern rest = null;
4753
for (Expression param : params) {
@@ -143,4 +149,8 @@ public ITypeExpression getThisParameterType() {
143149
public List<DecoratorList> getParameterDecorators() {
144150
return parameterDecorators;
145151
}
152+
153+
public IntList getOptionalParmaeterIndices() {
154+
return optionalParmaeterIndices;
155+
}
146156
}

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.semmle.ts.ast.DecoratorList;
44
import com.semmle.ts.ast.ITypeExpression;
55
import com.semmle.ts.ast.TypeParameter;
6+
import com.semmle.util.data.IntList;
67
import java.util.List;
78

89
/**
@@ -26,7 +27,8 @@ public AFunctionExpression(
2627
List<ITypeExpression> parameterTypes,
2728
List<DecoratorList> parameterDecorators,
2829
ITypeExpression returnType,
29-
ITypeExpression thisParameterType) {
30+
ITypeExpression thisParameterType,
31+
IntList optionalParameterIndices) {
3032
super(type, loc);
3133
this.fn =
3234
new AFunction<Node>(
@@ -39,7 +41,8 @@ public AFunctionExpression(
3941
parameterTypes,
4042
parameterDecorators,
4143
returnType,
42-
thisParameterType);
44+
thisParameterType,
45+
optionalParameterIndices);
4346
}
4447

4548
public AFunctionExpression(String type, SourceLocation loc, AFunction<? extends Node> fn) {
@@ -155,4 +158,8 @@ public int getDeclaredSignatureId() {
155158
public void setDeclaredSignatureId(int id) {
156159
declaredSignature = id;
157160
}
161+
162+
public IntList getOptionalParameterIndices() {
163+
return fn.getOptionalParmaeterIndices();
164+
}
158165
}

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

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

33
import com.semmle.ts.ast.ITypeExpression;
44
import com.semmle.ts.ast.TypeParameter;
5+
import com.semmle.util.data.IntList;
56
import java.util.Collections;
67
import java.util.List;
78

@@ -21,7 +22,8 @@ public ArrowFunctionExpression(
2122
Collections.emptyList(),
2223
Collections.emptyList(),
2324
null,
24-
null);
25+
null,
26+
AFunction.noOptionalParams);
2527
}
2628

2729
public ArrowFunctionExpression(
@@ -32,7 +34,8 @@ public ArrowFunctionExpression(
3234
Boolean async,
3335
List<TypeParameter> typeParameters,
3436
List<ITypeExpression> parameterTypes,
35-
ITypeExpression returnType) {
37+
ITypeExpression returnType,
38+
IntList optionalParameterIndices) {
3639
super(
3740
"ArrowFunctionExpression",
3841
loc,
@@ -45,7 +48,8 @@ public ArrowFunctionExpression(
4548
parameterTypes,
4649
Collections.emptyList(),
4750
returnType,
48-
null);
51+
null,
52+
optionalParameterIndices);
4953
}
5054

5155
@Override

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.semmle.ts.ast.DecoratorList;
44
import com.semmle.ts.ast.ITypeExpression;
55
import com.semmle.ts.ast.TypeParameter;
6+
import com.semmle.util.data.IntList;
67
import java.util.Collections;
78
import java.util.List;
89

@@ -41,7 +42,8 @@ public FunctionDeclaration(
4142
Collections.emptyList(),
4243
Collections.emptyList(),
4344
null,
44-
null),
45+
null,
46+
AFunction.noOptionalParams),
4547
false);
4648
}
4749

@@ -56,7 +58,8 @@ public FunctionDeclaration(
5658
List<TypeParameter> typeParameters,
5759
List<ITypeExpression> parameterTypes,
5860
ITypeExpression returnType,
59-
ITypeExpression thisParameterType) {
61+
ITypeExpression thisParameterType,
62+
IntList optionalParameterIndices) {
6063
this(
6164
loc,
6265
new AFunction<>(
@@ -69,7 +72,8 @@ public FunctionDeclaration(
6972
parameterTypes,
7073
Collections.emptyList(),
7174
returnType,
72-
thisParameterType),
75+
thisParameterType,
76+
optionalParameterIndices),
7377
hasDeclareKeyword);
7478
}
7579

@@ -207,4 +211,8 @@ public int getDeclaredSignatureId() {
207211
public void setDeclaredSignatureId(int id) {
208212
declaredSignature = id;
209213
}
214+
215+
public IntList getOptionalParameterIndices() {
216+
return fn.getOptionalParmaeterIndices();
217+
}
210218
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.semmle.ts.ast.DecoratorList;
44
import com.semmle.ts.ast.ITypeExpression;
55
import com.semmle.ts.ast.TypeParameter;
6+
import com.semmle.util.data.IntList;
67
import java.util.Collections;
78
import java.util.List;
89

@@ -27,7 +28,8 @@ public FunctionExpression(
2728
Collections.emptyList(),
2829
Collections.emptyList(),
2930
null,
30-
null);
31+
null,
32+
AFunction.noOptionalParams);
3133
}
3234

3335
public FunctionExpression(
@@ -41,7 +43,8 @@ public FunctionExpression(
4143
List<ITypeExpression> parameterTypes,
4244
List<DecoratorList> parameterDecorators,
4345
ITypeExpression returnType,
44-
ITypeExpression thisParameterType) {
46+
ITypeExpression thisParameterType,
47+
IntList optionalParameterIndices) {
4548
super(
4649
"FunctionExpression",
4750
loc,
@@ -54,7 +57,8 @@ public FunctionExpression(
5457
parameterTypes,
5558
parameterDecorators,
5659
returnType,
57-
thisParameterType);
60+
thisParameterType,
61+
optionalParameterIndices);
5862
}
5963

6064
public FunctionExpression(SourceLocation loc, AFunction<? extends Node> fn) {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.semmle.ts.ast.ITypeExpression;
66
import com.semmle.ts.ast.ITypedAstNode;
77
import com.semmle.ts.ast.TypeParameter;
8+
import com.semmle.util.data.IntList;
89
import java.util.List;
910

1011
/** A function declaration or expression. */
@@ -75,4 +76,6 @@ public interface IFunction extends IStatementContainer, INodeWithSymbol, ITypedA
7576
public int getDeclaredSignatureId();
7677

7778
public void setDeclaredSignatureId(int id);
79+
80+
public IntList getOptionalParameterIndices();
7881
}

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import com.semmle.ts.ast.TypeofTypeExpr;
4646
import com.semmle.ts.ast.UnaryTypeExpr;
4747
import com.semmle.ts.ast.UnionTypeExpr;
48+
import com.semmle.util.data.IntList;
4849
import java.util.ArrayList;
4950
import java.util.List;
5051

@@ -70,6 +71,10 @@ private <T extends INode> List<T> copy(List<T> ts) {
7071
return result;
7172
}
7273

74+
private IntList copy(IntList list) {
75+
return new IntList(list);
76+
}
77+
7378
@Override
7479
public AssignmentExpression visit(AssignmentExpression nd, Void q) {
7580
return new AssignmentExpression(
@@ -138,7 +143,8 @@ public FunctionDeclaration visit(FunctionDeclaration nd, Void q) {
138143
copy(nd.getTypeParameters()),
139144
copy(nd.getParameterTypes()),
140145
copy(nd.getReturnType()),
141-
copy(nd.getThisParameterType()));
146+
copy(nd.getThisParameterType()),
147+
copy(nd.getOptionalParameterIndices()));
142148
}
143149

144150
@Override
@@ -367,7 +373,8 @@ public FunctionExpression visit(FunctionExpression nd, Void q) {
367373
copy(nd.getParameterTypes()),
368374
copy(nd.getParameterDecorators()),
369375
copy(nd.getReturnType()),
370-
copy(nd.getThisParameterType()));
376+
copy(nd.getThisParameterType()),
377+
copy(nd.getOptionalParameterIndices()));
371378
}
372379

373380
@Override
@@ -427,7 +434,8 @@ public ArrowFunctionExpression visit(ArrowFunctionExpression nd, Void q) {
427434
nd.isAsync(),
428435
copy(nd.getTypeParameters()),
429436
copy(nd.getParameterTypes()),
430-
copy(nd.getReturnType()));
437+
copy(nd.getReturnType()),
438+
copy(nd.getOptionalParameterIndices()));
431439
}
432440

433441
@Override

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,12 @@ private void extractFunction(IFunction nd, Label key) {
900900
for (IPattern param : nd.getAllParams()) {
901901
scopeManager.addNames(
902902
scopeManager.collectDeclaredNames(param, isStrict, false, DeclKind.var));
903-
visit(param, key, i, IdContext.varDecl);
903+
Label paramKey = visit(param, key, i, IdContext.varDecl);
904+
905+
// Extract optional parameters
906+
if (nd.getOptionalParameterIndices().contains(i)) {
907+
trapwriter.addTuple("isOptionalParameterDeclaration", paramKey);
908+
}
904909
++i;
905910
}
906911

@@ -1393,7 +1398,8 @@ private void addDefaultConstructor(AClass ac) {
13931398
Collections.emptyList(),
13941399
Collections.emptyList(),
13951400
null,
1396-
null);
1401+
null,
1402+
AFunction.noOptionalParams);
13971403
String fnSrc = hasSuperClass ? "(...args) { super(...args); }" : "() {}";
13981404
SourceLocation fnloc = fakeLoc(fnSrc, loc);
13991405
FunctionExpression fn = new FunctionExpression(fnloc, fndef);

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

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
import com.semmle.ts.ast.UnaryTypeExpr;
144144
import com.semmle.ts.ast.UnionTypeExpr;
145145
import com.semmle.util.collections.CollectionUtil;
146+
import com.semmle.util.data.IntList;
146147
import java.util.ArrayList;
147148
import java.util.Collections;
148149
import java.util.LinkedHashMap;
@@ -804,7 +805,8 @@ private Node convertArrowFunction(JsonObject node, SourceLocation loc) throws Pa
804805
hasModifier(node, "AsyncKeyword"),
805806
convertChildrenNotNull(node, "typeParameters"),
806807
convertParameterTypes(node),
807-
convertChildAsType(node, "type"));
808+
convertChildAsType(node, "type"),
809+
getOptionalParameterIndices(node));
808810
attachDeclaredSignature(function, node);
809811
return function;
810812
}
@@ -1063,7 +1065,8 @@ private Node convertConstructor(JsonObject node, SourceLocation loc) throws Pars
10631065
paramTypes,
10641066
paramDecorators,
10651067
null,
1066-
null);
1068+
null,
1069+
getOptionalParameterIndices(node));
10671070
attachSymbolInformation(value, node);
10681071
attachStaticType(value, node);
10691072
attachDeclaredSignature(value, node);
@@ -1262,7 +1265,8 @@ private Node convertFunctionDeclaration(JsonObject node, SourceLocation loc) thr
12621265
typeParameters,
12631266
paramTypes,
12641267
returnType,
1265-
thisParam);
1268+
thisParam,
1269+
getOptionalParameterIndices(node));
12661270
attachSymbolInformation(function, node);
12671271
attachStaticType(function, node);
12681272
attachDeclaredSignature(function, node);
@@ -1291,7 +1295,8 @@ private Node convertFunctionExpression(JsonObject node, SourceLocation loc) thro
12911295
paramTypes,
12921296
paramDecorators,
12931297
returnType,
1294-
thisParam);
1298+
thisParam,
1299+
getOptionalParameterIndices(node));
12951300
attachStaticType(function, node);
12961301
attachDeclaredSignature(function, node);
12971302
return function;
@@ -1645,7 +1650,8 @@ private FunctionExpression convertImplicitFunction(JsonObject node, SourceLocati
16451650
paramTypes,
16461651
paramDecorators,
16471652
returnType,
1648-
thisType);
1653+
thisType,
1654+
getOptionalParameterIndices(node));
16491655
attachSymbolInformation(function, node);
16501656
attachStaticType(function, node);
16511657
attachDeclaredSignature(function, node);
@@ -1890,6 +1896,19 @@ private List<ITypeExpression> convertParameterTypes(JsonObject function) throws
18901896
return result;
18911897
}
18921898

1899+
private IntList getOptionalParameterIndices(JsonObject function) throws ParseError {
1900+
IntList list = IntList.create(0);
1901+
int index = -1;
1902+
for (JsonElement param : getProperParameters(function)) {
1903+
++index;
1904+
if (param.getAsJsonObject().has("questionToken")) {
1905+
list.add(index);
1906+
;
1907+
}
1908+
}
1909+
return list;
1910+
}
1911+
18931912
private List<FieldDefinition> convertParameterFields(JsonObject function) throws ParseError {
18941913
List<FieldDefinition> result = new ArrayList<>();
18951914
int index = -1;

javascript/ql/src/semmle/javascript/Variables.qll

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,21 @@ class Parameter extends BindingPattern {
749749
JSDocTag getJSDocTag() {
750750
none() // overridden in SimpleParameter
751751
}
752+
753+
/**
754+
* Holds if this is a parameter declared optional with the `?` token.
755+
*
756+
* Note that this does not hold for rest parameters, and does not in general
757+
* hold for parameters with defaults.
758+
*
759+
* For example, `x`, is declared optional below:
760+
* ```
761+
* function f(x?: number) {}
762+
* ```
763+
*/
764+
predicate isDeclaredOptional() {
765+
isOptionalParameterDeclaration(this)
766+
}
752767
}
753768

754769
/**

0 commit comments

Comments
 (0)