Merge pull request #2379 from asger-semmle/typescript-fixes

TS: A bunch of TypeScript fixes
This commit is contained in:
Esben Sparre Andreasen
2019-11-22 13:31:30 +01:00
committed by GitHub
58 changed files with 3116 additions and 80 deletions

View File

@@ -8,7 +8,8 @@
"build": "tsc --project tsconfig.json",
"check": "tsc --noEmit --project . && tslint --project .",
"lint": "tslint --project .",
"lint-fix": "tslint --project . --fix"
"lint-fix": "tslint --project . --fix",
"watch": "tsc -p . -w --sourceMap"
},
"devDependencies": {
"@types/node": "12.7.11",

View File

@@ -198,7 +198,9 @@ export function augmentAst(ast: AugmentedSourceFile, code: string, project: Proj
: null;
let type = contextualType || typeChecker.getTypeAtLocation(node);
if (type != null) {
let id = typeTable.buildType(type);
let parent = node.parent;
let unfoldAlias = ts.isTypeAliasDeclaration(parent) && node === parent.type;
let id = typeTable.buildType(type, unfoldAlias);
if (id != null) {
node.$type = id;
}

View File

@@ -253,7 +253,7 @@ function handleOpenProjectCommand(command: OpenProjectCommand) {
fileExists: (path: string) => fs.existsSync(path),
readFile: ts.sys.readFile,
};
let config = ts.parseJsonConfigFileContent(tsConfig, parseConfigHost, basePath);
let config = ts.parseJsonConfigFileContent(tsConfig.config, parseConfigHost, basePath);
let project = new Project(tsConfigFilename, config, state.typeTable);
project.load();
@@ -272,7 +272,9 @@ function handleOpenProjectCommand(command: OpenProjectCommand) {
});
for (let typeRoot of typeRoots || []) {
traverseTypeRoot(typeRoot, "");
if (fs.existsSync(typeRoot) && fs.statSync(typeRoot).isDirectory()) {
traverseTypeRoot(typeRoot, "");
}
}
for (let sourceFile of program.getSourceFiles()) {

View File

@@ -92,6 +92,20 @@ function isTypeofCandidateSymbol(symbol: ts.Symbol) {
const signatureKinds = [ts.SignatureKind.Call, ts.SignatureKind.Construct];
/**
* Bitmask of flags set on a signature, but not exposed in the public API.
*/
const enum InternalSignatureFlags {
HasRestParameter = 1
}
/**
* Signature interface with some internal properties exposed.
*/
interface AugmentedSignature extends ts.Signature {
flags?: InternalSignatureFlags;
}
/**
* Encodes property lookup tuples `(baseType, name, property)` as three
* staggered arrays.
@@ -102,6 +116,16 @@ interface PropertyLookupTable {
propertyTypes: number[];
}
/**
* Encodes `(aliasType, underlyingType)` tuples as two staggered arrays.
*
* Such a tuple denotes that `aliasType` is an alias for `underlyingType`.
*/
interface TypeAliasTable {
aliasTypes: number[];
underlyingTypes: number[];
}
/**
* Encodes type signature tuples `(baseType, kind, index, signature)` as four
* staggered arrays.
@@ -287,6 +311,11 @@ export class TypeTable {
propertyTypes: [],
};
private typeAliases: TypeAliasTable = {
aliasTypes: [],
underlyingTypes: [],
};
private signatureMappings: SignatureTable = {
baseTypes: [],
kinds: [],
@@ -304,7 +333,7 @@ export class TypeTable {
propertyTypes: [],
};
private buildTypeWorklist: [ts.Type, number][] = [];
private buildTypeWorklist: [ts.Type, number, boolean][] = [];
private expansiveTypes: Map<number, boolean> = new Map();
@@ -372,9 +401,9 @@ export class TypeTable {
/**
* Gets the canonical ID for the given type, generating a fresh ID if necessary.
*/
public buildType(type: ts.Type): number | null {
public buildType(type: ts.Type, unfoldAlias: boolean): number | null {
this.isInShallowTypeContext = false;
let id = this.getId(type);
let id = this.getId(type, unfoldAlias);
this.iterateBuildTypeWorklist();
if (id == null) return null;
return id;
@@ -385,7 +414,7 @@ export class TypeTable {
*
* Returns `null` if we do not support extraction of this type.
*/
public getId(type: ts.Type): number | null {
public getId(type: ts.Type, unfoldAlias: boolean): number | null {
if (this.typeRecursionDepth > 100) {
// Ignore infinitely nested anonymous types, such as `{x: {x: {x: ... }}}`.
// Such a type can't be written directly with TypeScript syntax (as it would need to be named),
@@ -397,19 +426,19 @@ export class TypeTable {
type = this.typeChecker.getBaseTypeOfLiteralType(type);
}
++this.typeRecursionDepth;
let content = this.getTypeString(type);
let content = this.getTypeString(type, unfoldAlias);
--this.typeRecursionDepth;
if (content == null) return null; // Type not supported.
let id = this.typeIds.get(content);
if (id == null) {
let stringValue = this.stringifyType(type);
let stringValue = this.stringifyType(type, unfoldAlias);
if (stringValue == null) {
return null; // Type not supported.
}
id = this.typeIds.size;
this.typeIds.set(content, id);
this.typeToStringValues.push(stringValue);
this.buildTypeWorklist.push([type, id]);
this.buildTypeWorklist.push([type, id, unfoldAlias]);
this.typeExtractionState.push(
this.isInShallowTypeContext ? TypeExtractionState.PendingShallow : TypeExtractionState.PendingFull);
// If the type is the self-type for a named type (not a generic instantiation of it),
@@ -426,20 +455,20 @@ export class TypeTable {
this.typeExtractionState[id] = TypeExtractionState.PendingFull;
} else if (state === TypeExtractionState.DoneShallow) {
this.typeExtractionState[id] = TypeExtractionState.PendingFull;
this.buildTypeWorklist.push([type, id]);
this.buildTypeWorklist.push([type, id, unfoldAlias]);
}
}
return id;
}
private stringifyType(type: ts.Type): string {
private stringifyType(type: ts.Type, unfoldAlias: boolean): string {
let formatFlags = unfoldAlias
? ts.TypeFormatFlags.InTypeAlias
: ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope;
let toStringValue: string;
// Some types can't be stringified. Just discard the type if we can't stringify it.
try {
toStringValue = this.typeChecker.typeToString(
type,
undefined,
ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope);
toStringValue = this.typeChecker.typeToString(type, undefined, formatFlags);
} catch (e) {
console.warn("Recovered from a compiler crash while stringifying a type. Discarding the type.");
console.warn(e.stack);
@@ -477,9 +506,9 @@ export class TypeTable {
/**
* Gets a string representing the kind and contents of the given type.
*/
private getTypeString(type: AugmentedType): string | null {
private getTypeString(type: AugmentedType, unfoldAlias: boolean): string | null {
// Reference to a type alias.
if (type.aliasSymbol != null) {
if (!unfoldAlias && type.aliasSymbol != null) {
let tag = "reference;" + this.getSymbolId(type.aliasSymbol);
return type.aliasTypeArguments == null
? tag
@@ -499,7 +528,7 @@ export class TypeTable {
if (flags & ts.TypeFlags.TypeVariable) {
let enclosingType = getEnclosingTypeOfThisType(type);
if (enclosingType != null) {
return "this;" + this.getId(enclosingType);
return "this;" + this.getId(enclosingType, false);
} else if (symbol.parent == null) {
// The type variable is bound on a call signature. Only extract it by name.
return "lextypevar;" + symbol.name;
@@ -730,7 +759,7 @@ export class TypeTable {
private makeTypeStringVector(tag: string, types: ReadonlyArray<ts.Type>, length = types.length): string | null {
let hash = tag;
for (let i = 0; i < length; ++i) {
let id = this.getId(types[i]);
let id = this.getId(types[i], false);
if (id == null) return null;
hash += ";" + id;
}
@@ -748,7 +777,7 @@ export class TypeTable {
for (let property of type.getProperties()) {
let propertyType = this.typeChecker.getTypeOfSymbolAtLocation(property, this.arbitraryAstNode);
if (propertyType == null) return null;
let propertyTypeId = this.getId(propertyType);
let propertyTypeId = this.getId(propertyType, false);
if (propertyTypeId == null) return null;
hash += ";p" + this.getSymbolId(property) + ';' + propertyTypeId;
}
@@ -761,13 +790,13 @@ export class TypeTable {
}
let indexType = type.getStringIndexType();
if (indexType != null) {
let indexTypeId = this.getId(indexType);
let indexTypeId = this.getId(indexType, false);
if (indexTypeId == null) return null;
hash += ";s" + indexTypeId;
}
indexType = type.getNumberIndexType();
if (indexType != null) {
let indexTypeId = this.getId(indexType);
let indexTypeId = this.getId(indexType, false);
if (indexTypeId == null) return null;
hash += ";i" + indexTypeId;
}
@@ -789,6 +818,7 @@ export class TypeTable {
typeStrings: Array.from(this.typeIds.keys()),
typeToStringValues: this.typeToStringValues,
propertyLookups: this.propertyLookups,
typeAliases: this.typeAliases,
symbolStrings: Array.from(this.symbolIds.keys()),
moduleMappings: this.moduleMappings,
globalMappings: this.globalMappings,
@@ -812,10 +842,17 @@ export class TypeTable {
let worklist = this.buildTypeWorklist;
let typeExtractionState = this.typeExtractionState;
while (worklist.length > 0) {
let [type, id] = worklist.pop();
let [type, id, unfoldAlias] = worklist.pop();
let isShallowContext = typeExtractionState[id] === TypeExtractionState.PendingShallow;
if (isShallowContext && !isTypeAlwaysSafeToExpand(type)) {
typeExtractionState[id] = TypeExtractionState.DoneShallow;
} else if (type.aliasSymbol != null && !unfoldAlias) {
typeExtractionState[id] = TypeExtractionState.DoneFull;
let underlyingTypeId = this.getId(type, true);
if (underlyingTypeId != null) {
this.typeAliases.aliasTypes.push(id);
this.typeAliases.underlyingTypes.push(underlyingTypeId);
}
} else {
typeExtractionState[id] = TypeExtractionState.DoneFull;
this.isInShallowTypeContext = isShallowContext || this.isExpansiveTypeReference(type);
@@ -847,7 +884,7 @@ export class TypeTable {
for (let symbol of props) {
let propertyType = this.typeChecker.getTypeOfSymbolAtLocation(symbol, this.arbitraryAstNode);
if (propertyType == null) continue;
let propertyTypeId = this.getId(propertyType);
let propertyTypeId = this.getId(propertyType, false);
if (propertyTypeId == null) continue;
this.propertyLookups.baseTypes.push(id);
this.propertyLookups.names.push(symbol.name);
@@ -879,7 +916,7 @@ export class TypeTable {
/**
* Returns a unique string for the given call/constructor signature.
*/
private getSignatureString(kind: ts.SignatureKind, signature: ts.Signature): string {
private getSignatureString(kind: ts.SignatureKind, signature: AugmentedSignature): string {
let parameters = signature.getParameters();
let numberOfTypeParameters = signature.typeParameters == null
? 0
@@ -892,27 +929,51 @@ export class TypeTable {
break;
}
}
let returnTypeId = this.getId(signature.getReturnType());
let hasRestParam = (signature.flags & InternalSignatureFlags.HasRestParameter) !== 0;
let restParameterTag = '';
if (hasRestParam) {
if (requiredParameters === parameters.length) {
// Do not count the rest parameter as a required parameter
requiredParameters = parameters.length - 1;
}
if (parameters.length === 0) return null;
let restParameter = parameters[parameters.length - 1];
let restParameterType = this.typeChecker.getTypeOfSymbolAtLocation(restParameter, this.arbitraryAstNode);
if (restParameterType == null) return null;
let restParameterTypeId = this.getId(restParameterType, false);
if (restParameterTypeId == null) return null;
restParameterTag = '' + restParameterTypeId;
}
let returnTypeId = this.getId(signature.getReturnType(), false);
if (returnTypeId == null) {
return null;
}
let tag = `${kind};${numberOfTypeParameters};${requiredParameters};${returnTypeId}`;
let tag = `${kind};${numberOfTypeParameters};${requiredParameters};${restParameterTag};${returnTypeId}`;
for (let typeParameter of signature.typeParameters || []) {
tag += ";" + typeParameter.symbol.name;
let constraint = typeParameter.getConstraint();
let constraintId: number;
if (constraint == null || (constraintId = this.getId(constraint)) == null) {
if (constraint == null || (constraintId = this.getId(constraint, false)) == null) {
tag += ";";
} else {
tag += ";" + constraintId;
}
}
for (let parameter of parameters) {
for (let paramIndex = 0; paramIndex < parameters.length; ++paramIndex) {
let parameter = parameters[paramIndex];
let parameterType = this.typeChecker.getTypeOfSymbolAtLocation(parameter, this.arbitraryAstNode);
if (parameterType == null) {
return null;
}
let parameterTypeId = this.getId(parameterType);
let isRestParameter = hasRestParam && (paramIndex === parameters.length - 1);
if (isRestParameter) {
// The type of the rest parameter is the array type, but we wish to extract the non-array type.
if (!isTypeReference(parameterType)) return null;
let typeArguments = parameterType.typeArguments;
if (typeArguments == null || typeArguments.length === 0) return null;
parameterType = typeArguments[0];
}
let parameterTypeId = this.getId(parameterType, false);
if (parameterTypeId == null) {
return null;
}
@@ -946,7 +1007,7 @@ export class TypeTable {
private extractIndexer(baseType: number, indexType: ts.Type, table: IndexerTable) {
if (indexType == null) return;
let indexTypeId = this.getId(indexType);
let indexTypeId = this.getId(indexType, false);
if (indexTypeId == null) return;
table.baseTypes.push(baseType);
table.propertyTypes.push(indexTypeId);
@@ -1017,7 +1078,7 @@ export class TypeTable {
let selfType = this.getSelfType(type);
if (selfType != null) {
this.checkExpansiveness(selfType);
let id = this.getId(selfType);
let id = this.getId(selfType, false);
return this.expansiveTypes.get(id);
}
return false;
@@ -1086,7 +1147,7 @@ export class TypeTable {
search(type, 0);
function search(type: ts.TypeReference, expansionDepth: number): number | null {
let id = typeTable.getId(type);
let id = typeTable.getId(type, false);
if (id == null) return null;
let index = indexTable.get(id);

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 optionalParameterIndices;
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 optionalParameterIndices) {
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.optionalParameterIndices = optionalParameterIndices;
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 optionalParameterIndices;
}
}

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

@@ -578,6 +578,11 @@ public class AutoBuild {
for (File sourceFile : project.getSourceFiles()) {
Path sourcePath = sourceFile.toPath();
if (!files.contains(normalizePath(sourcePath))) continue;
if (!FileType.TYPESCRIPT.getExtensions().contains(FileUtil.extension(sourcePath))) {
// For the time being, skip non-TypeScript files, even if the TypeScript
// compiler can parse them for us.
continue;
}
if (!extractedFiles.contains(sourcePath)) {
typeScriptFiles.add(sourcePath.toFile());
}

View File

@@ -37,7 +37,7 @@ public class Main {
* A version identifier that should be updated every time the extractor changes in such a way that
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
*/
public static final String EXTRACTOR_VERSION = "2019-11-17";
public static final String EXTRACTOR_VERSION = "2019-11-19";
public static final Pattern NEWLINE = Pattern.compile("\n");
@@ -147,7 +147,8 @@ public class Main {
List<File> filesToExtract = new ArrayList<>();
for (File sourceFile : project.getSourceFiles()) {
if (files.contains(normalizeFile(sourceFile))
&& !extractedFiles.contains(sourceFile.getAbsoluteFile())) {
&& !extractedFiles.contains(sourceFile.getAbsoluteFile())
&& FileType.TYPESCRIPT.getExtensions().contains(FileUtil.extension(sourceFile))) {
filesToExtract.add(sourceFile);
}
}

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,18 @@ 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

@@ -291,10 +291,10 @@ public class TypeScriptParser {
LogbackUtils.getLogger(AbstractProcessBuilder.class).setLevel(Level.INFO);
String explicitPath = Env.systemEnv().get(PARSER_WRAPPER_PATH_ENV_VAR);
String semmleDistVar = Env.systemEnv().get(Env.Var.SEMMLE_DIST.name());
if (semmleDistVar != null && !semmleDistVar.isEmpty()) {
parserWrapper = new File(semmleDistVar, "tools/typescript-parser-wrapper/main.js");
} else if (explicitPath != null) {
if (explicitPath != null) {
parserWrapper = new File(explicitPath);
} else if (semmleDistVar != null && !semmleDistVar.isEmpty()) {
parserWrapper = new File(semmleDistVar, "tools/typescript-parser-wrapper/main.js");
} else {
throw new CatastrophicError(
"Could not find TypeScript parser: " + Env.Var.SEMMLE_DIST.name() + " is not set.");

View File

@@ -81,6 +81,7 @@ public class TypeExtractor {
extractType(i);
}
extractPropertyLookups(table.getPropertyLookups());
extractTypeAliases(table.getTypeAliases());
for (int i = 0; i < table.getNumberOfSymbols(); ++i) {
extractSymbol(i);
}
@@ -161,6 +162,19 @@ public class TypeExtractor {
}
}
private void extractTypeAliases(JsonObject aliases) {
JsonArray aliasTypes = aliases.get("aliasTypes").getAsJsonArray();
JsonArray underlyingTypes = aliases.get("underlyingTypes").getAsJsonArray();
for (int i = 0; i < aliasTypes.size(); ++i) {
int aliasType = aliasTypes.get(i).getAsInt();
int underlyingType = underlyingTypes.get(i).getAsInt();
trapWriter.addTuple(
"type_alias",
trapWriter.globalID("type;" + aliasType),
trapWriter.globalID("type;" + underlyingType));
}
}
private void extractSymbol(int index) {
// Format is: kind;decl;parent;name
String[] parts = split(table.getSymbolString(index), 4);
@@ -187,13 +201,18 @@ public class TypeExtractor {
private void extractSignature(int index) {
// Format is:
// kind;numTypeParams;requiredParams;returnType(;paramName;paramType)*
// kind;numTypeParams;requiredParams;restParamType;returnType(;paramName;paramType)*
String[] parts = split(table.getSignatureString(index));
Label label = trapWriter.globalID("signature;" + index);
int kind = Integer.parseInt(parts[0]);
int numberOfTypeParameters = Integer.parseInt(parts[1]);
int requiredParameters = Integer.parseInt(parts[2]);
Label returnType = trapWriter.globalID("type;" + parts[3]);
String restParamTypeTag = parts[3];
if (!restParamTypeTag.isEmpty()) {
trapWriter.addTuple(
"signature_rest_parameter", label, trapWriter.globalID("type;" + restParamTypeTag));
}
Label returnType = trapWriter.globalID("type;" + parts[4]);
trapWriter.addTuple(
"signature_types",
label,
@@ -202,9 +221,9 @@ public class TypeExtractor {
numberOfTypeParameters,
requiredParameters);
trapWriter.addTuple("signature_contains_type", returnType, label, -1);
int numberOfParameters = (parts.length - 4) / 2; // includes type parameters
int numberOfParameters = (parts.length - 5) / 2; // includes type parameters
for (int i = 0; i < numberOfParameters; ++i) {
int partIndex = 4 + (2 * i);
int partIndex = 5 + (2 * i);
String paramName = parts[partIndex];
String paramTypeId = parts[partIndex + 1];
if (paramTypeId.length() > 0) { // Unconstrained type parameters have an empty type ID.

View File

@@ -12,6 +12,7 @@ public class TypeTable {
private final JsonArray typeStrings;
private final JsonArray typeToStringValues;
private final JsonObject propertyLookups;
private final JsonObject typeAliases;
private final JsonArray symbolStrings;
private final JsonObject moduleMappings;
private final JsonObject globalMappings;
@@ -27,6 +28,7 @@ public class TypeTable {
this.typeStrings = typeTable.get("typeStrings").getAsJsonArray();
this.typeToStringValues = typeTable.get("typeToStringValues").getAsJsonArray();
this.propertyLookups = typeTable.get("propertyLookups").getAsJsonObject();
this.typeAliases = typeTable.get("typeAliases").getAsJsonObject();
this.symbolStrings = typeTable.get("symbolStrings").getAsJsonArray();
this.moduleMappings = typeTable.get("moduleMappings").getAsJsonObject();
this.globalMappings = typeTable.get("globalMappings").getAsJsonObject();
@@ -51,6 +53,10 @@ public class TypeTable {
return propertyLookups;
}
public JsonObject getTypeAliases() {
return typeAliases;
}
public int getNumberOfTypes() {
return typeStrings.size();
}

View File

@@ -1816,7 +1816,7 @@ class Type extends @type {
*
* For example, for a type `(S & T) | U` this gets the types `S`, `T`, and `U`.
*/
Type unfold() {
Type unfoldUnionAndIntersection() {
not result instanceof UnionOrIntersectionType and
(
result = this
@@ -1829,6 +1829,27 @@ class Type extends @type {
)
}
/**
* Repeatedly unfolds unions, intersections, and type aliases and gets any of the underlying types,
* or this type itself if it is not a union or intersection.
*
* For example, the type `(S & T) | U` unfolds to `S`, `T`, and `U`.
*
* If this is a type alias, the alias is itself included in the result, but this is not the case for intermediate type aliases.
* For example:
* ```js
* type One = number | string;
* type Two = One | Function & {x: string};
* One; // unfolds to number, string, and One
* Two; // unfolds to number, string, One, Function, {x: string}, and Two
* ```
*/
Type unfold() {
result = unfoldUnionAndIntersection()
or
result = this.(TypeAliasReference).getAliasedType().unfoldUnionAndIntersection()
}
/**
* Holds if this refers to the given named type, or is declared as a subtype thereof,
* or is a union or intersection containing such a type.
@@ -2287,6 +2308,24 @@ class EnumLiteralType extends TypeReference {
EnumMember getEnumMember() { result = declaration }
}
/**
* A type that refers to a type alias.
*/
class TypeAliasReference extends TypeReference {
TypeAliasReference() {
type_alias(this, _)
}
/**
* Gets the type behind the type alias.
*
* For example, for `type B<T> = T[][]`, this maps the type `B<number>` to `number[][]`.
*/
Type getAliasedType() {
type_alias(this, result)
}
}
/**
* An anonymous interface type, such as `{ x: number }`.
*/
@@ -2516,17 +2555,19 @@ class CallSignatureType extends @signature_type {
predicate hasTypeParameters() { getNumTypeParameter() > 0 }
/**
* Gets the type of the `n`th parameter of this signature.
* Gets the type of the `n`th parameter declared in this signature.
*
* If the `n`th parameter is a rest parameter `...T[]`, gets type `T`.
*/
Type getParameter(int n) { n >= 0 and result = getChild(n + getNumTypeParameter()) }
/**
* Gets the type of a parameter of this signature.
* Gets the type of a parameter of this signature, including the rest parameter, if any.
*/
Type getAParameter() { result = getParameter(_) }
/**
* Gets the number of parameters.
* Gets the number of parameters, including the rest parameter, if any.
*/
int getNumParameter() { result = count(int i | exists(getParameter(i))) }
@@ -2538,7 +2579,7 @@ class CallSignatureType extends @signature_type {
/**
* Gets the number of optional parameters, that is,
* parameters that are marked as optional with the `?` suffix.
* parameters that are marked as optional with the `?` suffix or is a rest parameter.
*/
int getNumOptionalParameter() { result = getNumParameter() - getNumRequiredParameter() }
@@ -2552,7 +2593,9 @@ class CallSignatureType extends @signature_type {
}
/**
* Holds if the `n`th parameter is declared optional with the `?` suffix.
* Holds if the `n`th parameter is declared optional with the `?` suffix or is the rest parameter.
*
* Note that rest parameters are not considered optional in this sense.
*/
predicate isOptionalParameter(int n) {
exists(getParameter(n)) and
@@ -2571,6 +2614,30 @@ class CallSignatureType extends @signature_type {
* Gets the name of a parameter of this signature.
*/
string getAParameterName() { result = getParameterName(_) }
/**
* Holds if this signature declares a rest parameter, such as `(x: number, ...y: string[])`.
*/
predicate hasRestParameter() { signature_rest_parameter(this, _) }
/**
* Gets the type of the rest parameter, if any.
*
* For example, for the signature `(...y: string[])`, this gets the type `string`.
*/
Type getRestParameterType() {
hasRestParameter() and
result = getParameter(getNumParameter() - 1)
}
/**
* Gets the type of the rest parameter as an array, if it exists.
*
* For example, for the signature `(...y: string[])`, this gets the type `string[]`.
*/
PlainArrayType getRestParameterArrayType() {
signature_rest_parameter(this, result)
}
}
/**

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(
@@ -703,6 +704,10 @@ type_property(
varchar(900) name: string ref,
int propertyType: @type ref);
type_alias(
unique int aliasType: @type ref,
int underlyingType: @type ref);
@literaltype = @stringliteraltype | @numberliteraltype | @booleanliteraltype | @bigintliteraltype;
@type_with_literal_value = @stringliteraltype | @numberliteraltype | @bigintliteraltype;
type_literal_value(
@@ -717,6 +722,11 @@ signature_types (
int required_params: int ref
);
signature_rest_parameter(
unique int sig: @signature_type ref,
int rest_param_arra_type: @type ref
);
case @signature_type.kind of
0 = @function_signature_type
| 1 = @constructor_signature_type

View File

@@ -8006,6 +8006,17 @@
<dependencies/>
</relation>
<relation>
<name>isOptionalParameterDeclaration</name>
<cardinality>3966</cardinality>
<columnsizes>
<e>
<k>parameter</k>
<v>3966</v>
</e>
</columnsizes>
<dependencies/>
</relation>
<relation>
<name>parameter_fields</name>
<cardinality>2693</cardinality>
<columnsizes>
@@ -13516,6 +13527,54 @@
<dependencies/>
</relation>
<relation>
<name>type_alias</name>
<cardinality>1386</cardinality>
<columnsizes>
<e>
<k>aliasType</k>
<v>1386</v>
</e>
<e>
<k>underlyingType</k>
<v>1361</v>
</e>
</columnsizes>
<dependencies>
<dep>
<src>underlyingType</src>
<trg>aliasType</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>1</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>aliasType</src>
<trg>underlyingType</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>1</v>
</b>
</bs>
</hist>
</val>
</dep>
</dependencies>
</relation>
<relation>
<name>type_literal_value</name>
<cardinality>31882</cardinality>
<columnsizes>
@@ -14222,6 +14281,54 @@
</dependencies>
</relation>
<relation>
<name>signature_rest_parameter</name>
<cardinality>19521</cardinality>
<columnsizes>
<e>
<k>sig</k>
<v>19521</v>
</e>
<e>
<k>rest_param_arra_type</k>
<v>14259</v>
</e>
</columnsizes>
<dependencies>
<dep>
<src>rest_param_arra_type</src>
<trg>sig</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>1</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>sig</src>
<trg>rest_param_arra_type</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>1</v>
</b>
</bs>
</hist>
</val>
</dep>
</dependencies>
</relation>
<relation>
<name>type_contains_signature</name>
<cardinality>87640</cardinality>
<columnsizes>

View File

@@ -47,14 +47,39 @@ test_ExprSignature
| tst.ts:45:15:45:15 | x | string |
| tst.ts:46:3:46:25 | constru ... umber); | any |
| tst.ts:46:15:46:15 | x | number |
| tst.ts:50:3:50:36 | method( ... ing[]); | (x: number, ...y: string[]): any |
| tst.ts:50:10:50:10 | x | number |
| tst.ts:50:24:50:24 | y | string[] |
| tst.ts:51:4:51:4 | x | number |
| tst.ts:51:18:51:18 | y | string[] |
| tst.ts:52:7:52:7 | x | number |
| tst.ts:52:21:52:21 | y | string[] |
| tst.ts:54:3:54:34 | method2 ... ing[]); | (x: number, y: string[]): any |
| tst.ts:54:11:54:11 | x | number |
| tst.ts:54:22:54:22 | y | string[] |
| tst.ts:55:3:55:32 | method3 ... tring); | (x: number, y: string): any |
| tst.ts:55:11:55:11 | x | number |
| tst.ts:55:22:55:22 | y | string |
| tst.ts:59:3:59:25 | method( ... ing[]); | (...y: string[]): any |
| tst.ts:59:13:59:13 | y | string[] |
| tst.ts:60:7:60:7 | y | string[] |
| tst.ts:61:10:61:10 | y | string[] |
| tst.ts:63:3:63:23 | method2 ... ing[]); | (y: string[]): any |
| tst.ts:63:11:63:11 | y | string[] |
| tst.ts:64:3:64:21 | method3(y: string); | (y: string): any |
| tst.ts:64:11:64:11 | y | string |
test_TypeReferenceSig
| Callable | function | 0 | (x: number): string |
| Newable | constructor | 0 | new (x: number): any |
| OnlyRestParams | constructor | 0 | new (...y: string[]): any |
| OnlyRestParams | function | 0 | (...y: string[]): any |
| OverloadedCallable | function | 0 | (x: number): number |
| OverloadedCallable | function | 1 | (x: string): string |
| OverloadedCallable | function | 2 | (x: any): any |
| OverloadedNewable | constructor | 0 | new (x: number): OverloadedNewable |
| OverloadedNewable | constructor | 1 | new (x: any): any |
| WithRestParams | constructor | 0 | new (x: number, ...y: string[]): any |
| WithRestParams | function | 0 | (x: number, ...y: string[]): any |
test_FunctionCallSig
| tst.ts:2:3:2:22 | (x: number): string; | (x: number): string |
| tst.ts:6:3:6:22 | (x: number): number; | (x: number): number |
@@ -72,3 +97,62 @@ test_FunctionCallSig
| tst.ts:40:1:42:1 | functio ... oo");\\n} | (g: Generic<string>): string |
| tst.ts:45:3:45:25 | constru ... tring); | new (x: string): C |
| tst.ts:46:3:46:25 | constru ... umber); | new (x: number): C |
| tst.ts:50:3:50:36 | method( ... ing[]); | (x: number, ...y: string[]): any |
| tst.ts:51:3:51:30 | (x: num ... ing[]); | (x: number, ...y: string[]): any |
| tst.ts:52:3:52:33 | new(x: ... ing[]); | new (x: number, ...y: string[]): any |
| tst.ts:54:3:54:34 | method2 ... ing[]); | (x: number, y: string[]): any |
| tst.ts:55:3:55:32 | method3 ... tring); | (x: number, y: string): any |
| tst.ts:59:3:59:25 | method( ... ing[]); | (...y: string[]): any |
| tst.ts:60:3:60:19 | (...y: string[]); | (...y: string[]): any |
| tst.ts:61:3:61:22 | new(...y: string[]); | new (...y: string[]): any |
| tst.ts:63:3:63:23 | method2 ... ing[]); | (y: string[]): any |
| tst.ts:64:3:64:21 | method3(y: string); | (y: string): any |
test_getRestParameterType
| (...items: (string \| ConcatArray<string>)[]): string[] | string \| ConcatArray<string> |
| (...items: ConcatArray<string>[]): string[] | ConcatArray<string> |
| (...items: string[]): number | string |
| (...strings: string[]): string | string |
| (...y: string[]): any | string |
| (start: number, deleteCount: number, ...items: string[]): string[] | string |
| (substring: string, ...args: any[]): string | any |
| (x: number, ...y: string[]): any | string |
| new (...y: string[]): any | string |
| new (x: number, ...y: string[]): any | string |
test_getRestParameterArray
| (...items: (string \| ConcatArray<string>)[]): string[] | (string \| ConcatArray<string>)[] |
| (...items: ConcatArray<string>[]): string[] | ConcatArray<string>[] |
| (...items: string[]): number | string[] |
| (...strings: string[]): string | string[] |
| (...y: string[]): any | string[] |
| (start: number, deleteCount: number, ...items: string[]): string[] | string[] |
| (substring: string, ...args: any[]): string | any[] |
| (x: number, ...y: string[]): any | string[] |
| new (...y: string[]): any | string[] |
| new (x: number, ...y: string[]): any | string[] |
test_RestSig_getParameter
| (...items: (string \| ConcatArray<string>)[]): string[] | 0 | items | string \| ConcatArray<string> |
| (...items: ConcatArray<string>[]): string[] | 0 | items | ConcatArray<string> |
| (...items: string[]): number | 0 | items | string |
| (...strings: string[]): string | 0 | strings | string |
| (...y: string[]): any | 0 | y | string |
| (start: number, deleteCount: number, ...items: string[]): string[] | 0 | start | number |
| (start: number, deleteCount: number, ...items: string[]): string[] | 1 | deleteCount | number |
| (start: number, deleteCount: number, ...items: string[]): string[] | 2 | items | string |
| (substring: string, ...args: any[]): string | 0 | substring | string |
| (substring: string, ...args: any[]): string | 1 | args | any |
| (x: number, ...y: string[]): any | 0 | x | number |
| (x: number, ...y: string[]): any | 1 | y | string |
| new (...y: string[]): any | 0 | y | string |
| new (x: number, ...y: string[]): any | 0 | x | number |
| new (x: number, ...y: string[]): any | 1 | y | string |
test_RestSig_numRequiredParams
| (...items: (string \| ConcatArray<string>)[]): string[] | 0 |
| (...items: ConcatArray<string>[]): string[] | 0 |
| (...items: string[]): number | 0 |
| (...strings: string[]): string | 0 |
| (...y: string[]): any | 0 |
| (start: number, deleteCount: number, ...items: string[]): string[] | 2 |
| (substring: string, ...args: any[]): string | 1 |
| (x: number, ...y: string[]): any | 1 |
| new (...y: string[]): any | 0 |
| new (x: number, ...y: string[]): any | 1 |

View File

@@ -20,3 +20,22 @@ query predicate test_TypeReferenceSig(TypeReference type, SignatureKind kind, in
query predicate test_FunctionCallSig(Function f, CallSignatureType sig) {
sig = f.getCallSignature()
}
query Type test_getRestParameterType(CallSignatureType sig) {
result = sig.getRestParameterType()
}
query Type test_getRestParameterArray(CallSignatureType sig) {
result = sig.getRestParameterArrayType()
}
query predicate test_RestSig_getParameter(CallSignatureType sig, int n, string name, Type type) {
sig.hasRestParameter() and
name = sig.getParameterName(n) and
type = sig.getParameter(n)
}
query int test_RestSig_numRequiredParams(CallSignatureType sig) {
sig.hasRestParameter() and
result = sig.getNumRequiredParameter()
}

View File

@@ -45,3 +45,21 @@ declare class C {
constructor(x: string);
constructor(x: number);
}
interface WithRestParams {
method(x: number, ...y: string[]);
(x: number, ...y: string[]);
new(x: number, ...y: string[]);
method2(x: number, y: string[]);
method3(x: number, y: string);
}
interface OnlyRestParams {
method(...y: string[]);
(...y: string[]);
new(...y: string[]);
method2(y: string[]);
method3(y: string);
}

View File

@@ -0,0 +1,9 @@
exprType
| tst.ts:1:5:1:16 | stringOrNUll | string \| null |
| tst.ts:2:5:2:21 | stringOrUndefined | string \| undefined |
| tst.ts:3:5:3:27 | stringO ... defined | string \| null \| undefined |
| tst.ts:4:5:4:16 | stringOrVoid | string \| void |
| tst.ts:7:5:7:21 | stringOrNullAlias | string \| null |
| tst.ts:8:5:8:32 | stringO ... defined | string \| null \| undefined |
| tst.ts:10:5:10:23 | arrayOfStringOrNull | (string \| null)[] |
unaliasedType

View File

@@ -0,0 +1,5 @@
import javascript
query Type exprType(Expr e) { result = e.getType() }
query Type unaliasedType(TypeAliasReference ref) { result = ref.getAliasedType() }

View File

@@ -0,0 +1,6 @@
{
"include": ["."],
"compilerOptions": {
"strict": true
}
}

View File

@@ -0,0 +1,10 @@
let stringOrNUll: string | null;
let stringOrUndefined: string | undefined;
let stringOrNullOrUndefined: string | null | undefined;
let stringOrVoid: string | void;
type StringOrNullAlias = string | null;
let stringOrNullAlias: StringOrNullAlias;
let stringOrNullAliasOrUndefined: StringOrNullAlias | undefined;
let arrayOfStringOrNull: Array<string | null>;

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

View File

@@ -0,0 +1,2 @@
import f = require("./tst");
f("world");

View File

@@ -0,0 +1,5 @@
| main.ts:1:8:1:8 | f | (x: string) => string |
| main.ts:1:20:1:26 | "./tst" | any |
| main.ts:2:1:2:1 | f | (x: string) => string |
| main.ts:2:1:2:10 | f("world") | string |
| main.ts:2:3:2:9 | "world" | "world" |

View File

@@ -0,0 +1,4 @@
import javascript
from Expr e
select e, e.getType()

View File

@@ -0,0 +1,6 @@
{
"compilerOptions": {
"allowJs": true
},
"include": ["."]
}

View File

@@ -0,0 +1,6 @@
/**
* @param {String} x
*/
module.exports = function(x) {
return 'Hello ' + x;
}

View File

@@ -0,0 +1 @@
| tst.ts:0:0:0:0 | tst.ts |

View File

@@ -0,0 +1,4 @@
import javascript
from File file
select file

View File

@@ -0,0 +1,6 @@
{
"include": ["."],
"compilerOptions": {
"typeRoots": ["does-not-exist"]
}
}

View File

@@ -1,2 +0,0 @@
| test.ts:3:1:7:6 | type Di ... \\n T; | Disjunction<T> |
| test.ts:9:8:9:65 | type Tr ... n<T>>>; | Disjunction<Disjunction<Disjunction<T>>> |

View File

@@ -0,0 +1,2 @@
| tst.ts:0:0:0:0 | tst.ts |
| typeroot.d.ts:0:0:0:0 | typeroot.d.ts |

View File

@@ -0,0 +1,4 @@
import javascript
from File file
select file

View File

@@ -0,0 +1,6 @@
{
"include": ["."],
"compilerOptions": {
"typeRoots": ["typeroot.d.ts"]
}
}

View File

@@ -1,3 +1,44 @@
rightHandSide
| tst.ts:1:1:1:16 | type A = number; | number |
| tst.ts:2:1:2:16 | type B<T> = T[]; | T[] |
| tst.ts:8:10:8:20 | type C = A; | number |
| tst.ts:15:1:15:23 | type Un ... \| Two; | One \| Two |
| tst.ts:17:1:17:36 | type Un ... mber }; | (One & { x: number; }) \| (Two & { x: number; }) |
| tst.ts:18:1:18:21 | type Un ... Union2; | (One & { x: number; }) \| (Two & { x: number; }) |
| tst.ts:19:1:19:21 | type Un ... Union3; | (One & { x: number; }) \| (Two & { x: number; }) |
| tst.ts:20:1:20:30 | type Un ... number; | number \| (One & { x: number; }) \| (Two & { x: n... |
getAliasedType
| B<T> | T[] |
| B<number> | number[] |
| Union | One \| Two |
| Union2 | (One & { x: number; }) \| (Two & { x: number; }) |
| Union5 | number \| (One & { x: number; }) \| (Two & { x: n... |
getTypeArgument
| B<T> | 0 | T |
| B<number> | 0 | number |
unfold
| B<T> | B<T> |
| B<T> | T[] |
| B<number> | B<number> |
| B<number> | number[] |
| Union | One |
| Union | Two |
| Union | Union |
| Union2 | One |
| Union2 | Two |
| Union2 | Union2 |
| Union2 | { x: number; } |
| Union5 | One |
| Union5 | Two |
| Union5 | Union5 |
| Union5 | number |
| Union5 | { x: number; } |
#select
| tst.ts:1:1:1:16 | type A = number; | tst.ts:1:6:1:6 | A | 0 | tst.ts:1:10:1:15 | number |
| tst.ts:2:1:2:16 | type B<T> = T[]; | tst.ts:2:6:2:6 | B | 1 | tst.ts:2:13:2:15 | T[] |
| tst.ts:8:10:8:20 | type C = A; | tst.ts:8:15:8:15 | C | 0 | tst.ts:8:19:8:19 | A |
| tst.ts:15:1:15:23 | type Un ... \| Two; | tst.ts:15:6:15:10 | Union | 0 | tst.ts:15:14:15:22 | One \| Two |
| tst.ts:17:1:17:36 | type Un ... mber }; | tst.ts:17:6:17:11 | Union2 | 0 | tst.ts:17:15:17:35 | Union & ... umber } |
| tst.ts:18:1:18:21 | type Un ... Union2; | tst.ts:18:6:18:11 | Union3 | 0 | tst.ts:18:15:18:20 | Union2 |
| tst.ts:19:1:19:21 | type Un ... Union3; | tst.ts:19:6:19:11 | Union4 | 0 | tst.ts:19:15:19:20 | Union3 |
| tst.ts:20:1:20:30 | type Un ... number; | tst.ts:20:6:20:11 | Union5 | 0 | tst.ts:20:15:20:29 | Union4 \| number |

View File

@@ -2,3 +2,19 @@ import javascript
from TypeAliasDeclaration decl
select decl, decl.getIdentifier(), decl.getNumTypeParameter(), decl.getDefinition()
query Type rightHandSide(TypeAliasDeclaration decl) {
result = decl.getDefinition().getType()
}
query Type getAliasedType(TypeAliasReference ref) {
result = ref.getAliasedType()
}
query Type getTypeArgument(TypeAliasReference ref, int n) {
result = ref.getTypeArgument(n)
}
query Type unfold(TypeAliasReference t) {
result = t.unfold()
}

View File

@@ -0,0 +1,3 @@
{
"include": ["."]
}

View File

@@ -8,3 +8,13 @@ namespace Q {
export type C = A;
}
var z: Q.C;
interface One { a: number }
interface Two { b: number }
type Union = One | Two;
type Union2 = Union & { x: number };
type Union3 = Union2;
type Union4 = Union3;
type Union5 = Union4 | number;

View File

@@ -74,7 +74,7 @@
| type_alias.ts:5:6:5:17 | ValueOrArray | ValueOrArray<T> |
| type_alias.ts:5:19:5:19 | T | T |
| type_alias.ts:5:24:5:24 | T | T |
| type_alias.ts:5:24:5:49 | T \| Arr ... ray<T>> | ValueOrArray<T> |
| type_alias.ts:5:24:5:49 | T \| Arr ... ray<T>> | T \| ValueOrArray<T>[] |
| type_alias.ts:5:28:5:32 | Array | T[] |
| type_alias.ts:5:28:5:49 | Array<V ... ray<T>> | ValueOrArray<T>[] |
| type_alias.ts:5:34:5:45 | ValueOrArray | ValueOrArray<T> |
@@ -84,7 +84,7 @@
| type_alias.ts:7:8:7:27 | ValueOrArray<number> | ValueOrArray<number> |
| type_alias.ts:7:21:7:26 | number | number |
| type_alias.ts:9:6:9:9 | Json | Json |
| type_alias.ts:10:5:15:12 | \| strin ... Json[] | Json |
| type_alias.ts:10:5:15:12 | \| strin ... Json[] | string \| number \| boolean \| { [property: string... |
| type_alias.ts:10:7:10:12 | string | string |
| type_alias.ts:11:7:11:12 | number | number |
| type_alias.ts:12:7:12:13 | boolean | boolean |
@@ -96,7 +96,7 @@
| type_alias.ts:15:7:15:12 | Json[] | Json[] |
| type_alias.ts:17:11:17:14 | Json | Json |
| type_alias.ts:19:6:19:16 | VirtualNode | VirtualNode |
| type_alias.ts:20:5:21:56 | \| strin ... Node[]] | VirtualNode |
| type_alias.ts:20:5:21:56 | \| strin ... Node[]] | string \| [string, { [key: string]: any; }, ...V... |
| type_alias.ts:20:7:20:12 | string | string |
| type_alias.ts:21:7:21:56 | [string ... Node[]] | [string, { [key: string]: any; }, ...VirtualNod... |
| type_alias.ts:21:8:21:13 | string | string |
@@ -123,7 +123,7 @@
| type_definitions.ts:21:6:21:10 | Alias | Alias<T> |
| type_definitions.ts:21:12:21:12 | T | T |
| type_definitions.ts:21:17:21:17 | T | T |
| type_definitions.ts:21:17:21:19 | T[] | Alias<T> |
| type_definitions.ts:21:17:21:19 | T[] | T[] |
| type_definitions.ts:22:26:22:30 | Alias | Alias<T> |
| type_definitions.ts:22:26:22:38 | Alias<number> | Alias<number> |
| type_definitions.ts:22:32:22:37 | number | number |

View File

@@ -1,5 +1,5 @@
{
"compilerOptions": {
"lib": ["es2015.symbol"]
"lib": ["es2015"]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: improve TypeScript support for type aliases, rest parameters, and optional parameters
compatibility: backwards