TS: Expose optional parameters at syntax level

This commit is contained in:
Asger F
2019-11-16 08:38:44 +00:00
parent b6b8213e13
commit 4a885cbf92
15 changed files with 146 additions and 22 deletions

View File

@@ -3,6 +3,7 @@ package com.semmle.js.ast;
import com.semmle.ts.ast.DecoratorList;
import com.semmle.ts.ast.ITypeExpression;
import com.semmle.ts.ast.TypeParameter;
import com.semmle.util.data.IntList;
import java.util.ArrayList;
import java.util.List;
@@ -18,6 +19,9 @@ public class AFunction<B> {
private final List<ITypeExpression> parameterTypes;
private final ITypeExpression thisParameterType;
private final List<DecoratorList> parameterDecorators;
private final IntList optionalParmaeterIndices;
public static final IntList noOptionalParams = IntList.create(0, 0);
public AFunction(
Identifier id,
@@ -29,7 +33,8 @@ public class AFunction<B> {
List<ITypeExpression> parameterTypes,
List<DecoratorList> parameterDecorators,
ITypeExpression returnType,
ITypeExpression thisParameterType) {
ITypeExpression thisParameterType,
IntList optionalParmaeterIndices) {
this.id = id;
this.params = new ArrayList<IPattern>(params.size());
this.defaults = new ArrayList<Expression>(params.size());
@@ -42,6 +47,7 @@ public class AFunction<B> {
this.returnType = returnType;
this.thisParameterType = thisParameterType;
this.parameterDecorators = parameterDecorators;
this.optionalParmaeterIndices = optionalParmaeterIndices;
IPattern rest = null;
for (Expression param : params) {
@@ -143,4 +149,8 @@ public class AFunction<B> {
public List<DecoratorList> getParameterDecorators() {
return parameterDecorators;
}
public IntList getOptionalParmaeterIndices() {
return optionalParmaeterIndices;
}
}

View File

@@ -3,6 +3,7 @@ package com.semmle.js.ast;
import com.semmle.ts.ast.DecoratorList;
import com.semmle.ts.ast.ITypeExpression;
import com.semmle.ts.ast.TypeParameter;
import com.semmle.util.data.IntList;
import java.util.List;
/**
@@ -26,7 +27,8 @@ public abstract class AFunctionExpression extends Expression implements IFunctio
List<ITypeExpression> parameterTypes,
List<DecoratorList> parameterDecorators,
ITypeExpression returnType,
ITypeExpression thisParameterType) {
ITypeExpression thisParameterType,
IntList optionalParameterIndices) {
super(type, loc);
this.fn =
new AFunction<Node>(
@@ -39,7 +41,8 @@ public abstract class AFunctionExpression extends Expression implements IFunctio
parameterTypes,
parameterDecorators,
returnType,
thisParameterType);
thisParameterType,
optionalParameterIndices);
}
public AFunctionExpression(String type, SourceLocation loc, AFunction<? extends Node> fn) {
@@ -155,4 +158,8 @@ public abstract class AFunctionExpression extends Expression implements IFunctio
public void setDeclaredSignatureId(int id) {
declaredSignature = id;
}
public IntList getOptionalParameterIndices() {
return fn.getOptionalParmaeterIndices();
}
}

View File

@@ -2,6 +2,7 @@ package com.semmle.js.ast;
import com.semmle.ts.ast.ITypeExpression;
import com.semmle.ts.ast.TypeParameter;
import com.semmle.util.data.IntList;
import java.util.Collections;
import java.util.List;
@@ -21,7 +22,8 @@ public class ArrowFunctionExpression extends AFunctionExpression {
Collections.emptyList(),
Collections.emptyList(),
null,
null);
null,
AFunction.noOptionalParams);
}
public ArrowFunctionExpression(
@@ -32,7 +34,8 @@ public class ArrowFunctionExpression extends AFunctionExpression {
Boolean async,
List<TypeParameter> typeParameters,
List<ITypeExpression> parameterTypes,
ITypeExpression returnType) {
ITypeExpression returnType,
IntList optionalParameterIndices) {
super(
"ArrowFunctionExpression",
loc,
@@ -45,7 +48,8 @@ public class ArrowFunctionExpression extends AFunctionExpression {
parameterTypes,
Collections.emptyList(),
returnType,
null);
null,
optionalParameterIndices);
}
@Override

View File

@@ -3,6 +3,7 @@ package com.semmle.js.ast;
import com.semmle.ts.ast.DecoratorList;
import com.semmle.ts.ast.ITypeExpression;
import com.semmle.ts.ast.TypeParameter;
import com.semmle.util.data.IntList;
import java.util.Collections;
import java.util.List;
@@ -41,7 +42,8 @@ public class FunctionDeclaration extends Statement implements IFunction {
Collections.emptyList(),
Collections.emptyList(),
null,
null),
null,
AFunction.noOptionalParams),
false);
}
@@ -56,7 +58,8 @@ public class FunctionDeclaration extends Statement implements IFunction {
List<TypeParameter> typeParameters,
List<ITypeExpression> parameterTypes,
ITypeExpression returnType,
ITypeExpression thisParameterType) {
ITypeExpression thisParameterType,
IntList optionalParameterIndices) {
this(
loc,
new AFunction<>(
@@ -69,7 +72,8 @@ public class FunctionDeclaration extends Statement implements IFunction {
parameterTypes,
Collections.emptyList(),
returnType,
thisParameterType),
thisParameterType,
optionalParameterIndices),
hasDeclareKeyword);
}
@@ -207,4 +211,8 @@ public class FunctionDeclaration extends Statement implements IFunction {
public void setDeclaredSignatureId(int id) {
declaredSignature = id;
}
public IntList getOptionalParameterIndices() {
return fn.getOptionalParmaeterIndices();
}
}

View File

@@ -3,6 +3,7 @@ package com.semmle.js.ast;
import com.semmle.ts.ast.DecoratorList;
import com.semmle.ts.ast.ITypeExpression;
import com.semmle.ts.ast.TypeParameter;
import com.semmle.util.data.IntList;
import java.util.Collections;
import java.util.List;
@@ -27,7 +28,8 @@ public class FunctionExpression extends AFunctionExpression {
Collections.emptyList(),
Collections.emptyList(),
null,
null);
null,
AFunction.noOptionalParams);
}
public FunctionExpression(
@@ -41,7 +43,8 @@ public class FunctionExpression extends AFunctionExpression {
List<ITypeExpression> parameterTypes,
List<DecoratorList> parameterDecorators,
ITypeExpression returnType,
ITypeExpression thisParameterType) {
ITypeExpression thisParameterType,
IntList optionalParameterIndices) {
super(
"FunctionExpression",
loc,
@@ -54,7 +57,8 @@ public class FunctionExpression extends AFunctionExpression {
parameterTypes,
parameterDecorators,
returnType,
thisParameterType);
thisParameterType,
optionalParameterIndices);
}
public FunctionExpression(SourceLocation loc, AFunction<? extends Node> fn) {

View File

@@ -5,6 +5,7 @@ import com.semmle.ts.ast.INodeWithSymbol;
import com.semmle.ts.ast.ITypeExpression;
import com.semmle.ts.ast.ITypedAstNode;
import com.semmle.ts.ast.TypeParameter;
import com.semmle.util.data.IntList;
import java.util.List;
/** A function declaration or expression. */
@@ -75,4 +76,6 @@ public interface IFunction extends IStatementContainer, INodeWithSymbol, ITypedA
public int getDeclaredSignatureId();
public void setDeclaredSignatureId(int id);
public IntList getOptionalParameterIndices();
}

View File

@@ -45,6 +45,7 @@ import com.semmle.ts.ast.TypeParameter;
import com.semmle.ts.ast.TypeofTypeExpr;
import com.semmle.ts.ast.UnaryTypeExpr;
import com.semmle.ts.ast.UnionTypeExpr;
import com.semmle.util.data.IntList;
import java.util.ArrayList;
import java.util.List;
@@ -70,6 +71,10 @@ public class NodeCopier implements Visitor<Void, INode> {
return result;
}
private IntList copy(IntList list) {
return new IntList(list);
}
@Override
public AssignmentExpression visit(AssignmentExpression nd, Void q) {
return new AssignmentExpression(
@@ -138,7 +143,8 @@ public class NodeCopier implements Visitor<Void, INode> {
copy(nd.getTypeParameters()),
copy(nd.getParameterTypes()),
copy(nd.getReturnType()),
copy(nd.getThisParameterType()));
copy(nd.getThisParameterType()),
copy(nd.getOptionalParameterIndices()));
}
@Override
@@ -367,7 +373,8 @@ public class NodeCopier implements Visitor<Void, INode> {
copy(nd.getParameterTypes()),
copy(nd.getParameterDecorators()),
copy(nd.getReturnType()),
copy(nd.getThisParameterType()));
copy(nd.getThisParameterType()),
copy(nd.getOptionalParameterIndices()));
}
@Override
@@ -427,7 +434,8 @@ public class NodeCopier implements Visitor<Void, INode> {
nd.isAsync(),
copy(nd.getTypeParameters()),
copy(nd.getParameterTypes()),
copy(nd.getReturnType()));
copy(nd.getReturnType()),
copy(nd.getOptionalParameterIndices()));
}
@Override

View File

@@ -900,7 +900,12 @@ public class ASTExtractor {
for (IPattern param : nd.getAllParams()) {
scopeManager.addNames(
scopeManager.collectDeclaredNames(param, isStrict, false, DeclKind.var));
visit(param, key, i, IdContext.varDecl);
Label paramKey = visit(param, key, i, IdContext.varDecl);
// Extract optional parameters
if (nd.getOptionalParameterIndices().contains(i)) {
trapwriter.addTuple("isOptionalParameterDeclaration", paramKey);
}
++i;
}
@@ -1393,7 +1398,8 @@ public class ASTExtractor {
Collections.emptyList(),
Collections.emptyList(),
null,
null);
null,
AFunction.noOptionalParams);
String fnSrc = hasSuperClass ? "(...args) { super(...args); }" : "() {}";
SourceLocation fnloc = fakeLoc(fnSrc, loc);
FunctionExpression fn = new FunctionExpression(fnloc, fndef);

View File

@@ -143,6 +143,7 @@ import com.semmle.ts.ast.TypeofTypeExpr;
import com.semmle.ts.ast.UnaryTypeExpr;
import com.semmle.ts.ast.UnionTypeExpr;
import com.semmle.util.collections.CollectionUtil;
import com.semmle.util.data.IntList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -804,7 +805,8 @@ public class TypeScriptASTConverter {
hasModifier(node, "AsyncKeyword"),
convertChildrenNotNull(node, "typeParameters"),
convertParameterTypes(node),
convertChildAsType(node, "type"));
convertChildAsType(node, "type"),
getOptionalParameterIndices(node));
attachDeclaredSignature(function, node);
return function;
}
@@ -1063,7 +1065,8 @@ public class TypeScriptASTConverter {
paramTypes,
paramDecorators,
null,
null);
null,
getOptionalParameterIndices(node));
attachSymbolInformation(value, node);
attachStaticType(value, node);
attachDeclaredSignature(value, node);
@@ -1262,7 +1265,8 @@ public class TypeScriptASTConverter {
typeParameters,
paramTypes,
returnType,
thisParam);
thisParam,
getOptionalParameterIndices(node));
attachSymbolInformation(function, node);
attachStaticType(function, node);
attachDeclaredSignature(function, node);
@@ -1291,7 +1295,8 @@ public class TypeScriptASTConverter {
paramTypes,
paramDecorators,
returnType,
thisParam);
thisParam,
getOptionalParameterIndices(node));
attachStaticType(function, node);
attachDeclaredSignature(function, node);
return function;
@@ -1645,7 +1650,8 @@ public class TypeScriptASTConverter {
paramTypes,
paramDecorators,
returnType,
thisType);
thisType,
getOptionalParameterIndices(node));
attachSymbolInformation(function, node);
attachStaticType(function, node);
attachDeclaredSignature(function, node);
@@ -1890,6 +1896,19 @@ public class TypeScriptASTConverter {
return result;
}
private IntList getOptionalParameterIndices(JsonObject function) throws ParseError {
IntList list = IntList.create(0);
int index = -1;
for (JsonElement param : getProperParameters(function)) {
++index;
if (param.getAsJsonObject().has("questionToken")) {
list.add(index);
;
}
}
return list;
}
private List<FieldDefinition> convertParameterFields(JsonObject function) throws ParseError {
List<FieldDefinition> result = new ArrayList<>();
int index = -1;

View File

@@ -749,6 +749,21 @@ class Parameter extends BindingPattern {
JSDocTag getJSDocTag() {
none() // overridden in SimpleParameter
}
/**
* Holds if this is a parameter declared optional with the `?` token.
*
* Note that this does not hold for rest parameters, and does not in general
* hold for parameters with defaults.
*
* For example, `x`, is declared optional below:
* ```
* function f(x?: number) {}
* ```
*/
predicate isDeclaredOptional() {
isOptionalParameterDeclaration(this)
}
}
/**

View File

@@ -520,6 +520,7 @@ hasProtectedKeyword (int id: @property ref);
hasReadonlyKeyword (int id: @property ref);
isOptionalMember (int id: @property ref);
hasDefiniteAssignmentAssertion (int id: @field_or_vardeclarator ref);
isOptionalParameterDeclaration (unique int parameter: @pattern ref);
#keyset[constructor, param_index]
parameter_fields(

View File

@@ -0,0 +1,15 @@
| tst.ts:1:23:1:23 | y |
| tst.ts:2:16:2:16 | y |
| tst.ts:6:28:6:28 | y |
| tst.ts:8:12:8:12 | x |
| tst.ts:8:24:8:24 | y |
| tst.ts:9:13:9:13 | x |
| tst.ts:13:7:13:7 | x |
| tst.ts:14:13:14:13 | x |
| tst.ts:15:17:15:17 | y |
| tst.ts:18:26:18:26 | y |
| tst.ts:18:30:18:30 | z |
| tst.ts:18:34:18:34 | w |
| tst.ts:20:40:20:45 | {x, y} |
| tst.ts:20:49:20:51 | [w] |
| withDefault.ts:1:22:1:22 | x |

View File

@@ -0,0 +1,3 @@
import javascript
query Parameter optionalParams() { result.isDeclaredOptional() }

View File

@@ -0,0 +1,20 @@
function f(x: number, y?: string) {
return (x, y?) => {};
}
class C {
constructor(x: number, y?: string) {}
method(x?: number, y?: string) {}
noTypes(x?) {}
}
interface I {
m(x?: number);
field: (x?: number) => void;
(x: number, y?: string): void;
}
function manyDefaults(x, y?, z?, w?) {}
declare function optionalDestructuring({x, y}?, [w]?);

View File

@@ -0,0 +1 @@
function withDefault(x? = 5) {} // not valid syntax