Merge pull request #19640 from asgerf/js/no-type-extraction

JS: Disable type extraction
This commit is contained in:
Asger F
2025-07-03 11:18:42 +02:00
committed by GitHub
259 changed files with 737 additions and 13383 deletions

View File

@@ -10,7 +10,6 @@ export interface AugmentedSourceFile extends ts.SourceFile {
/** Internal property that we expose as a workaround. */
redirectInfo?: object | null;
$tokens?: Token[];
$symbol?: number;
$lineStarts?: ReadonlyArray<number>;
}
@@ -18,11 +17,6 @@ export interface AugmentedNode extends ts.Node {
$pos?: any;
$end?: any;
$declarationKind?: string;
$type?: number;
$symbol?: number;
$resolvedSignature?: number;
$overloadIndex?: number;
$declaredSignature?: number;
}
export type AugmentedPos = number;
@@ -73,7 +67,7 @@ function tryGetTypeOfNode(typeChecker: ts.TypeChecker, node: AugmentedNode): ts.
} catch (e) {
let sourceFile = node.getSourceFile();
let { line, character } = sourceFile.getLineAndCharacterOfPosition(node.pos);
console.warn(`Could not compute type of ${ts.SyntaxKind[node.kind]} at ${sourceFile.fileName}:${line+1}:${character+1}`);
console.warn(`Could not compute type of ${ts.SyntaxKind[node.kind]} at ${sourceFile.fileName}:${line + 1}:${character + 1}`);
return null;
}
}
@@ -157,17 +151,6 @@ export function augmentAst(ast: AugmentedSourceFile, code: string, project: Proj
});
}
let typeChecker = project && project.program.getTypeChecker();
let typeTable = project && project.typeTable;
// Associate a symbol with the AST node root, in case it is a module.
if (typeTable != null) {
let symbol = typeChecker.getSymbolAtLocation(ast);
if (symbol != null) {
ast.$symbol = typeTable.getSymbolId(symbol);
}
}
visitAstNode(ast);
function visitAstNode(node: AugmentedNode) {
ts.forEachChild(node, visitAstNode);
@@ -190,192 +173,5 @@ export function augmentAst(ast: AugmentedSourceFile, code: string, project: Proj
node.$declarationKind = "var";
}
}
if (typeChecker != null) {
if (isTypedNode(node) && !typeTable.skipExtractingTypes) {
let contextualType = isContextuallyTypedNode(node)
? typeChecker.getContextualType(node)
: null;
let type = contextualType || tryGetTypeOfNode(typeChecker, node);
if (type != null) {
let parent = node.parent;
let unfoldAlias = ts.isTypeAliasDeclaration(parent) && node === parent.type;
let id = typeTable.buildType(type, unfoldAlias);
if (id != null) {
node.$type = id;
}
}
// Extract the target call signature of a function call.
// In case the callee is overloaded or generic, this is not something we can
// derive from the callee type in QL.
if (ts.isCallOrNewExpression(node)) {
let kind = ts.isCallExpression(node) ? ts.SignatureKind.Call : ts.SignatureKind.Construct;
let resolvedSignature = typeChecker.getResolvedSignature(node);
if (resolvedSignature != null) {
let resolvedId = typeTable.getSignatureId(kind, resolvedSignature);
if (resolvedId != null) {
(node as AugmentedNode).$resolvedSignature = resolvedId;
}
let declaration = resolvedSignature.declaration;
if (declaration != null) {
// Find the generic signature, i.e. without call-site type arguments substituted,
// but with overloading resolved.
let calleeType = typeChecker.getTypeAtLocation(node.expression);
if (calleeType != null && declaration != null) {
let calleeSignatures = typeChecker.getSignaturesOfType(calleeType, kind);
for (let i = 0; i < calleeSignatures.length; ++i) {
if (calleeSignatures[i].declaration === declaration) {
(node as AugmentedNode).$overloadIndex = i;
break;
}
}
}
// Extract the symbol so the declaration can be found from QL.
let name = (declaration as any).name;
let symbol = name && typeChecker.getSymbolAtLocation(name);
if (symbol != null) {
(node as AugmentedNode).$symbol = typeTable.getSymbolId(symbol);
}
}
}
}
}
let symbolNode =
isNamedNodeWithSymbol(node) ? node.name :
ts.isImportDeclaration(node) ? node.moduleSpecifier :
ts.isExternalModuleReference(node) ? node.expression :
null;
if (symbolNode != null) {
let symbol = typeChecker.getSymbolAtLocation(symbolNode);
if (symbol != null) {
node.$symbol = typeTable.getSymbolId(symbol);
}
}
if (ts.isTypeReferenceNode(node)) {
// For type references we inject a symbol on each part of the name.
// We traverse each node in the name here since we know these are part of
// a type annotation. This means we don't have to do it for all identifiers
// and qualified names, which would extract more information than we need.
let namePart: (ts.EntityName & AugmentedNode) = node.typeName;
while (ts.isQualifiedName(namePart)) {
let symbol = typeChecker.getSymbolAtLocation(namePart.right);
if (symbol != null) {
namePart.$symbol = typeTable.getSymbolId(symbol);
}
// Traverse into the prefix.
namePart = namePart.left;
}
let symbol = typeChecker.getSymbolAtLocation(namePart);
if (symbol != null) {
namePart.$symbol = typeTable.getSymbolId(symbol);
}
}
if (ts.isFunctionLike(node)) {
let signature = typeChecker.getSignatureFromDeclaration(node);
if (signature != null) {
let kind = ts.isConstructSignatureDeclaration(node) || ts.isConstructorDeclaration(node)
? ts.SignatureKind.Construct : ts.SignatureKind.Call;
let id = typeTable.getSignatureId(kind, signature);
if (id != null) {
(node as AugmentedNode).$declaredSignature = id;
}
}
}
}
}
}
type NamedNodeWithSymbol = AugmentedNode & (ts.ClassDeclaration | ts.InterfaceDeclaration
| ts.TypeAliasDeclaration | ts.EnumDeclaration | ts.EnumMember | ts.ModuleDeclaration | ts.FunctionDeclaration
| ts.MethodDeclaration | ts.MethodSignature);
/**
* True if the given AST node has a name, and should be associated with a symbol.
*/
function isNamedNodeWithSymbol(node: ts.Node): node is NamedNodeWithSymbol {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.TypeAliasDeclaration:
case ts.SyntaxKind.EnumDeclaration:
case ts.SyntaxKind.EnumMember:
case ts.SyntaxKind.ModuleDeclaration:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.MethodDeclaration:
case ts.SyntaxKind.MethodSignature:
return true;
}
return false;
}
/**
* True if the given AST node has a type.
*/
function isTypedNode(node: ts.Node): boolean {
switch (node.kind) {
case ts.SyntaxKind.ArrayLiteralExpression:
case ts.SyntaxKind.ArrowFunction:
case ts.SyntaxKind.AsExpression:
case ts.SyntaxKind.SatisfiesExpression:
case ts.SyntaxKind.AwaitExpression:
case ts.SyntaxKind.BinaryExpression:
case ts.SyntaxKind.CallExpression:
case ts.SyntaxKind.ClassExpression:
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.CommaListExpression:
case ts.SyntaxKind.ConditionalExpression:
case ts.SyntaxKind.Constructor:
case ts.SyntaxKind.DeleteExpression:
case ts.SyntaxKind.ElementAccessExpression:
case ts.SyntaxKind.ExpressionStatement:
case ts.SyntaxKind.ExpressionWithTypeArguments:
case ts.SyntaxKind.FalseKeyword:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.FunctionExpression:
case ts.SyntaxKind.GetAccessor:
case ts.SyntaxKind.Identifier:
case ts.SyntaxKind.IndexSignature:
case ts.SyntaxKind.JsxExpression:
case ts.SyntaxKind.LiteralType:
case ts.SyntaxKind.MethodDeclaration:
case ts.SyntaxKind.MethodSignature:
case ts.SyntaxKind.NewExpression:
case ts.SyntaxKind.NonNullExpression:
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
case ts.SyntaxKind.NumericLiteral:
case ts.SyntaxKind.ObjectKeyword:
case ts.SyntaxKind.ObjectLiteralExpression:
case ts.SyntaxKind.OmittedExpression:
case ts.SyntaxKind.ParenthesizedExpression:
case ts.SyntaxKind.PartiallyEmittedExpression:
case ts.SyntaxKind.PostfixUnaryExpression:
case ts.SyntaxKind.PrefixUnaryExpression:
case ts.SyntaxKind.PropertyAccessExpression:
case ts.SyntaxKind.RegularExpressionLiteral:
case ts.SyntaxKind.SetAccessor:
case ts.SyntaxKind.StringLiteral:
case ts.SyntaxKind.TaggedTemplateExpression:
case ts.SyntaxKind.TemplateExpression:
case ts.SyntaxKind.TemplateHead:
case ts.SyntaxKind.TemplateMiddle:
case ts.SyntaxKind.TemplateSpan:
case ts.SyntaxKind.TemplateTail:
case ts.SyntaxKind.TrueKeyword:
case ts.SyntaxKind.TypeAssertionExpression:
case ts.SyntaxKind.TypeLiteral:
case ts.SyntaxKind.TypeOfExpression:
case ts.SyntaxKind.VoidExpression:
case ts.SyntaxKind.YieldExpression:
return true;
default:
return ts.isTypeNode(node);
}
}
type ContextuallyTypedNode = (ts.ArrayLiteralExpression | ts.ObjectLiteralExpression) & AugmentedNode;
function isContextuallyTypedNode(node: ts.Node): node is ContextuallyTypedNode {
let kind = node.kind;
return kind === ts.SyntaxKind.ArrayLiteralExpression || kind === ts.SyntaxKind.ObjectLiteralExpression;
}

View File

@@ -1,54 +1,26 @@
import * as ts from "./typescript";
import { TypeTable } from "./type_table";
import * as pathlib from "path";
import { VirtualSourceRoot } from "./virtual_source_root";
/**
* Extracts the package name from the prefix of an import string.
*/
const packageNameRex = /^(?:@[\w.-]+[/\\]+)?\w[\w.-]*(?=[/\\]|$)/;
const extensions = ['.ts', '.tsx', '.d.ts', '.js', '.jsx'];
function getPackageName(importString: string) {
let packageNameMatch = packageNameRex.exec(importString);
if (packageNameMatch == null) return null;
let packageName = packageNameMatch[0];
if (packageName.charAt(0) === '@') {
packageName = packageName.replace(/[/\\]+/g, '/'); // Normalize slash after the scope.
}
return packageName;
}
export class Project {
public program: ts.Program = null;
private host: ts.CompilerHost;
private resolutionCache: ts.ModuleResolutionCache;
constructor(
public tsConfig: string,
public config: ts.ParsedCommandLine,
public typeTable: TypeTable,
public packageEntryPoints: Map<string, string>,
public virtualSourceRoot: VirtualSourceRoot) {
public tsConfig: string,
public config: ts.ParsedCommandLine,
public packageEntryPoints: Map<string, string>) {
this.resolveModuleNames = this.resolveModuleNames.bind(this);
this.resolutionCache = ts.createModuleResolutionCache(pathlib.dirname(tsConfig), ts.sys.realpath, config.options);
let host = ts.createCompilerHost(config.options, true);
host.resolveModuleNames = this.resolveModuleNames;
host.trace = undefined; // Disable tracing which would otherwise go to standard out
this.host = host;
}
public unload(): void {
this.typeTable.releaseProgram();
this.program = null;
}
public load(): void {
const { config, host } = this;
this.program = ts.createProgram(config.fileNames, config.options, host);
this.typeTable.setProgram(this.program, this.virtualSourceRoot);
}
/**
@@ -60,74 +32,4 @@ export class Project {
this.unload();
this.load();
}
/**
* Override for module resolution in the TypeScript compiler host.
*/
private resolveModuleNames(
moduleNames: string[],
containingFile: string,
reusedNames: string[],
redirectedReference: ts.ResolvedProjectReference,
options: ts.CompilerOptions) {
let oppositePath =
this.virtualSourceRoot.toVirtualPath(containingFile) ||
this.virtualSourceRoot.fromVirtualPath(containingFile);
const { host, resolutionCache } = this;
return moduleNames.map((moduleName) => {
let redirected = this.redirectModuleName(moduleName, containingFile, options);
if (redirected != null) return redirected;
if (oppositePath != null) {
// If the containing file is in the virtual source root, try resolving from the real source root, and vice versa.
redirected = ts.resolveModuleName(moduleName, oppositePath, options, host, resolutionCache).resolvedModule;
if (redirected != null) return redirected;
}
return ts.resolveModuleName(moduleName, containingFile, options, host, resolutionCache).resolvedModule;
});
}
/**
* Returns the path that the given import string should be redirected to, or null if it should
* fall back to standard module resolution.
*/
private redirectModuleName(moduleName: string, containingFile: string, options: ts.CompilerOptions): ts.ResolvedModule {
// Get a package name from the leading part of the module name, e.g. '@scope/foo' from '@scope/foo/bar'.
let packageName = getPackageName(moduleName);
if (packageName == null) return null;
// Get the overridden location of this package, if one exists.
let packageEntryPoint = this.packageEntryPoints.get(packageName);
if (packageEntryPoint == null) return null;
// If the requested module name is exactly the overridden package name,
// return the entry point file (it is not necessarily called `index.ts`).
if (moduleName === packageName) {
return { resolvedFileName: packageEntryPoint, isExternalLibraryImport: true };
}
// Get the suffix after the package name, e.g. the '/bar' in '@scope/foo/bar'.
let suffix = moduleName.substring(packageName.length);
// Resolve the suffix relative to the package directory.
let packageDir = pathlib.dirname(packageEntryPoint);
let joinedPath = pathlib.join(packageDir, suffix);
// Add implicit '/index'
if (ts.sys.directoryExists(joinedPath)) {
joinedPath = pathlib.join(joinedPath, 'index');
}
// Try each recognized extension. We must not return a file whose extension is not
// recognized by TypeScript.
for (let ext of extensions) {
let candidate = joinedPath.endsWith(ext) ? joinedPath : (joinedPath + ext);
if (ts.sys.fileExists(candidate)) {
return { resolvedFileName: candidate, isExternalLibraryImport: true };
}
}
return null;
}
}

View File

@@ -31,14 +31,12 @@
"use strict";
import * as fs from "fs";
import * as pathlib from "path";
import * as readline from "readline";
import * as ts from "./typescript";
import * as ast_extractor from "./ast_extractor";
import { Project } from "./common";
import { TypeTable } from "./type_table";
import { VirtualSourceRoot } from "./virtual_source_root";
// Remove limit on stack trace depth.
@@ -55,19 +53,6 @@ interface LoadCommand {
packageEntryPoints: [string, string][];
packageJsonFiles: [string, string][];
}
interface OpenProjectCommand extends LoadCommand {
command: "open-project";
}
interface GetOwnFilesCommand extends LoadCommand {
command: "get-own-files";
}
interface CloseProjectCommand {
command: "close-project";
tsConfig: string;
}
interface GetTypeTableCommand {
command: "get-type-table";
}
interface ResetCommand {
command: "reset";
}
@@ -81,13 +66,11 @@ interface PrepareFilesCommand {
interface GetMetadataCommand {
command: "get-metadata";
}
type Command = ParseCommand | OpenProjectCommand | GetOwnFilesCommand | CloseProjectCommand
| GetTypeTableCommand | ResetCommand | QuitCommand | PrepareFilesCommand | GetMetadataCommand;
type Command = ParseCommand | ResetCommand | QuitCommand | PrepareFilesCommand | GetMetadataCommand;
/** The state to be shared between commands. */
class State {
public project: Project = null;
public typeTable = new TypeTable();
/** List of files that have been requested. */
public pendingFiles: string[] = [];
@@ -205,22 +188,18 @@ function checkCycle(root: any) {
visit(root);
if (path.length > 0) {
path.reverse();
console.log(JSON.stringify({type: "error", message: "Cycle = " + path.join(".")}));
console.log(JSON.stringify({ type: "error", message: "Cycle = " + path.join(".") }));
}
}
/** Property names to extract from the TypeScript AST. */
const astProperties: string[] = [
"$declarationKind",
"$declaredSignature",
"$end",
"$lineStarts",
"$overloadIndex",
"$pos",
"$resolvedSignature",
"$symbol",
"$tokens",
"$type",
"argument",
"argumentExpression",
"arguments",
@@ -392,20 +371,14 @@ function isExtractableSourceFile(ast: ast_extractor.AugmentedSourceFile): boolea
* an already-open project, or by parsing the file.
*/
function getAstForFile(filename: string): ts.SourceFile {
if (state.project != null) {
let ast = state.project.program.getSourceFile(filename);
if (ast != null && isExtractableSourceFile(ast)) {
ast_extractor.augmentAst(ast, ast.text, state.project);
return ast;
}
let { ast, code } = parseSingleFile(filename);
if (ast != null && isExtractableSourceFile(ast)) {
ast_extractor.augmentAst(ast, code, null);
}
// Fall back to extracting without a project.
let {ast, code} = parseSingleFile(filename);
ast_extractor.augmentAst(ast, code, null);
return ast;
}
function parseSingleFile(filename: string): {ast: ts.SourceFile, code: string} {
function parseSingleFile(filename: string): { ast: ts.SourceFile, code: string } {
let code = ts.sys.readFile(filename);
// create a compiler host that only allows access to `filename`
@@ -436,7 +409,7 @@ function parseSingleFile(filename: string): {ast: ts.SourceFile, code: string} {
let ast = program.getSourceFile(filename);
return {ast, code};
return { ast, code };
}
/**
@@ -507,7 +480,7 @@ function loadTsConfig(command: LoadCommand): LoadedConfig {
let virtualExclusions = excludes == null ? [] : [...excludes];
virtualExclusions.push('**/node_modules/**/*');
let virtualResults = ts.sys.readDirectory(virtualDir, extensions, virtualExclusions, includes, depth)
return [ ...originalResults, ...virtualResults ];
return [...originalResults, ...virtualResults];
},
fileExists: (path: string) => {
return ts.sys.fileExists(path)
@@ -531,256 +504,6 @@ function loadTsConfig(command: LoadCommand): LoadedConfig {
return { config, basePath, packageJsonFiles, packageEntryPoints, virtualSourceRoot, ownFiles };
}
/**
* Returns the list of files included in the given tsconfig.json file's include pattern,
* (not including those only references through imports).
*/
function handleGetFileListCommand(command: GetOwnFilesCommand) {
let { config, ownFiles } = loadTsConfig(command);
console.log(JSON.stringify({
type: "file-list",
ownFiles,
}));
}
function handleOpenProjectCommand(command: OpenProjectCommand) {
let { config, packageEntryPoints, virtualSourceRoot, basePath, ownFiles } = loadTsConfig(command);
let project = new Project(command.tsConfig, config, state.typeTable, packageEntryPoints, virtualSourceRoot);
project.load();
state.project = project;
let program = project.program;
let typeChecker = program.getTypeChecker();
let shouldReportDiagnostics = getEnvironmentVariable("SEMMLE_TYPESCRIPT_REPORT_DIAGNOSTICS", v => v.trim().toLowerCase() === "true", false);
let diagnostics = shouldReportDiagnostics
? program.getSemanticDiagnostics().filter(d => d.category === ts.DiagnosticCategory.Error)
: [];
if (diagnostics.length > 0) {
console.warn('TypeScript: reported ' + diagnostics.length + ' semantic errors.');
}
for (let diagnostic of diagnostics) {
let text = diagnostic.messageText;
if (text && typeof text !== 'string') {
text = text.messageText;
}
let locationStr = '';
let { file } = diagnostic;
if (file != null) {
let { line, character } = file.getLineAndCharacterOfPosition(diagnostic.start);
locationStr = `${file.fileName}:${line}:${character}`;
}
console.warn(`TypeScript: ${locationStr} ${text}`);
}
// Associate external module names with the corresponding file symbols.
// We need these mappings to identify which module a given external type comes from.
// The TypeScript API lets us resolve a module name to a source file, but there is no
// inverse mapping, nor a way to enumerate all known module names. So we discover all
// modules on the type roots (usually "node_modules/@types" but this is configurable).
let typeRoots = ts.getEffectiveTypeRoots(config.options, {
getCurrentDirectory: () => basePath,
});
for (let typeRoot of typeRoots || []) {
if (ts.sys.directoryExists(typeRoot)) {
traverseTypeRoot(typeRoot, "");
}
let virtualTypeRoot = virtualSourceRoot.toVirtualPathIfDirectoryExists(typeRoot);
if (virtualTypeRoot != null) {
traverseTypeRoot(virtualTypeRoot, "");
}
}
for (let sourceFile of program.getSourceFiles()) {
addModuleBindingsFromModuleDeclarations(sourceFile);
addModuleBindingsFromFilePath(sourceFile);
}
/** Concatenates two imports paths. These always use `/` as path separator. */
function joinModulePath(prefix: string, suffix: string) {
if (prefix.length === 0) return suffix;
if (suffix.length === 0) return prefix;
return prefix + "/" + suffix;
}
/**
* Traverses a directory that is a type root or contained in a type root, and associates
* module names (i.e. import strings) with files in this directory.
*
* `importPrefix` denotes an import string that resolves to this directory,
* or an empty string if the file itself is a type root.
*
* The `filePath` is a system file path, possibly absolute, whereas `importPrefix`
* is generally short and system-independent, typically just the name of a module.
*/
function traverseTypeRoot(filePath: string, importPrefix: string) {
for (let child of fs.readdirSync(filePath)) {
if (child[0] === ".") continue;
let childPath = pathlib.join(filePath, child);
if (fs.statSync(childPath).isDirectory()) {
traverseTypeRoot(childPath, joinModulePath(importPrefix, child));
continue;
}
let sourceFile = program.getSourceFile(childPath);
if (sourceFile == null) {
continue;
}
let importPath = getImportPathFromFileInFolder(importPrefix, child);
addModuleBindingFromImportPath(sourceFile, importPath);
}
}
function getImportPathFromFileInFolder(folder: string, baseName: string) {
let stem = getStem(baseName);
return (stem === "index")
? folder
: joinModulePath(folder, stem);
}
/**
* Emits module bindings for a module with relative path `folder/baseName`.
*/
function addModuleBindingFromImportPath(sourceFile: ts.SourceFile, importPath: string) {
let symbol = typeChecker.getSymbolAtLocation(sourceFile);
if (symbol == null) return; // Happens if the source file is not a module.
let canonicalSymbol = getEffectiveExportTarget(symbol); // Follow `export = X` declarations.
let symbolId = state.typeTable.getSymbolId(canonicalSymbol);
// Associate the module name with this symbol.
state.typeTable.addModuleMapping(symbolId, importPath);
// Associate global variable names with this module.
// For each `export as X` declaration, the global X refers to this module.
// Note: the `globalExports` map is stored on the original symbol, not the target of `export=`.
if (symbol.globalExports != null) {
symbol.globalExports.forEach((global: ts.Symbol) => {
state.typeTable.addGlobalMapping(symbolId, global.name);
});
}
}
/**
* Returns the basename of `file` without its extension, while treating `.d.ts` as a
* single extension.
*/
function getStem(file: string) {
if (file.endsWith(".d.ts")) {
return pathlib.basename(file, ".d.ts");
}
if (file.endsWith(".d.mts") || file.endsWith(".d.cts")) {
// We don't extract d.mts or d.cts files, but their symbol can coincide with that of a d.ts file,
// which means any module bindings we generate for it will ultimately be visible in QL.
let base = pathlib.basename(file);
return base.substring(0, base.length - '.d.mts'.length);
}
let base = pathlib.basename(file);
let dot = base.lastIndexOf('.');
return dot === -1 || dot === 0 ? base : base.substring(0, dot);
}
/**
* Emits module bindings for a module based on its file path.
*
* This looks for enclosing `node_modules` folders to determine the module name.
* This is needed for modules that ship their own type definitions as opposed to having
* type definitions loaded from a type root (conventionally named `@types/xxx`).
*/
function addModuleBindingsFromFilePath(sourceFile: ts.SourceFile) {
let fullPath = sourceFile.fileName;
let index = fullPath.lastIndexOf('/node_modules/');
if (index === -1) return;
let relativePath = fullPath.substring(index + '/node_modules/'.length);
// Ignore node_modules/@types folders here as they are typically handled as type roots.
if (relativePath.startsWith("@types/")) return;
// If the enclosing package has a "typings" field, only add module bindings for that file.
let packageJsonFile = getEnclosingPackageJson(fullPath);
if (packageJsonFile != null) {
let json = getPackageJson(packageJsonFile);
let typings = getPackageTypings(packageJsonFile);
if (json != null && typings != null) {
let name = json.name;
if (typings === fullPath && typeof name === 'string') {
addModuleBindingFromImportPath(sourceFile, name);
} else if (typings != null) {
return; // Typings field prevents access to other files in package.
}
}
}
// Add module bindings relative to package directory.
let { dir, base } = pathlib.parse(relativePath);
addModuleBindingFromImportPath(sourceFile, getImportPathFromFileInFolder(dir, base));
}
/**
* Emit module name bindings for external module declarations, i.e: `declare module 'X' {..}`
* These can generally occur anywhere; they may or may not be on the type root path.
*/
function addModuleBindingsFromModuleDeclarations(sourceFile: ts.SourceFile) {
for (let stmt of sourceFile.statements) {
if (ts.isModuleDeclaration(stmt) && ts.isStringLiteral(stmt.name)) {
let symbol = (stmt as any).symbol;
if (symbol == null) continue;
symbol = getEffectiveExportTarget(symbol);
let symbolId = state.typeTable.getSymbolId(symbol);
let moduleName = stmt.name.text;
state.typeTable.addModuleMapping(symbolId, moduleName);
}
}
}
/**
* If `symbol` refers to a container with an `export = X` declaration, returns
* the target of `X`, otherwise returns `symbol`.
*/
function getEffectiveExportTarget(symbol: ts.Symbol) {
if (symbol.exports != null && symbol.exports.has(ts.InternalSymbolName.ExportEquals)) {
let exportAlias = symbol.exports.get(ts.InternalSymbolName.ExportEquals);
if (exportAlias.flags & ts.SymbolFlags.Alias) {
return typeChecker.getAliasedSymbol(exportAlias);
}
}
return symbol;
}
// Unlike in the get-own-files command, this command gets all files we can possibly
// extract type information for, including files referenced outside the tsconfig's inclusion pattern.
let allFiles = program.getSourceFiles().map(sf => pathlib.resolve(sf.fileName));
console.log(JSON.stringify({
type: "project-opened",
ownFiles,
allFiles,
}));
}
function handleCloseProjectCommand(command: CloseProjectCommand) {
if (state.project == null) {
console.log(JSON.stringify({
type: "error",
message: "No project is open",
}));
return;
}
state.project.unload();
state.project = null;
console.log(JSON.stringify({type: "project-closed"}));
}
function handleGetTypeTableCommand(command: GetTypeTableCommand) {
console.log(JSON.stringify({
type: "type-table",
typeTable: state.typeTable.getTypeTableJson(),
}));
}
function handleResetCommand(command: ResetCommand) {
reset();
console.log(JSON.stringify({
@@ -807,8 +530,6 @@ function handleGetMetadataCommand(command: GetMetadataCommand) {
function reset() {
state = new State();
state.typeTable.restrictedExpansion = getEnvironmentVariable("SEMMLE_TYPESCRIPT_NO_EXPANSION", v => v.trim().toLowerCase() === "true", true);
state.typeTable.skipExtractingTypes = getEnvironmentVariable("CODEQL_EXTRACTOR_JAVASCRIPT_OPTION_SKIP_TYPES", v => v.trim().toLowerCase() === "true", false);
}
function getEnvironmentVariable<T>(name: string, parse: (x: string) => T, defaultValue: T) {
@@ -848,35 +569,23 @@ function runReadLineInterface() {
rl.on("line", (line: string) => {
let req: Command = JSON.parse(line);
switch (req.command) {
case "parse":
handleParseCommand(req);
break;
case "open-project":
handleOpenProjectCommand(req);
break;
case "get-own-files":
handleGetFileListCommand(req);
break;
case "close-project":
handleCloseProjectCommand(req);
break;
case "get-type-table":
handleGetTypeTableCommand(req);
break;
case "prepare-files":
handlePrepareFilesCommand(req);
break;
case "reset":
handleResetCommand(req);
break;
case "get-metadata":
handleGetMetadataCommand(req);
break;
case "quit":
rl.close();
break;
default:
throw new Error("Unknown command " + (req as any).command + ".");
case "parse":
handleParseCommand(req);
break;
case "prepare-files":
handlePrepareFilesCommand(req);
break;
case "reset":
handleResetCommand(req);
break;
case "get-metadata":
handleGetMetadataCommand(req);
break;
case "quit":
rl.close();
break;
default:
throw new Error("Unknown command " + (req as any).command + ".");
}
});
}
@@ -886,23 +595,6 @@ if (process.argv.length > 2) {
let argument = process.argv[2];
if (argument === "--version") {
console.log("parser-wrapper with TypeScript " + ts.version);
} else if (pathlib.basename(argument) === "tsconfig.json") {
reset();
handleOpenProjectCommand({
command: "open-project",
tsConfig: argument,
packageEntryPoints: [],
packageJsonFiles: [],
sourceRoot: null,
virtualSourceRoot: null,
});
for (let sf of state.project.program.getSourceFiles()) {
if (/lib\..*\.d\.ts/.test(pathlib.basename(sf.fileName)) || pathlib.basename(sf.fileName) === "lib.d.ts") continue;
handleParseCommand({
command: "parse",
filename: sf.fileName,
}, false);
}
} else if (pathlib.extname(argument) === ".ts" || pathlib.extname(argument) === ".tsx") {
handleParseCommand({
command: "parse",

File diff suppressed because it is too large Load Diff

View File

@@ -51,10 +51,8 @@ import com.semmle.js.extractor.trapcache.DummyTrapCache;
import com.semmle.js.extractor.trapcache.ITrapCache;
import com.semmle.js.parser.ParseError;
import com.semmle.js.parser.ParsedProject;
import com.semmle.ts.extractor.TypeExtractor;
import com.semmle.ts.extractor.TypeScriptParser;
import com.semmle.ts.extractor.TypeScriptWrapperOOMError;
import com.semmle.ts.extractor.TypeTable;
import com.semmle.util.data.StringUtil;
import com.semmle.util.diagnostic.DiagnosticLevel;
import com.semmle.util.diagnostic.DiagnosticLocation;
@@ -220,7 +218,6 @@ public class AutoBuild {
private final Set<String> xmlExtensions = new LinkedHashSet<>();
private ProjectLayout filters;
private final Path LGTM_SRC, SEMMLE_DIST;
private final TypeScriptMode typeScriptMode;
private final String defaultEncoding;
private ExecutorService threadPool;
private volatile boolean seenCode = false;
@@ -238,8 +235,6 @@ public class AutoBuild {
this.SEMMLE_DIST = Paths.get(EnvironmentVariables.getExtractorRoot());
this.outputConfig = new ExtractorOutputConfig(LegacyLanguage.JAVASCRIPT);
this.trapCache = ITrapCache.fromExtractorOptions();
this.typeScriptMode =
getEnumFromEnvVar("LGTM_INDEX_TYPESCRIPT", TypeScriptMode.class, TypeScriptMode.FULL);
this.defaultEncoding = getEnvVar("LGTM_INDEX_DEFAULT_ENCODING");
this.installDependencies = Boolean.valueOf(getEnvVar("LGTM_INDEX_TYPESCRIPT_INSTALL_DEPS"));
this.virtualSourceRoot = makeVirtualSourceRoot();
@@ -395,7 +390,7 @@ public class AutoBuild {
defaultExtract.add(FileType.HTML);
defaultExtract.add(FileType.JS);
defaultExtract.add(FileType.YAML);
if (typeScriptMode != TypeScriptMode.NONE) defaultExtract.add(FileType.TYPESCRIPT);
defaultExtract.add(FileType.TYPESCRIPT);
for (FileType filetype : defaultExtract)
for (String extension : filetype.getExtensions()) patterns.add("**/*" + extension);
@@ -785,7 +780,7 @@ public class AutoBuild {
extractTypeScript(filesToExtract, extractedFiles,
extractors, tsconfigFiles, dependencyInstallationResult);
boolean hasTypeScriptFiles = extractedFiles.size() > 0;
boolean hasTypeScriptFiles = hasTypeScriptFiles(filesToExtract);
// extract remaining files
return extractFiles(
@@ -1044,7 +1039,6 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
private ExtractorConfig mkExtractorConfig() {
ExtractorConfig config = new ExtractorConfig(true);
config = config.withSourceType(getSourceType());
config = config.withTypeScriptMode(typeScriptMode);
config = config.withVirtualSourceRoot(virtualSourceRoot);
if (defaultEncoding != null) config = config.withDefaultEncoding(defaultEncoding);
return config;
@@ -1065,75 +1059,26 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
FileExtractors extractors,
List<Path> tsconfig,
DependencyInstallationResult deps) {
if (hasTypeScriptFiles(files) || !tsconfig.isEmpty()) {
List<Path> typeScriptFiles = new ArrayList<>();
// Get all TypeScript files.
for (Path f : files) {
if (extractors.fileType(f) == FileType.TYPESCRIPT) {
typeScriptFiles.add(f);
}
}
// Also get TypeScript files from HTML file snippets.
for (Map.Entry<Path, FileSnippet> entry : state.getSnippets().entrySet()) {
if (!extractedFiles.contains(entry.getKey())
&& FileType.forFileExtension(entry.getKey().toFile()) == FileType.TYPESCRIPT) {
typeScriptFiles.add(entry.getKey());
}
}
if (!typeScriptFiles.isEmpty()) {
TypeScriptParser tsParser = state.getTypeScriptParser();
verifyTypeScriptInstallation(state);
// Collect all files included in a tsconfig.json inclusion pattern.
// If a given file is referenced by multiple tsconfig files, we prefer to extract it using
// one that includes it rather than just references it.
Set<File> explicitlyIncludedFiles = new LinkedHashSet<>();
if (tsconfig.size() > 1) { // No prioritization needed if there's only one tsconfig.
for (Path projectPath : tsconfig) {
explicitlyIncludedFiles.addAll(tsParser.getOwnFiles(projectPath.toFile(), deps, virtualSourceRoot));
}
}
// Extract TypeScript projects
for (Path projectPath : tsconfig) {
File projectFile = projectPath.toFile();
long start = logBeginProcess("Opening project " + projectFile);
ParsedProject project = tsParser.openProject(projectFile, deps, virtualSourceRoot);
logEndProcess(start, "Done opening project " + projectFile);
// Extract all files belonging to this project which are also matched
// by our include/exclude filters.
List<Path> typeScriptFiles = new ArrayList<Path>();
for (File sourceFile : project.getAllFiles()) {
Path sourcePath = sourceFile.toPath();
Path normalizedFile = normalizePath(sourcePath);
if (!files.contains(normalizedFile) && !state.getSnippets().containsKey(normalizedFile)) {
continue;
}
if (!project.getOwnFiles().contains(sourceFile) && explicitlyIncludedFiles.contains(sourceFile)) continue;
if (extractors.fileType(sourcePath) != FileType.TYPESCRIPT) {
// For the time being, skip non-TypeScript files, even if the TypeScript
// compiler can parse them for us.
continue;
}
if (extractedFiles.contains(sourcePath)) {
continue;
}
typeScriptFiles.add(sourcePath);
}
typeScriptFiles.sort(PATH_ORDERING);
extractTypeScriptFiles(typeScriptFiles, extractedFiles, extractors);
tsParser.closeProject(projectFile);
}
// Extract all the types discovered when extracting the ASTs.
if (!tsconfig.isEmpty()) {
TypeTable typeTable = tsParser.getTypeTable();
extractTypeTable(tsconfig.iterator().next(), typeTable);
}
// Extract remaining TypeScript files.
List<Path> remainingTypeScriptFiles = new ArrayList<>();
for (Path f : files) {
if (!extractedFiles.contains(f)
&& extractors.fileType(f) == FileType.TYPESCRIPT) {
remainingTypeScriptFiles.add(f);
}
}
for (Map.Entry<Path, FileSnippet> entry : state.getSnippets().entrySet()) {
if (!extractedFiles.contains(entry.getKey())
&& FileType.forFileExtension(entry.getKey().toFile()) == FileType.TYPESCRIPT) {
remainingTypeScriptFiles.add(entry.getKey());
}
}
if (!remainingTypeScriptFiles.isEmpty()) {
extractTypeScriptFiles(remainingTypeScriptFiles, extractedFiles, extractors);
}
extractTypeScriptFiles(typeScriptFiles, extractedFiles, extractors);
// The TypeScript compiler instance is no longer needed.
tsParser.killProcess();
}
@@ -1186,8 +1131,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
}
// extract TypeScript projects from 'tsconfig.json'
if (typeScriptMode != TypeScriptMode.NONE
&& treatAsTSConfig(file.getFileName().toString())
if (treatAsTSConfig(file.getFileName().toString())
&& !excludes.contains(file)
&& isFileIncluded(file)) {
tsconfigFiles.add(file);
@@ -1246,18 +1190,6 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
return path.toAbsolutePath().normalize();
}
private void extractTypeTable(Path fileHandle, TypeTable table) {
TrapWriter trapWriter =
outputConfig
.getTrapWriterFactory()
.mkTrapWriter(new File(fileHandle.toString() + ".codeql-typescript-typetable"));
try {
new TypeExtractor(trapWriter, table).extract();
} finally {
FileUtil.close(trapWriter);
}
}
/**
* Get the source type specified in <code>LGTM_INDEX_SOURCE_TYPE</code>, or the default of {@link
* SourceType#AUTO}.

View File

@@ -221,9 +221,6 @@ public class ExtractorConfig {
/** Should textual information be extracted into the lines/4 relation? */
private boolean extractLines;
/** Should TypeScript files be extracted? */
private TypeScriptMode typescriptMode;
/** Override amount of RAM to allocate to the TypeScript compiler. */
private int typescriptRam;
@@ -245,7 +242,6 @@ public class ExtractorConfig {
this.esnext = true;
this.v8Extensions = true;
}
this.typescriptMode = TypeScriptMode.NONE;
this.e4x = experimental;
this.defaultEncoding = StandardCharsets.UTF_8.name();
this.virtualSourceRoot = VirtualSourceRoot.none;
@@ -266,7 +262,6 @@ public class ExtractorConfig {
this.sourceType = that.sourceType;
this.htmlHandling = that.htmlHandling;
this.extractLines = that.extractLines;
this.typescriptMode = that.typescriptMode;
this.typescriptRam = that.typescriptRam;
this.defaultEncoding = that.defaultEncoding;
this.virtualSourceRoot = that.virtualSourceRoot;
@@ -416,20 +411,10 @@ public class ExtractorConfig {
return res;
}
public TypeScriptMode getTypeScriptMode() {
return typescriptMode;
}
public int getTypeScriptRam() {
return typescriptRam;
}
public ExtractorConfig withTypeScriptMode(TypeScriptMode typescriptMode) {
ExtractorConfig res = new ExtractorConfig(this);
res.typescriptMode = typescriptMode;
return res;
}
public ExtractorConfig withTypeScriptRam(int ram) {
ExtractorConfig res = new ExtractorConfig(this);
res.typescriptRam = ram;
@@ -490,8 +475,6 @@ public class ExtractorConfig {
+ sourceType
+ ", extractLines="
+ extractLines
+ ", typescriptMode="
+ typescriptMode
+ ", defaultEncoding="
+ defaultEncoding
+ ", virtualSourceRoot="

View File

@@ -217,8 +217,6 @@ public class FileExtractor {
TYPESCRIPT(".ts", ".tsx", ".mts", ".cts") {
@Override
protected boolean contains(File f, String lcExt, ExtractorConfig config) {
if (config.getTypeScriptMode() == TypeScriptMode.NONE) return false;
// Read the beginning of the file to guess the file type.
if (hasBadFileHeader(f, lcExt, config)) {
return false;

View File

@@ -18,9 +18,7 @@ import com.semmle.js.extractor.ExtractorConfig.SourceType;
import com.semmle.js.extractor.FileExtractor.FileType;
import com.semmle.js.extractor.trapcache.ITrapCache;
import com.semmle.js.parser.ParsedProject;
import com.semmle.ts.extractor.TypeExtractor;
import com.semmle.ts.extractor.TypeScriptParser;
import com.semmle.ts.extractor.TypeTable;
import com.semmle.util.data.StringUtil;
import com.semmle.util.data.UnitParser;
import com.semmle.util.exception.ResourceError;
@@ -79,7 +77,6 @@ public class Main {
private PathMatcher includeMatcher, excludeMatcher;
private FileExtractor fileExtractor;
private ExtractorState extractorState;
private Set<File> projectFiles = new LinkedHashSet<>();
private Set<File> files = new LinkedHashSet<>();
private final Set<File> extractedFiles = new LinkedHashSet<>();
@@ -121,10 +118,6 @@ public class Main {
}
// Sort files for determinism
projectFiles = projectFiles.stream()
.sorted(AutoBuild.FILE_ORDERING)
.collect(Collectors.toCollection(() -> new LinkedHashSet<>()));
files = files.stream()
.sorted(AutoBuild.FILE_ORDERING)
.collect(Collectors.toCollection(() -> new LinkedHashSet<>()));
@@ -142,53 +135,22 @@ public class Main {
tsParser.verifyInstallation(!ap.has(P_QUIET));
}
for (File projectFile : projectFiles) {
long start = verboseLogStartTimer(ap, "Opening project " + projectFile);
ParsedProject project = tsParser.openProject(projectFile, DependencyInstallationResult.empty, extractorConfig.getVirtualSourceRoot());
verboseLogEndTimer(ap, start);
// Extract all files belonging to this project which are also matched
// by our include/exclude filters.
List<File> filesToExtract = new ArrayList<>();
for (File sourceFile : project.getOwnFiles()) {
File normalizedFile = normalizeFile(sourceFile);
if ((files.contains(normalizedFile) || extractorState.getSnippets().containsKey(normalizedFile.toPath()))
&& !extractedFiles.contains(sourceFile.getAbsoluteFile())
&& FileType.TYPESCRIPT.getExtensions().contains(FileUtil.extension(sourceFile))) {
filesToExtract.add(sourceFile);
}
}
tsParser.prepareFiles(filesToExtract);
for (int i = 0; i < filesToExtract.size(); ++i) {
ensureFileIsExtracted(filesToExtract.get(i), ap);
}
// Close the project to free memory. This does not need to be in a `finally` as
// the project is not a system resource.
tsParser.closeProject(projectFile);
}
if (!projectFiles.isEmpty()) {
// Extract all the types discovered when extracting the ASTs.
TypeTable typeTable = tsParser.getTypeTable();
extractTypeTable(projectFiles.iterator().next(), typeTable);
}
List<File> remainingTypescriptFiles = new ArrayList<>();
List<File> typeScriptFiles = new ArrayList<>();
for (File f : files) {
if (!extractedFiles.contains(f.getAbsoluteFile())
&& FileType.forFileExtension(f) == FileType.TYPESCRIPT) {
remainingTypescriptFiles.add(f);
typeScriptFiles.add(f);
}
}
for (Map.Entry<Path, FileSnippet> entry : extractorState.getSnippets().entrySet()) {
if (!extractedFiles.contains(entry.getKey().toFile())
&& FileType.forFileExtension(entry.getKey().toFile()) == FileType.TYPESCRIPT) {
remainingTypescriptFiles.add(entry.getKey().toFile());
typeScriptFiles.add(entry.getKey().toFile());
}
}
if (!remainingTypescriptFiles.isEmpty()) {
tsParser.prepareFiles(remainingTypescriptFiles);
for (File f : remainingTypescriptFiles) {
if (!typeScriptFiles.isEmpty()) {
tsParser.prepareFiles(typeScriptFiles);
for (File f : typeScriptFiles) {
ensureFileIsExtracted(f, ap);
}
}
@@ -225,21 +187,6 @@ public class Main {
return false;
}
private void extractTypeTable(File fileHandle, TypeTable table) {
TrapWriter trapWriter =
extractorOutputConfig
.getTrapWriterFactory()
.mkTrapWriter(
new File(
fileHandle.getParentFile(),
fileHandle.getName() + ".codeql-typescript-typetable"));
try {
new TypeExtractor(trapWriter, table).extract();
} finally {
FileUtil.close(trapWriter);
}
}
private void ensureFileIsExtracted(File f, ArgsParser ap) {
if (!extractedFiles.add(f.getAbsoluteFile())) {
// The file has already been extracted as part of a project.
@@ -304,11 +251,8 @@ public class Main {
includes.add("**/.babelrc*.json");
// extract TypeScript if `--typescript` or `--typescript-full` was specified
if (getTypeScriptMode(ap) != TypeScriptMode.NONE) {
addIncludesFor(includes, FileType.TYPESCRIPT);
includes.add("**/*tsconfig*.json");
}
addIncludesFor(includes, FileType.TYPESCRIPT);
includes.add("**/*tsconfig*.json");
// add explicit include patterns
for (String pattern : ap.getZeroOrMore(P_INCLUDE))
@@ -442,12 +386,6 @@ public class Main {
return ap.has(P_EXPERIMENTAL) || ap.has(P_JSCRIPT) || ap.has(P_MOZ_EXTENSIONS);
}
private static TypeScriptMode getTypeScriptMode(ArgsParser ap) {
if (ap.has(P_TYPESCRIPT_FULL)) return TypeScriptMode.FULL;
if (ap.has(P_TYPESCRIPT)) return TypeScriptMode.BASIC;
return TypeScriptMode.NONE;
}
private Path inferSourceRoot(ArgsParser ap) {
List<File> files = getFilesArg(ap);
Path sourceRoot = files.iterator().next().toPath().toAbsolutePath().getParent();
@@ -478,7 +416,6 @@ public class Main {
.withFileType(getFileType(ap))
.withSourceType(ap.getEnum(P_SOURCE_TYPE, SourceType.class, SourceType.AUTO))
.withExtractLines(ap.has(P_EXTRACT_PROGRAM_TEXT))
.withTypeScriptMode(getTypeScriptMode(ap))
.withTypeScriptRam(
ap.has(P_TYPESCRIPT_RAM)
? UnitParser.parseOpt(ap.getString(P_TYPESCRIPT_RAM), UnitParser.MEGABYTES)
@@ -542,12 +479,6 @@ public class Main {
&& (explicit || includeMatcher.matches(path) && !excludeMatcher.matches(path))) {
files.add(normalizeFile(root));
}
if (extractorConfig.getTypeScriptMode() == TypeScriptMode.FULL
&& AutoBuild.treatAsTSConfig(root.getName())
&& !excludeMatcher.matches(path)) {
projectFiles.add(root);
}
}
}

View File

@@ -1,18 +0,0 @@
package com.semmle.js.extractor;
/** The amount of information to extract from TypeScript files. */
public enum TypeScriptMode {
/** TypeScript files will not be extracted. */
NONE,
/**
* Only syntactic information will be extracted from TypeScript files.
*
* <p>This requires Node.js and the TypeScript compiler to be installed if the project contains
* any TypeScript files.
*/
BASIC,
/** Extract as much as possible from TypeScript files. */
FULL;
}

View File

@@ -1,330 +0,0 @@
package com.semmle.ts.extractor;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.semmle.util.trap.TrapWriter;
import com.semmle.util.trap.TrapWriter.Label;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Extracts type and symbol information into TRAP files.
*
* <p>This is closely coupled with the <code>type_table.ts</code> file in the parser-wrapper. Type
* strings and symbol strings generated in that file are parsed here. See that file for reference
* and documentation.
*/
public class TypeExtractor {
private final TrapWriter trapWriter;
private final TypeTable table;
private static final Map<String, Integer> tagToKind = new LinkedHashMap<String, Integer>();
private static final int referenceKind = 6;
private static final int objectKind = 7;
private static final int typevarKind = 8;
private static final int typeofKind = 9;
private static final int uniqueSymbolKind = 15;
private static final int tupleKind = 18;
private static final int lexicalTypevarKind = 19;
private static final int thisKind = 20;
private static final int numberLiteralTypeKind = 21;
private static final int stringLiteralTypeKind = 22;
private static final int bigintLiteralTypeKind = 25;
static {
tagToKind.put("any", 0);
tagToKind.put("string", 1);
tagToKind.put("number", 2);
tagToKind.put("union", 3);
tagToKind.put("true", 4);
tagToKind.put("false", 5);
tagToKind.put("reference", referenceKind);
tagToKind.put("object", objectKind);
tagToKind.put("typevar", typevarKind);
tagToKind.put("typeof", typeofKind);
tagToKind.put("void", 10);
tagToKind.put("undefined", 11);
tagToKind.put("null", 12);
tagToKind.put("never", 13);
tagToKind.put("plainsymbol", 14);
tagToKind.put("uniquesymbol", uniqueSymbolKind);
tagToKind.put("objectkeyword", 16);
tagToKind.put("intersection", 17);
tagToKind.put("tuple", tupleKind);
tagToKind.put("lextypevar", lexicalTypevarKind);
tagToKind.put("this", thisKind);
tagToKind.put("numlit", numberLiteralTypeKind);
tagToKind.put("strlit", stringLiteralTypeKind);
tagToKind.put("unknown", 23);
tagToKind.put("bigint", 24);
tagToKind.put("bigintlit", bigintLiteralTypeKind);
}
private static final Map<String, Integer> symbolKind = new LinkedHashMap<String, Integer>();
static {
symbolKind.put("root", 0);
symbolKind.put("member", 1);
symbolKind.put("other", 2);
}
public TypeExtractor(TrapWriter trapWriter, TypeTable table) {
this.trapWriter = trapWriter;
this.table = table;
}
public void extract() {
for (int i = 0; i < table.getNumberOfTypes(); ++i) {
extractType(i);
}
extractPropertyLookups(table.getPropertyLookups());
extractTypeAliases(table.getTypeAliases());
for (int i = 0; i < table.getNumberOfSymbols(); ++i) {
extractSymbol(i);
}
extractSymbolNameMapping("symbol_module", table.getModuleMappings());
extractSymbolNameMapping("symbol_global", table.getGlobalMappings());
extractSignatureMappings(table.getSignatureMappings());
for (int i = 0; i < table.getNumberOfSignatures(); ++i) {
extractSignature(i);
}
extractIndexTypeTable(table.getNumberIndexTypes(), "number_index_type");
extractIndexTypeTable(table.getStringIndexTypes(), "string_index_type");
extractBaseTypes(table.getBaseTypes());
extractSelfTypes(table.getSelfTypes());
}
private void extractType(int id) {
Label lbl = trapWriter.globalID("type;" + id);
String contents = table.getTypeString(id);
String[] parts = split(contents);
int kind = tagToKind.get(parts[0]);
trapWriter.addTuple("types", lbl, kind, table.getTypeToStringValue(id));
int firstChild = 1;
switch (kind) {
case referenceKind:
case typevarKind:
case typeofKind:
case uniqueSymbolKind:
{
// The first part of a reference is the symbol for name binding.
Label symbol = trapWriter.globalID("symbol;" + parts[1]);
trapWriter.addTuple("type_symbol", lbl, symbol);
++firstChild;
break;
}
case tupleKind:
{
// The first two parts denote minimum length and index of rest element (or -1 if no rest element).
trapWriter.addTuple("tuple_type_min_length", lbl, Integer.parseInt(parts[1]));
int restIndex = Integer.parseInt(parts[2]);
if (restIndex != -1) {
trapWriter.addTuple("tuple_type_rest_index", lbl, restIndex);
}
firstChild += 2;
break;
}
case objectKind:
case lexicalTypevarKind:
firstChild = parts.length; // No children.
break;
case numberLiteralTypeKind:
case stringLiteralTypeKind:
case bigintLiteralTypeKind:
firstChild = parts.length; // No children.
// The string value may contain `;` so don't use the split().
String value = contents.substring(parts[0].length() + 1);
trapWriter.addTuple("type_literal_value", lbl, value);
break;
}
for (int i = firstChild; i < parts.length; ++i) {
Label childLabel = trapWriter.globalID("type;" + parts[i]);
trapWriter.addTuple("type_child", childLabel, lbl, i - firstChild);
}
}
private void extractPropertyLookups(JsonObject lookups) {
JsonArray baseTypes = lookups.get("baseTypes").getAsJsonArray();
JsonArray names = lookups.get("names").getAsJsonArray();
JsonArray propertyTypes = lookups.get("propertyTypes").getAsJsonArray();
for (int i = 0; i < baseTypes.size(); ++i) {
int baseType = baseTypes.get(i).getAsInt();
String name = names.get(i).getAsString();
int propertyType = propertyTypes.get(i).getAsInt();
trapWriter.addTuple(
"type_property",
trapWriter.globalID("type;" + baseType),
name,
trapWriter.globalID("type;" + propertyType));
}
}
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);
int kind = symbolKind.get(parts[0]);
String name = parts[3];
Label label = trapWriter.globalID("symbol;" + index);
trapWriter.addTuple("symbols", label, kind, name);
String parentStr = parts[2];
if (parentStr.length() > 0) {
Label parentLabel = trapWriter.globalID("symbol;" + parentStr);
trapWriter.addTuple("symbol_parent", label, parentLabel);
}
}
private void extractSymbolNameMapping(String relationName, JsonObject mappings) {
JsonArray symbols = mappings.get("symbols").getAsJsonArray();
JsonArray names = mappings.get("names").getAsJsonArray();
for (int i = 0; i < symbols.size(); ++i) {
Label symbol = trapWriter.globalID("symbol;" + symbols.get(i).getAsInt());
String moduleName = names.get(i).getAsString();
trapWriter.addTuple(relationName, symbol, moduleName);
}
}
private void extractSignature(int index) {
// Format is:
// kind;isAbstract;numTypeParams;requiredParams;restParamType;returnType(;paramName;paramType)*
String[] parts = split(table.getSignatureString(index));
Label label = trapWriter.globalID("signature;" + index);
int kind = Integer.parseInt(parts[0]);
boolean isAbstract = parts[1].equals("t");
if (isAbstract) {
trapWriter.addTuple("is_abstract_signature", label);
}
int numberOfTypeParameters = Integer.parseInt(parts[2]);
int requiredParameters = Integer.parseInt(parts[3]);
String restParamTypeTag = parts[4];
if (!restParamTypeTag.isEmpty()) {
trapWriter.addTuple(
"signature_rest_parameter", label, trapWriter.globalID("type;" + restParamTypeTag));
}
Label returnType = trapWriter.globalID("type;" + parts[5]);
trapWriter.addTuple(
"signature_types",
label,
kind,
table.getSignatureToStringValue(index),
numberOfTypeParameters,
requiredParameters);
trapWriter.addTuple("signature_contains_type", returnType, label, -1);
int numberOfParameters = (parts.length - 6) / 2; // includes type parameters
for (int i = 0; i < numberOfParameters; ++i) {
int partIndex = 6 + (2 * i);
String paramName = parts[partIndex];
String paramTypeId = parts[partIndex + 1];
if (paramTypeId.length() > 0) { // Unconstrained type parameters have an empty type ID.
Label paramType = trapWriter.globalID("type;" + parts[partIndex + 1]);
trapWriter.addTuple("signature_contains_type", paramType, label, i);
}
trapWriter.addTuple("signature_parameter_name", label, i, paramName);
}
}
private void extractSignatureMappings(JsonObject mappings) {
JsonArray baseTypes = mappings.get("baseTypes").getAsJsonArray();
JsonArray kinds = mappings.get("kinds").getAsJsonArray();
JsonArray indices = mappings.get("indices").getAsJsonArray();
JsonArray signatures = mappings.get("signatures").getAsJsonArray();
for (int i = 0; i < baseTypes.size(); ++i) {
int baseType = baseTypes.get(i).getAsInt();
int kind = kinds.get(i).getAsInt();
int index = indices.get(i).getAsInt();
int signatureId = signatures.get(i).getAsInt();
trapWriter.addTuple(
"type_contains_signature",
trapWriter.globalID("type;" + baseType),
kind,
index,
trapWriter.globalID("signature;" + signatureId));
}
}
private void extractIndexTypeTable(JsonObject table, String relationName) {
JsonArray baseTypes = table.get("baseTypes").getAsJsonArray();
JsonArray propertyTypes = table.get("propertyTypes").getAsJsonArray();
for (int i = 0; i < baseTypes.size(); ++i) {
int baseType = baseTypes.get(i).getAsInt();
int propertyType = propertyTypes.get(i).getAsInt();
trapWriter.addTuple(
relationName,
trapWriter.globalID("type;" + baseType),
trapWriter.globalID("type;" + propertyType));
}
}
private void extractBaseTypes(JsonObject table) {
JsonArray symbols = table.get("symbols").getAsJsonArray();
JsonArray baseTypeSymbols = table.get("baseTypeSymbols").getAsJsonArray();
for (int i = 0; i < symbols.size(); ++i) {
int symbolId = symbols.get(i).getAsInt();
int baseTypeSymbolId = baseTypeSymbols.get(i).getAsInt();
trapWriter.addTuple(
"base_type_names",
trapWriter.globalID("symbol;" + symbolId),
trapWriter.globalID("symbol;" + baseTypeSymbolId));
}
}
private void extractSelfTypes(JsonObject table) {
JsonArray symbols = table.get("symbols").getAsJsonArray();
JsonArray selfTypes = table.get("selfTypes").getAsJsonArray();
for (int i = 0; i < symbols.size(); ++i) {
int symbolId = symbols.get(i).getAsInt();
int typeId = selfTypes.get(i).getAsInt();
trapWriter.addTuple(
"self_types",
trapWriter.globalID("symbol;" + symbolId),
trapWriter.globalID("type;" + typeId));
}
}
/** Like {@link #split(String)} without a limit. */
private static String[] split(String input) {
return split(input, -1);
}
/**
* Splits the input around the semicolon (<code>;</code>) character, preserving all empty
* substrings.
*
* <p>At most <code>limit</code> substrings will be extracted. If the limit is reached, the last
* substring will extend to the end of the string, possibly itself containing semicolons.
*
* <p>Note that the {@link String#split(String)} method does not preserve empty substrings at the
* end of the string in case the string ends with a semicolon.
*/
private static String[] split(String input, int limit) {
List<String> result = new ArrayList<String>();
int lastPos = 0;
for (int i = 0; i < input.length(); ++i) {
if (input.charAt(i) == ';') {
result.add(input.substring(lastPos, i));
lastPos = i + 1;
if (result.size() == limit - 1) break;
}
}
result.add(input.substring(lastPos));
return result.toArray(EMPTY_STRING_ARRAY);
}
private static final String[] EMPTY_STRING_ARRAY = new String[0];
}

View File

@@ -463,113 +463,6 @@ public class TypeScriptParser {
checkResponseType(response, "ok");
}
/**
* Converts a map to an array of [key, value] pairs.
*/
private JsonArray mapToArray(Map<String, Path> map) {
JsonArray result = new JsonArray();
map.forEach(
(key, path) -> {
JsonArray entry = new JsonArray();
entry.add(key);
entry.add(path.toString());
result.add(entry);
});
return result;
}
private static Set<File> getFilesFromJsonArray(JsonArray array) {
Set<File> files = new LinkedHashSet<>();
for (JsonElement elm : array) {
files.add(new File(elm.getAsString()));
}
return files;
}
/**
* Returns the set of files included by the inclusion pattern in the given tsconfig.json file.
*/
public Set<File> getOwnFiles(File tsConfigFile, DependencyInstallationResult deps, VirtualSourceRoot vroot) {
JsonObject request = makeLoadCommand("get-own-files", tsConfigFile, deps, vroot);
JsonObject response = talkToParserWrapper(request);
try {
checkResponseType(response, "file-list");
return getFilesFromJsonArray(response.get("ownFiles").getAsJsonArray());
} catch (IllegalStateException e) {
throw new CatastrophicError(
"TypeScript parser wrapper sent unexpected response: " + response, e);
}
}
/**
* Opens a new project based on a tsconfig.json file. The compiler will analyze all files in the
* project.
*
* <p>Call {@link #parse} to access individual files in the project.
*
* <p>Only one project should be opened at once.
*/
public ParsedProject openProject(File tsConfigFile, DependencyInstallationResult deps, VirtualSourceRoot vroot) {
JsonObject request = makeLoadCommand("open-project", tsConfigFile, deps, vroot);
JsonObject response = talkToParserWrapper(request);
try {
checkResponseType(response, "project-opened");
ParsedProject project = new ParsedProject(tsConfigFile,
getFilesFromJsonArray(response.get("ownFiles").getAsJsonArray()),
getFilesFromJsonArray(response.get("allFiles").getAsJsonArray()));
return project;
} catch (IllegalStateException e) {
throw new CatastrophicError(
"TypeScript parser wrapper sent unexpected response: " + response, e);
}
}
private JsonObject makeLoadCommand(String command, File tsConfigFile, DependencyInstallationResult deps, VirtualSourceRoot vroot) {
JsonObject request = new JsonObject();
request.add("command", new JsonPrimitive(command));
request.add("tsConfig", new JsonPrimitive(tsConfigFile.getPath()));
request.add("packageEntryPoints", mapToArray(deps.getPackageEntryPoints()));
request.add("packageJsonFiles", mapToArray(deps.getPackageJsonFiles()));
request.add("sourceRoot", vroot.getSourceRoot() == null
? JsonNull.INSTANCE
: new JsonPrimitive(vroot.getSourceRoot().toString()));
request.add("virtualSourceRoot", vroot.getVirtualSourceRoot() == null
? JsonNull.INSTANCE
: new JsonPrimitive(vroot.getVirtualSourceRoot().toString()));
return request;
}
/**
* Closes a project previously opened.
*
* <p>This main purpose is to free heap space in the Node.js process.
*/
public void closeProject(File tsConfigFile) {
JsonObject request = new JsonObject();
request.add("command", new JsonPrimitive("close-project"));
request.add("tsConfig", new JsonPrimitive(tsConfigFile.getPath()));
JsonObject response = talkToParserWrapper(request);
try {
checkResponseType(response, "project-closed");
} catch (IllegalStateException e) {
throw new CatastrophicError(
"TypeScript parser wrapper sent unexpected response: " + response, e);
}
}
public TypeTable getTypeTable() {
JsonObject request = new JsonObject();
request.add("command", new JsonPrimitive("get-type-table"));
JsonObject response = talkToParserWrapper(request);
try {
checkResponseType(response, "type-table");
return new TypeTable(response.get("typeTable").getAsJsonObject());
} catch (IllegalStateException e) {
throw new CatastrophicError(
"TypeScript parser wrapper sent unexpected response: " + response, e);
}
}
/**
* Closes any open project, and in general, brings the TypeScript wrapper to a fresh state as if
* it had just been restarted.

View File

@@ -1,119 +0,0 @@
package com.semmle.ts.extractor;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* Holds the output of the <code>get-type-table</code> command.
*
* <p>See documentation in <code>parser-wrapper/src/type_table.ts</code>.
*/
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;
private final JsonArray signatureStrings;
private final JsonObject signatureMappings;
private final JsonArray signatureToStringValues;
private final JsonObject stringIndexTypes;
private final JsonObject numberIndexTypes;
private final JsonObject baseTypes;
private final JsonObject selfTypes;
public TypeTable(JsonObject 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();
this.signatureStrings = typeTable.get("signatureStrings").getAsJsonArray();
this.signatureMappings = typeTable.get("signatureMappings").getAsJsonObject();
this.signatureToStringValues = typeTable.get("signatureToStringValues").getAsJsonArray();
this.numberIndexTypes = typeTable.get("numberIndexTypes").getAsJsonObject();
this.stringIndexTypes = typeTable.get("stringIndexTypes").getAsJsonObject();
this.baseTypes = typeTable.get("baseTypes").getAsJsonObject();
this.selfTypes = typeTable.get("selfTypes").getAsJsonObject();
}
public String getTypeString(int index) {
return typeStrings.get(index).getAsString();
}
public String getTypeToStringValue(int index) {
return typeToStringValues.get(index).getAsString();
}
public JsonObject getPropertyLookups() {
return propertyLookups;
}
public JsonObject getTypeAliases() {
return typeAliases;
}
public int getNumberOfTypes() {
return typeStrings.size();
}
public String getSymbolString(int index) {
return symbolStrings.get(index).getAsString();
}
public int getNumberOfSymbols() {
return symbolStrings.size();
}
public JsonObject getModuleMappings() {
return moduleMappings;
}
public JsonObject getGlobalMappings() {
return globalMappings;
}
public JsonArray getSignatureStrings() {
return signatureStrings;
}
public int getNumberOfSignatures() {
return signatureStrings.size();
}
public String getSignatureString(int i) {
return signatureStrings.get(i).getAsString();
}
public JsonObject getSignatureMappings() {
return signatureMappings;
}
public JsonArray getSignatureToStringValues() {
return signatureToStringValues;
}
public String getSignatureToStringValue(int i) {
return signatureToStringValues.get(i).getAsString();
}
public JsonObject getNumberIndexTypes() {
return numberIndexTypes;
}
public JsonObject getStringIndexTypes() {
return stringIndexTypes;
}
public JsonObject getBaseTypes() {
return baseTypes;
}
public JsonObject getSelfTypes() {
return selfTypes;
}
}

View File

@@ -111,12 +111,17 @@ public class AutoBuildTests {
try {
Set<String> actual = new LinkedHashSet<>();
new AutoBuild() {
private void markExtracted(Path file, FileExtractor extractor) {
String extracted = file.toString();
if (extractor.getConfig().hasFileType()) {
extracted += ":" + extractor.getFileType(file.toFile());
}
actual.add(extracted);
}
@Override
protected CompletableFuture<?> extract(FileExtractor extractor, Path file, boolean concurrent) {
String extracted = file.toString();
if (extractor.getConfig().hasFileType())
extracted += ":" + extractor.getFileType(file.toFile());
actual.add(extracted);
markExtracted(file, extractor);
return CompletableFuture.completedFuture(null);
}
@@ -134,7 +139,7 @@ public class AutoBuildTests {
java.util.Set<Path> extractedFiles,
FileExtractors extractors) {
for (Path f : files) {
actual.add(f.toString());
markExtracted(f, extractors.forFile(f));
extractedFiles.add(f);
}
}
@@ -190,15 +195,6 @@ public class AutoBuildTests {
@Test
public void typescript() throws IOException {
envVars.put("LGTM_INDEX_TYPESCRIPT", "basic");
addFile(true, LGTM_SRC, "tst.ts");
addFile(true, LGTM_SRC, "tst.tsx");
runTest();
}
@Test(expected = UserError.class)
public void typescriptWrongConfig() throws IOException {
envVars.put("LGTM_INDEX_TYPESCRIPT", "true");
addFile(true, LGTM_SRC, "tst.ts");
addFile(true, LGTM_SRC, "tst.tsx");
runTest();
@@ -207,7 +203,6 @@ public class AutoBuildTests {
@Test
public void skipJsFilesDerivedFromTypeScriptFiles() throws IOException {
// JS-derived files (.js, .cjs, .mjs, .jsx, .cjsx, .mjsx) should be skipped when TS indexing
envVars.put("LGTM_INDEX_TYPESCRIPT", "basic");
// Add TypeScript sources
addFile(true, LGTM_SRC, "foo.ts");
addFile(true, LGTM_SRC, "bar.tsx");
@@ -225,7 +220,6 @@ public class AutoBuildTests {
@Test
public void skipFilesInTsconfigOutDir() throws IOException {
envVars.put("LGTM_INDEX_TYPESCRIPT", "basic");
// Files under outDir in tsconfig.json should be excluded
// Create tsconfig.json with outDir set to "dist"
addFile(true, LGTM_SRC, "tsconfig.json");
@@ -506,15 +500,6 @@ public class AutoBuildTests {
runTest();
}
@Test
public void noTypescriptExtraction() throws IOException {
envVars.put("LGTM_INDEX_TYPESCRIPT", "none");
addFile(false, LGTM_SRC, "tst.ts");
addFile(false, LGTM_SRC, "sub.js", "tst.ts");
addFile(false, LGTM_SRC, "tst.js.ts");
runTest();
}
@Test
public void includeNonExistentFile() throws IOException {
envVars.put("LGTM_INDEX_INCLUDE", "tst.js");

View File

@@ -1 +0,0 @@
export const foo: { bar: number } = { bar: 42};

View File

@@ -1,5 +0,0 @@
import javascript
from Expr e, Type t
where t = e.getType()
select e, t

View File

@@ -1,2 +0,0 @@
def test(codeql, javascript):
codeql.database.create(extractor_option="skip_types=true")

View File

@@ -145,4 +145,3 @@ ql/javascript/ql/src/meta/analysis-quality/UnresolvableImports.ql
ql/javascript/ql/src/meta/extraction-metrics/FileData.ql
ql/javascript/ql/src/meta/extraction-metrics/MissingMetrics.ql
ql/javascript/ql/src/meta/extraction-metrics/PhaseTimings.ql
ql/javascript/ql/src/meta/types/TypedExprs.ql

View File

@@ -126,7 +126,7 @@ private predicate propertyLookup(Expr prop, AstNode write, string kind) {
private predicate typeLookup(AstNode ref, AstNode decl, string kind) {
exists(TypeAccess typeAccess |
ref = typeAccess.getIdentifier() and
decl = typeAccess.getTypeName().getADefinition() and
decl = typeAccess.getTypeBinding().getTypeDefinition() and
kind = "T"
)
}

View File

@@ -47,7 +47,7 @@ import semmle.javascript.NodeJS
import semmle.javascript.NPM
import semmle.javascript.Paths
import semmle.javascript.Promises
import semmle.javascript.CanonicalNames
deprecated import semmle.javascript.CanonicalNames
import semmle.javascript.RangeAnalysis
import semmle.javascript.Regexp
import semmle.javascript.Routing

View File

@@ -5,6 +5,8 @@
import javascript
private import internal.StmtContainers
private import semmle.javascript.internal.CachedStages
private import semmle.javascript.internal.TypeResolution
private import semmle.javascript.internal.BindingInfo
/**
* A program element corresponding to JavaScript code, such as an expression
@@ -472,5 +474,22 @@ module AST {
/** Gets the data flow node associated with this program element. */
DataFlow::ValueNode flow() { result = DataFlow::valueNode(this) }
/**
* Gets information about the results of name-resolution for this expression.
*
* This can be used to map an expression to the class it refers to, or
* associate it with a named value coming from an dependency.
*/
ExprNameBindingNode getNameBinding() { result = this }
/**
* Gets information about the type of this expression.
*
* This can be used to map an expression to the classes it may be an instance of
* (according to the type system), or to associate it with a named type coming
* from a dependency.
*/
TypeNameBindingNode getTypeBinding() { TypeResolution::valueHasType(this, result) }
}
}

View File

@@ -1,6 +1,7 @@
/**
* Provides classes for working with name resolution of namespaces and types.
*/
deprecated module;
import javascript
@@ -18,7 +19,7 @@ import javascript
*
* This class is only populated when full TypeScript extraction is enabled.
*/
class CanonicalName extends @symbol {
deprecated class CanonicalName extends @symbol {
/**
* Gets the parent of this canonical name, that is, the prefix of its qualified name.
*/
@@ -218,7 +219,7 @@ class CanonicalName extends @symbol {
/**
* The canonical name for a type.
*/
class TypeName extends CanonicalName {
deprecated class TypeName extends CanonicalName {
TypeName() {
exists(TypeReference ref | type_symbol(ref, this)) or
exists(TypeDefinition def | ast_node_symbol(def, this)) or
@@ -261,7 +262,7 @@ class TypeName extends CanonicalName {
/**
* The canonical name for a namespace.
*/
class Namespace extends CanonicalName {
deprecated class Namespace extends CanonicalName {
Namespace() {
this.getAChild().isExportedMember() or
exists(NamespaceDefinition def | ast_node_symbol(def, this)) or
@@ -309,7 +310,7 @@ class Namespace extends CanonicalName {
/**
* The canonical name for a function.
*/
class CanonicalFunctionName extends CanonicalName {
deprecated class CanonicalFunctionName extends CanonicalName {
CanonicalFunctionName() {
exists(Function fun | ast_node_symbol(fun, this)) or
exists(InvokeExpr invoke | ast_node_symbol(invoke, this))

View File

@@ -119,7 +119,7 @@ class ClassOrInterface extends @class_or_interface, TypeParameterized {
*
* Anonymous classes and interfaces do not have a canonical name.
*/
TypeName getTypeName() { result.getADefinition() = this }
deprecated TypeName getTypeName() { result.getADefinition() = this }
/**
* Gets the ClassOrInterface corresponding to either a super type or an implemented interface.

View File

@@ -171,12 +171,14 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
predicate mayReferToParameter(Parameter p) { DataFlow::parameterNode(p).flowsToExpr(this) }
/**
* DEPRECATED. Use `getTypeBinding()` instead.
*
* Gets the static type of this expression, as determined by the TypeScript type system.
*
* Has no result if the expression is in a JavaScript file or in a TypeScript
* file that was extracted without type information.
*/
Type getType() { ast_node_type(this, result) }
deprecated Type getType() { ast_node_type(this, result) }
/**
* Holds if the syntactic context that the expression appears in relies on the expression
@@ -988,12 +990,14 @@ class InvokeExpr extends @invokeexpr, Expr {
}
/**
* DEPRECATED. No longer supported.
*
* Gets the call signature of the invoked function, as determined by the TypeScript
* type system, with overloading resolved and type parameters substituted.
*
* This predicate is only populated for files extracted with full TypeScript extraction.
*/
CallSignatureType getResolvedSignature() { invoke_expr_signature(this, result) }
deprecated CallSignatureType getResolvedSignature() { invoke_expr_signature(this, result) }
/**
* Gets the index of the targeted call signature among the overload signatures
@@ -1004,25 +1008,21 @@ class InvokeExpr extends @invokeexpr, Expr {
int getResolvedOverloadIndex() { invoke_expr_overload_index(this, result) }
/**
* DEPRECATED. No longer directly supported, but `getResolvedCallee()` may be usable as an alternative.
*
* Gets the canonical name of the static call target, as determined by the TypeScript type system.
*
* This predicate is only populated for files extracted with full TypeScript extraction.
*/
CanonicalFunctionName getResolvedCalleeName() { ast_node_symbol(this, result) }
deprecated CanonicalFunctionName getResolvedCalleeName() { ast_node_symbol(this, result) }
/**
* Gets the statically resolved target function, as determined by the TypeScript type system, if any.
*
* This predicate is only populated for files extracted with full TypeScript extraction.
*
* Note that the resolved function may be overridden in a subclass and thus is not
* necessarily the actual target of this invocation at runtime.
*/
Function getResolvedCallee() {
TypeResolution::callTarget(this, result)
or
result = this.getResolvedCalleeName().getImplementation()
}
Function getResolvedCallee() { TypeResolution::callTarget(this, result) }
}
/**

View File

@@ -434,12 +434,12 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
*
* This predicate is only populated for files extracted with full TypeScript extraction.
*/
CanonicalFunctionName getCanonicalName() { ast_node_symbol(this, result) }
deprecated CanonicalFunctionName getCanonicalName() { ast_node_symbol(this, result) }
/**
* Gets the call signature of this function, as determined by the TypeScript compiler, if any.
*/
CallSignatureType getCallSignature() { declared_function_signature(this, result) }
deprecated CallSignatureType getCallSignature() { declared_function_signature(this, result) }
}
/**

View File

@@ -6,11 +6,20 @@ import javascript
private import internal.StmtContainers
private import internal.NameResolution
private import internal.UnderlyingTypes
private import internal.BindingInfo
/**
* A type annotation, either in the form of a TypeScript type or a JSDoc comment.
*/
class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
/**
* Gets information about the results of name-resolution for this type.
*
* This can be used to map a type name to the class/interface it refers to, or
* associate it with a named type coming from an dependency.
*/
TypeNameBindingNode getTypeBinding() { result = this }
/** Holds if this is the `any` type. */
predicate isAny() { none() }
@@ -126,7 +135,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
*
* Note that this has no result for JSDoc type annotations.
*/
Type getType() { none() }
deprecated Type getType() { none() }
/**
* Gets the class referenced by this type annotation, if any.

View File

@@ -1,5 +1,4 @@
import javascript
private import semmle.javascript.internal.UnderlyingTypes
/**
* A statement that defines a namespace, that is, a namespace declaration or enum declaration.
@@ -32,7 +31,7 @@ class NamespaceDefinition extends Stmt, @namespace_definition, AST::ValueNode {
/**
* Gets the canonical name of the namespace being defined.
*/
Namespace getNamespace() { result.getADefinition() = this }
deprecated Namespace getNamespace() { result.getADefinition() = this }
}
/**
@@ -112,12 +111,12 @@ class TypeDefinition extends AstNode, @type_definition {
/**
* Gets the canonical name of the type being defined.
*/
TypeName getTypeName() { result.getADefinition() = this }
deprecated TypeName getTypeName() { result.getADefinition() = this }
/**
* Gets the type defined by this declaration.
*/
Type getType() { ast_node_type(this.getIdentifier(), result) }
deprecated Type getType() { ast_node_type(this.getIdentifier(), result) }
override string getAPrimaryQlClass() { result = "TypeDefinition" }
}
@@ -269,7 +268,7 @@ class TypeAliasDeclaration extends @type_alias_declaration, TypeParameterized, S
/**
* Gets the canonical name of the type being defined.
*/
TypeName getTypeName() { result.getADefinition() = this }
deprecated TypeName getTypeName() { result.getADefinition() = this }
override string getAPrimaryQlClass() { result = "TypeAliasDeclaration" }
}
@@ -549,7 +548,7 @@ class LocalNamespaceName extends @local_namespace_name, LexicalName {
/**
* Gets the canonical name of the namespace referenced by this name.
*/
Namespace getNamespace() { result = this.getADeclaration().getNamespace() }
deprecated Namespace getNamespace() { result = this.getADeclaration().getNamespace() }
override DeclarationSpace getDeclarationSpace() { result = "namespace" }
}
@@ -569,7 +568,7 @@ class TypeExpr extends ExprOrType, @typeexpr, TypeAnnotation {
* Has no result if this occurs in a TypeScript file that was extracted
* without type information.
*/
override Type getType() { ast_node_type(this, result) }
deprecated override Type getType() { ast_node_type(this, result) }
override Stmt getEnclosingStmt() { result = ExprOrType.super.getEnclosingStmt() }
@@ -693,7 +692,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef {
/**
* Gets the canonical name of the type being accessed.
*/
TypeName getTypeName() { ast_node_symbol(this, result) }
deprecated TypeName getTypeName() { ast_node_symbol(this, result) }
override string getAPrimaryQlClass() { result = "TypeAccess" }
}
@@ -1380,7 +1379,7 @@ class LocalNamespaceDecl extends VarDecl, NamespaceRef {
/**
* Gets the canonical name of the namespace being defined or aliased by this name.
*/
Namespace getNamespace() { ast_node_symbol(this, result) }
deprecated Namespace getNamespace() { ast_node_symbol(this, result) }
}
/**
@@ -1398,7 +1397,7 @@ class NamespaceAccess extends TypeExpr, NamespaceRef, @namespace_access {
/**
* Gets the canonical name of the namespace being accessed.
*/
Namespace getNamespace() { ast_node_symbol(this, result) }
deprecated Namespace getNamespace() { ast_node_symbol(this, result) }
override string getAPrimaryQlClass() { result = "NamespaceAccess" }
}
@@ -1507,7 +1506,7 @@ class EnumDeclaration extends NamespaceDefinition, @enum_declaration, AST::Value
/**
* Gets the canonical name of the type being defined.
*/
TypeName getTypeName() { ast_node_symbol(this, result) }
deprecated TypeName getTypeName() { ast_node_symbol(this, result) }
/**
* Gets the local namespace name introduced by the enumeration, for use in
@@ -1595,7 +1594,7 @@ class EnumMember extends AstNode, @enum_member {
/**
* Gets the canonical name of the type defined by this enum member.
*/
TypeName getTypeName() { ast_node_symbol(this, result) }
deprecated TypeName getTypeName() { ast_node_symbol(this, result) }
override string getAPrimaryQlClass() { result = "EnumMember" }
}
@@ -1770,13 +1769,18 @@ class TypeRootFolder extends Folder {
/// Types
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A static type in the TypeScript type system.
*
* Types are generally not associated with a specific location or AST node.
* For instance, there may be many AST nodes representing different uses of the
* `number` keyword, but there only exists one `number` type.
*/
class Type extends @type {
deprecated class Type extends @type {
/**
* Gets a string representation of this type.
*/
@@ -1973,9 +1977,14 @@ class Type extends @type {
}
/**
* * DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A union type or intersection type, such as `string | number` or `T & U`.
*/
class UnionOrIntersectionType extends Type, @union_or_intersection_type {
deprecated class UnionOrIntersectionType extends Type, @union_or_intersection_type {
/**
* Gets the `i`th member of this union or intersection, starting at 0.
*/
@@ -1993,19 +2002,34 @@ class UnionOrIntersectionType extends Type, @union_or_intersection_type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A union type, such as `string | number`.
*
* Note that the `boolean` type is represented as the union `true | false`,
* but is still displayed as `boolean` in string representations.
*/
class UnionType extends UnionOrIntersectionType, @union_type { }
deprecated class UnionType extends UnionOrIntersectionType, @union_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* An intersection type, such as `T & {x: number}`.
*/
class IntersectionType extends UnionOrIntersectionType, @intersection_type { }
deprecated class IntersectionType extends UnionOrIntersectionType, @intersection_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A type that describes a JavaScript `Array` object.
*
* Specifically, the following three kinds of types are considered array types:
@@ -2016,7 +2040,7 @@ class IntersectionType extends UnionOrIntersectionType, @intersection_type { }
* Foreign array-like objects such as `HTMLCollection` are not normal JavaScript arrays,
* and their corresponding types are not considered array types either.
*/
class ArrayType extends Type {
deprecated class ArrayType extends Type {
ArrayType() {
this instanceof @tuple_type or
this.(TypeReference).hasQualifiedName("Array") or
@@ -2030,25 +2054,40 @@ class ArrayType extends Type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* An array type such as `Array<string>`, or equivalently, `string[]`.
*/
class PlainArrayType extends ArrayType, TypeReference {
deprecated class PlainArrayType extends ArrayType, TypeReference {
PlainArrayType() { this.hasQualifiedName("Array") }
override Type getNumberIndexType() { result = this.getTypeArgument(0) }
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A read-only array type such as `ReadonlyArray<string>`.
*/
class ReadonlyArrayType extends ArrayType, TypeReference {
deprecated class ReadonlyArrayType extends ArrayType, TypeReference {
ReadonlyArrayType() { this.hasQualifiedName("ReadonlyArray") }
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A tuple type, such as `[number, string]`.
*/
class TupleType extends ArrayType, @tuple_type {
deprecated class TupleType extends ArrayType, @tuple_type {
/**
* Gets the `i`th member of this tuple type, starting at 0.
*/
@@ -2102,34 +2141,64 @@ class TupleType extends ArrayType, @tuple_type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The predefined `any` type.
*/
class AnyType extends Type, @any_type { }
deprecated class AnyType extends Type, @any_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The predefined `unknown` type.
*/
class UnknownType extends Type, @unknown_type { }
deprecated class UnknownType extends Type, @unknown_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The predefined `string` type.
*/
class StringType extends Type, @string_type { }
deprecated class StringType extends Type, @string_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The predefined `number` type.
*/
class NumberType extends Type, @number_type { }
deprecated class NumberType extends Type, @number_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The predefined `bigint` type.
*/
class BigIntType extends Type, @bigint_type { }
deprecated class BigIntType extends Type, @bigint_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A boolean, number, or string literal type.
*/
class LiteralType extends Type, @literal_type {
deprecated class LiteralType extends Type, @literal_type {
/**
* Gets the string value of this literal.
*/
@@ -2137,9 +2206,14 @@ class LiteralType extends Type, @literal_type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The boolean literal type `true` or `false`.
*/
class BooleanLiteralType extends LiteralType, @boolean_literal_type {
deprecated class BooleanLiteralType extends LiteralType, @boolean_literal_type {
/**
* Gets the boolean value represented by this type.
*/
@@ -2153,7 +2227,7 @@ class BooleanLiteralType extends LiteralType, @boolean_literal_type {
/**
* A number literal as a static type.
*/
class NumberLiteralType extends LiteralType, @number_literal_type {
deprecated class NumberLiteralType extends LiteralType, @number_literal_type {
override string getStringValue() { type_literal_value(this, result) }
/**
@@ -2168,16 +2242,26 @@ class NumberLiteralType extends LiteralType, @number_literal_type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A string literal as a static type.
*/
class StringLiteralType extends LiteralType, @string_literal_type {
deprecated class StringLiteralType extends LiteralType, @string_literal_type {
override string getStringValue() { type_literal_value(this, result) }
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A bigint literal as a static type.
*/
class BigIntLiteralType extends LiteralType {
deprecated class BigIntLiteralType extends LiteralType {
override string getStringValue() { type_literal_value(this, result) }
/**
@@ -2192,9 +2276,14 @@ class BigIntLiteralType extends LiteralType {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The `boolean` type, internally represented as the union type `true | false`.
*/
class BooleanType extends UnionType {
deprecated class BooleanType extends UnionType {
BooleanType() {
this.getAnElementType() instanceof @true_type and
this.getAnElementType() instanceof @false_type and
@@ -2203,9 +2292,14 @@ class BooleanType extends UnionType {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The `string` type or a string literal type.
*/
class StringLikeType extends Type {
deprecated class StringLikeType extends Type {
StringLikeType() {
this instanceof StringType or
this instanceof StringLiteralType
@@ -2213,9 +2307,14 @@ class StringLikeType extends Type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The `number` type or a number literal type.
*/
class NumberLikeType extends Type {
deprecated class NumberLikeType extends Type {
NumberLikeType() {
this instanceof NumberType or
this instanceof NumberLiteralType
@@ -2223,9 +2322,14 @@ class NumberLikeType extends Type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The `boolean`, `true,` or `false` type.
*/
class BooleanLikeType extends Type {
deprecated class BooleanLikeType extends Type {
BooleanLikeType() {
this instanceof BooleanType or
this instanceof BooleanLiteralType
@@ -2233,39 +2337,74 @@ class BooleanLikeType extends Type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The `void` type.
*/
class VoidType extends Type, @void_type { }
deprecated class VoidType extends Type, @void_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The `undefined` type.
*/
class UndefinedType extends Type, @undefined_type { }
deprecated class UndefinedType extends Type, @undefined_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The `null` type.
*/
class NullType extends Type, @null_type { }
deprecated class NullType extends Type, @null_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The `never` type.
*/
class NeverType extends Type, @never_type { }
deprecated class NeverType extends Type, @never_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The `symbol` type or a specific `unique symbol` type.
*/
class SymbolType extends Type, @symbol_type { }
deprecated class SymbolType extends Type, @symbol_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The `symbol` type.
*/
class PlainSymbolType extends SymbolType, @plain_symbol_type { }
deprecated class PlainSymbolType extends SymbolType, @plain_symbol_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A `unique symbol` type.
*/
class UniqueSymbolType extends SymbolType, @unique_symbol_type {
deprecated class UniqueSymbolType extends SymbolType, @unique_symbol_type {
/**
* Gets the canonical name of the variable exposing the symbol.
*/
@@ -2292,14 +2431,24 @@ class UniqueSymbolType extends SymbolType, @unique_symbol_type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The `object` type.
*/
class ObjectKeywordType extends Type, @objectkeyword_type { }
deprecated class ObjectKeywordType extends Type, @objectkeyword_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A type that refers to a class, interface, enum, or enum member.
*/
class TypeReference extends Type, @type_reference {
deprecated class TypeReference extends Type, @type_reference {
/**
* Gets the canonical name of the type being referenced.
*/
@@ -2350,9 +2499,14 @@ class TypeReference extends Type, @type_reference {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A type that refers to a class, possibly with type arguments.
*/
class ClassType extends TypeReference {
deprecated class ClassType extends TypeReference {
ClassDefinition declaration;
ClassType() { declaration = this.getADefinition() }
@@ -2364,9 +2518,14 @@ class ClassType extends TypeReference {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A type that refers to an interface, possibly with type arguents.
*/
class InterfaceType extends TypeReference {
deprecated class InterfaceType extends TypeReference {
InterfaceDeclaration declaration;
InterfaceType() { declaration = this.getADefinition() }
@@ -2378,9 +2537,14 @@ class InterfaceType extends TypeReference {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A type that refers to an enum.
*/
class EnumType extends TypeReference {
deprecated class EnumType extends TypeReference {
EnumDeclaration declaration;
EnumType() { declaration = this.getADefinition() }
@@ -2392,9 +2556,14 @@ class EnumType extends TypeReference {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A type that refers to the value of an enum member.
*/
class EnumLiteralType extends TypeReference {
deprecated class EnumLiteralType extends TypeReference {
EnumMember declaration;
EnumLiteralType() { declaration = this.getADefinition() }
@@ -2406,9 +2575,14 @@ class EnumLiteralType extends TypeReference {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A type that refers to a type alias.
*/
class TypeAliasReference extends TypeReference {
deprecated class TypeAliasReference extends TypeReference {
TypeAliasReference() { type_alias(this, _) }
/**
@@ -2420,14 +2594,24 @@ class TypeAliasReference extends TypeReference {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* An anonymous interface type, such as `{ x: number }`.
*/
class AnonymousInterfaceType extends Type, @object_type { }
deprecated class AnonymousInterfaceType extends Type, @object_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A type that refers to a type variable.
*/
class TypeVariableType extends Type, @typevariable_type {
deprecated class TypeVariableType extends Type, @typevariable_type {
/**
* Gets a syntactic declaration of this type variable.
*
@@ -2465,9 +2649,14 @@ class TypeVariableType extends Type, @typevariable_type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A type that refers to a type variable declared on a class, interface or function.
*/
class CanonicalTypeVariableType extends TypeVariableType, @canonical_type_variable_type {
deprecated class CanonicalTypeVariableType extends TypeVariableType, @canonical_type_variable_type {
override TypeName getHostType() { result = this.getCanonicalName().getParent() }
override CanonicalName getCanonicalName() { type_symbol(this, result) }
@@ -2476,6 +2665,11 @@ class CanonicalTypeVariableType extends TypeVariableType, @canonical_type_variab
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A type that refers to a type variable without a canonical name.
*
* These arise in generic call signatures such as `<T>(x: T) => T`.
@@ -2487,13 +2681,18 @@ class CanonicalTypeVariableType extends TypeVariableType, @canonical_type_variab
* - `<T>(x: T) => T`
* - `<S, T>(x: S, y: T) => T`.
*/
class LexicalTypeVariableType extends TypeVariableType, @lexical_type_variable_type {
deprecated class LexicalTypeVariableType extends TypeVariableType, @lexical_type_variable_type {
override string getName() {
types(this, _, result) // The toString value contains the name.
}
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A `this` type in a specific class or interface.
*
* For example, the return type of `span` below is a `this` type
@@ -2504,7 +2703,7 @@ class LexicalTypeVariableType extends TypeVariableType, @lexical_type_variable_t
* }
* ```
*/
class ThisType extends Type, @this_type {
deprecated class ThisType extends Type, @this_type {
/**
* Gets the type containing the `this` type.
*/
@@ -2514,10 +2713,15 @@ class ThisType extends Type, @this_type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* The type of a named value, `typeof X`, typically denoting the type of
* a class constructor, namespace object, enum object, or module object.
*/
class TypeofType extends Type, @typeof_type {
deprecated class TypeofType extends Type, @typeof_type {
/**
* Gets the canonical name of the named value.
*/
@@ -2590,9 +2794,14 @@ module SignatureKind {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A function or constructor signature in a TypeScript type.
*/
class CallSignatureType extends @signature_type {
deprecated class CallSignatureType extends @signature_type {
/**
* Gets a value indicating if this is a function or constructor signature.
*/
@@ -2739,14 +2948,25 @@ class CallSignatureType extends @signature_type {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A function call signature in a type, that is, a signature without the `new` keyword.
*/
class FunctionCallSignatureType extends CallSignatureType, @function_signature_type { }
deprecated class FunctionCallSignatureType extends CallSignatureType, @function_signature_type { }
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A constructor call signature in a type, that is, a signature with the `new` keyword.
*/
class ConstructorCallSignatureType extends CallSignatureType, @constructor_signature_type { }
deprecated class ConstructorCallSignatureType extends CallSignatureType, @constructor_signature_type
{ }
/**
* A type name that defines a promise.
@@ -2756,7 +2976,7 @@ class ConstructorCallSignatureType extends CallSignatureType, @constructor_signa
* - It has one type parameter, say, `T`
* - It has a `then` method whose first argument is a callback that takes a `T` as argument.
*/
private class PromiseTypeName extends TypeName {
deprecated private class PromiseTypeName extends TypeName {
PromiseTypeName() {
// The name must suggest it is a promise.
this.getName().matches(["%Promise", "%PromiseLike", "%Thenable", "%Deferred"]) and
@@ -2775,12 +2995,17 @@ private class PromiseTypeName extends TypeName {
}
/**
* DEPRECATED. Static types from the TypeScript compiler are not longer available. Use one of the following alternatives instead:
* - `Expr.getTypeBinding()`
* - `Expr.getNameBinding()`
* - `TypeAnnotation.getTypeBinding()`
*
* A type such as `Promise<T>`, describing a promise or promise-like object.
*
* This includes types whose name and `then` method signature suggest it is a promise,
* such as `PromiseLike<T>` and `Thenable<T>`.
*/
class PromiseType extends TypeReference {
deprecated class PromiseType extends TypeReference {
PromiseType() {
this.getNumTypeArgument() = 1 and
this.getTypeName() instanceof PromiseTypeName

View File

@@ -3,7 +3,6 @@
*/
private import javascript
private import semmle.javascript.internal.TypeResolution
/**
* An input to a view component, such as React props.
@@ -16,7 +15,7 @@ abstract class ViewComponentInput extends DataFlow::Node {
private class ViewComponentInputAsThreatModelSource extends ThreatModelSource::Range instanceof ViewComponentInput
{
ViewComponentInputAsThreatModelSource() {
not TypeResolution::valueHasSanitizingPrimitiveType(this.asExpr())
not this.asExpr().getTypeBinding().isSanitizingPrimitiveType()
}
final override string getThreatModel() { result = "view-component-input" }

View File

@@ -50,7 +50,7 @@ module ClassValidator {
pragma[noinline]
private ClassDefinition getClassReferencedByPropRead(DataFlow::PropRead read) {
read.getBase().asExpr().getType().unfold().(ClassType).getClass() = result
read.getBase().asExpr().getTypeBinding().getAnUnderlyingClass().getAstNode() = result
}
/**

View File

@@ -41,18 +41,19 @@ module Electron {
BrowserView() { this = DataFlow::moduleMember("electron", "BrowserView").getAnInstantiation() }
}
/**
* An expression of type `BrowserWindow` or `BrowserView`.
*/
private class BrowserObjectByType extends BrowserObject {
BrowserObjectByType() {
exists(string tp | tp = "BrowserWindow" or tp = "BrowserView" |
this.asExpr().getType().hasUnderlyingType("electron", tp)
)
private class ElectronEntryPoint extends API::EntryPoint {
ElectronEntryPoint() { this = "Electron.Browser" }
override DataFlow::SourceNode getASource() {
result.hasUnderlyingType(["Electron.BrowserWindow", "Electron.BrowserView"])
}
}
private API::Node browserObject() { result.asSource() instanceof NewBrowserObject }
private API::Node browserObject() {
result.asSource() instanceof NewBrowserObject or
result = API::Node::ofType("electron", ["BrowserWindow", "BrowserView"]) or
result = any(ElectronEntryPoint e).getANode()
}
/**
* A data flow node whose value may originate from a browser object instantiation.

View File

@@ -48,7 +48,7 @@ module Express {
private predicate isRouter(DataFlow::Node e) {
isRouter(e, _)
or
e.asExpr().getType().hasUnderlyingType("express", "Router")
e.(DataFlow::SourceNode).hasUnderlyingType("express", "Router")
or
// created by `webpack-dev-server`
WebpackDevServer::webpackDevServerApp().flowsTo(e)

View File

@@ -5,8 +5,6 @@
import javascript
private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations
private import semmle.javascript.dataflow.internal.PreCallGraphStep
private import semmle.javascript.internal.NameResolution
private import semmle.javascript.internal.TypeResolution
/**
* Provides classes and predicates for reasoning about [Nest](https://nestjs.com/).
@@ -137,7 +135,7 @@ module NestJS {
hasSanitizingPipe(this, true) and
// Note: we could consider types with class-validator decorators to be sanitized here, but instead we consider the root
// object to be tainted, but omit taint steps for the individual properties names that have sanitizing decorators. See ClassValidator.qll.
TypeResolution::isSanitizingPrimitiveType(this.getParameter().getTypeAnnotation())
this.getParameter().getTypeBinding().isSanitizingPrimitiveType()
}
}
@@ -337,9 +335,10 @@ module NestJS {
handler.isReturnValueReflected() and
this = handler.getAReturn() and
// Only returned strings are sinks. If we can find a type for the return value, it must be string-like.
not exists(NameResolution::Node type |
TypeResolution::valueHasType(this.asExpr(), type) and
not TypeResolution::hasUnderlyingStringOrAnyType(type)
(
this.asExpr().getTypeBinding().hasUnderlyingStringOrAnyType()
or
not exists(this.asExpr().getTypeBinding())
)
}
@@ -540,46 +539,32 @@ module NestJS {
)
}
private DataFlow::Node getConcreteClassFromProviderTuple(DataFlow::SourceNode tuple) {
result = tuple.getAPropertyWrite("useClass").getRhs()
private DataFlow::ClassNode getConcreteClassFromProviderTuple(DataFlow::SourceNode tuple) {
result = tuple.getAPropertyWrite("useClass").getRhs().asExpr().getNameBinding().getClassNode()
or
exists(DataFlow::FunctionNode f |
f = tuple.getAPropertyWrite("useFactory").getRhs().getAFunctionValue() and
result.getAstNode() = f.getFunction().getAReturnedExpr().getType().(ClassType).getClass()
result = f.getFunction().getAReturnedExpr().getTypeBinding().getAnUnderlyingClass()
)
or
result.getAstNode() =
tuple.getAPropertyWrite("useValue").getRhs().asExpr().getType().(ClassType).getClass()
result =
tuple.getAPropertyWrite("useValue").getRhs().asExpr().getTypeBinding().getAnUnderlyingClass()
}
private predicate providerPair(DataFlow::Node interface, DataFlow::Node concreteClass) {
private predicate providerPair(DataFlow::ClassNode interface, DataFlow::ClassNode concreteClass) {
exists(DataFlow::SourceNode tuple |
tuple = providerTuple().getALocalSource() and
interface = tuple.getAPropertyWrite("provide").getRhs() and
interface =
tuple.getAPropertyWrite("provide").getRhs().asExpr().getNameBinding().getClassNode() and
concreteClass = getConcreteClassFromProviderTuple(tuple)
)
}
/** Gets the class being referenced at `node` without relying on the call graph. */
private DataFlow::ClassNode getClassFromNode(DataFlow::Node node) {
result.getAstNode() = node.analyze().getAValue().(AbstractClass).getClass()
}
private predicate providerClassPair(
DataFlow::ClassNode interface, DataFlow::ClassNode concreteClass
) {
exists(DataFlow::Node interfaceNode, DataFlow::Node concreteClassNode |
providerPair(interfaceNode, concreteClassNode) and
interface = getClassFromNode(interfaceNode) and
concreteClass = getClassFromNode(concreteClassNode)
)
}
private class DependencyInjectionStep extends PreCallGraphStep {
override predicate classInstanceSource(DataFlow::ClassNode cls, DataFlow::Node node) {
exists(DataFlow::ClassNode interfaceClass |
node.asExpr().(Parameter).getType().(ClassType).getClass() = interfaceClass.getAstNode() and
providerClassPair(interfaceClass, cls)
node.asExpr().getTypeBinding().getTypeDefinition() = interfaceClass.getAstNode() and
providerPair(interfaceClass, cls)
)
}
}

View File

@@ -0,0 +1,177 @@
/**
* Provides a limited public interface to name/type resolution information.
*/
private import javascript
private import semmle.javascript.internal.NameResolution
private import semmle.javascript.internal.TypeResolution
private import semmle.javascript.internal.UnderlyingTypes
/**
* Interface for accessing name-resolution info about type names.
*/
class TypeNameBindingNode extends NameResolution::Node {
/**
* Holds if type refers to, or is an alias for, the given type name relative to the global scope.
*
* For example:
* ```ts
* var x: Document; // hasQualifiedName("Document")
* var x: Electron; // hasQualifiedName("Electron")
* var x: Electron.BrowserWindow; // hasQualifiedName("Electron.BrowserWindow")
* ```
*/
predicate hasQualifiedName(string qualifiedName) {
NameResolution::nodeRefersToModule(this, "global", qualifiedName)
}
/**
* Holds if this refers to a value exported by the given module, with the given
* qualified name. If the `qualifiedName` is empty, this refers to the module itself.
*
* For example, the type annotations below have the following name bindings:
* ```ts
* import { Request } from "express";
*
* var x: Request; // hasUnderlyingType("express", "Request")
* var x: Request | null; // no result (see hasUnderlyingType)
* var x: Request & { prop: string }; // no result (see hasUnderlyingType)
*
* interface CustomSubtype extends Request {}
*
* var x: CustomSubtype; // no result (see hasUnderlyingType)
*
* var x: typeof import("express"); // hasUnderlyingType("express", "")
* ```
*/
predicate hasQualifiedName(string moduleName, string qualifiedName) {
NameResolution::nodeRefersToModule(this, moduleName, qualifiedName)
}
/**
* Holds if this type refers to the given type exported from the given module, after
* unfolding unions and intersections, and following subtype relations.
*
* For example:
* ```ts
* import { Request } from "express";
*
* var x: Request; // hasUnderlyingType("express", "Request")
* var x: Request | null; // hasUnderlyingType("express", "Request")
* var x: Request & { prop: string }; // hasUnderlyingType("express", "Request")
*
* interface CustomSubtype extends Request {}
*
* var x: CustomSubtype; // hasUnderlyingType("express", "Request")
* ```
*/
predicate hasUnderlyingType(string moduleName, string qualifiedName) {
UnderlyingTypes::nodeHasUnderlyingType(this, moduleName, qualifiedName)
}
/**
* Holds if this type refers to the given type from the global scope, after
* unfolding unions and intersections, and following subtype relations.
*
* For example:
* ```ts
* var x: Document; // hasUnderlyingType("Document")
* var x: Document | null; // hasUnderlyingType("Document")
* var x: Document & { prop: string }; // hasUnderlyingType("Document")
*
* interface CustomSubtype extends Document {}
*
* var x: CustomSubtype; // hasUnderlyingType("Document")
* ```
*/
predicate hasUnderlyingType(string qualifiedName) {
UnderlyingTypes::nodeHasUnderlyingType(this, qualifiedName)
}
/**
* Gets the declaration of the type being referenced by this name.
*
* For example:
* ```ts
* class Foo {}
*
* type T = Foo;
* var x: T; // getTypeDefinition() maps T to the class Foo above
* ```
*
* Note that this has no result for function-style classes referenced from
* a JSDoc comment.
*/
TypeDefinition getTypeDefinition() { TypeResolution::trackType(result) = this }
/**
* Gets a class that this type refers to, after unfolding unions and intersections (but not subtyping).
*
* For example, the type of `x` maps to the class `C` in each example below:
* ```ts
* class C {}
*
* var x: C;
* var x: C | null;
* var x: C & { prop: string };
* ```
*/
DataFlow::ClassNode getAnUnderlyingClass() {
UnderlyingTypes::nodeHasUnderlyingClassType(this, result)
}
/**
* Holds if this type contains `string` or `any`, possibly wrapped in a promise.
*/
predicate hasUnderlyingStringOrAnyType() { TypeResolution::hasUnderlyingStringOrAnyType(this) }
/**
* Holds if this refers to a type that is considered untaintable (if actually enforced at runtime).
*
* Specifically, the types `number`, `boolean`, `null`, `undefined`, `void`, `never`, as well as literal types (`"foo"`)
* and enums and enum members have this property.
*/
predicate isSanitizingPrimitiveType() { TypeResolution::isSanitizingPrimitiveType(this) }
/**
* Holds if the given type is a Promise object. Does not hold for unions unless all parts of the union are promises.
*/
predicate isPromiseType() { TypeResolution::isPromiseType(this) }
}
/**
* Interface for accessing name-resolution info about expressions.
*/
class ExprNameBindingNode extends NameResolution::Node {
/**
* Holds if this refers a value exported by the given module, with the given
* qualified name. If the `qualifiedName` is empty, this refers to the module itself.
*
* For example, the type annotations below have the following name bindings:
* ```ts
* import * as f from "foo";
*
* var x = f; // hasQualifiedName("f", "")
* var x = f.x.y; // hasQualifiedName("f", "x.y")
* ```
*/
predicate hasQualifiedName(string moduleName, string qualifiedName) {
NameResolution::nodeRefersToModule(this, moduleName, qualifiedName)
}
/**
* Gets the class, or function acting as a class, referenced by this name.
*
* ```ts
* class Foo {}
* const T = Foo;
* var x = T; // getClassNode() maps T to the class Foo above
*
* function Bar() {}
* Bar.prototype.blah = function() {};
* const S = Bar;
* var x = S; // getClassNode() maps S to the function Bar above
* ```
*/
DataFlow::ClassNode getClassNode() { NameResolution::nodeRefersToClass(this, result) }
}

View File

@@ -17,6 +17,7 @@ module NameResolution {
* A node in a graph which we use to perform name and type resolution.
*/
class Node extends NodeBase {
/** Gets a string representation of this node. */
string toString() {
result = this.(AstNode).toString()
or
@@ -25,6 +26,7 @@ module NameResolution {
result = this.(JSDocTypeExpr).toString()
}
/** Gets the location of this node. */
Location getLocation() {
result = this.(AstNode).getLocation()
or
@@ -44,6 +46,9 @@ module NameResolution {
this instanceof Module
or
this instanceof NamespaceDefinition // `module {}` or `enum {}` statement
or
// A module wrapped in a promise. We model this as a module exporting the actual module in a property called `$$promise-content`.
this instanceof DynamicImportExpr
}
}
@@ -230,6 +235,19 @@ module NameResolution {
name = expr.getName() and
node2 = expr
)
or
exists(AwaitExpr await |
node1 = await.getOperand() and
name = "$$promise-content" and
node2 = await
)
or
exists(MethodCallExpr call |
call.getMethodName() = "then" and
node1 = call.getReceiver() and
name = "$$promise-content" and
node2 = call.getArgument(0).(Function).getParameter(0)
)
}
private signature module TypeResolutionInputSig {
@@ -332,6 +350,12 @@ module NameResolution {
)
or
storeToVariable(result, name, mod.(Closure::ClosureModule).getExportsVariable())
or
exists(DynamicImportExpr imprt |
mod = imprt and
name = "$$promise-content" and
result = imprt.getImportedPathExpr()
)
}
/**
@@ -408,6 +432,10 @@ module NameResolution {
*/
predicate trackModule = ValueFlow::TrackNode<ModuleLike>::track/1;
predicate trackClassValue = ValueFlow::TrackNode<ClassDefinition>::track/1;
predicate trackFunctionValue = ValueFlow::TrackNode<Function>::track/1;
/**
* Holds if `moduleName` appears to start with a package name, as opposed to a relative file import.
*/
@@ -509,4 +537,25 @@ module NameResolution {
qualifiedName = append(prefix, step)
)
}
pragma[nomagic]
predicate classHasGlobalName(DataFlow::ClassNode cls, string name) {
cls.flowsTo(AccessPath::getAnAssignmentTo(name)) and
not cls.getTopLevel().isExterns() // don't propagate externs classes
}
/**
* Holds if `node` refers to the given class.
*/
pragma[nomagic]
predicate nodeRefersToClass(Node node, DataFlow::ClassNode cls) {
exists(string name |
classHasGlobalName(cls, name) and
nodeRefersToModule(node, "global", name)
)
or
trackClassValue(cls.getAstNode()) = node
or
trackFunctionValue(cls.getAstNode()) = node
}
}

View File

@@ -4,8 +4,6 @@ private import semmle.javascript.internal.UnderlyingTypes
private import semmle.javascript.dataflow.internal.sharedlib.SummaryTypeTracker as SummaryTypeTracker
module TypeResolution {
predicate trackClassValue = ValueFlow::TrackNode<ClassDefinition>::track/1;
predicate trackType = TypeFlow::TrackNode<TypeDefinition>::track/1;
/**
@@ -24,8 +22,6 @@ module TypeResolution {
)
}
predicate trackFunctionValue = ValueFlow::TrackNode<Function>::track/1;
/**
* Gets the representative for the type containing the given member.
*
@@ -134,6 +130,13 @@ module TypeResolution {
or
SummaryTypeTracker::basicLoadStep(object.(AST::ValueNode).flow(),
member.(AST::ValueNode).flow(), contents)
or
exists(IndexExpr index |
not exists(index.getPropertyName()) and
object = index.getBase() and
member = index and
contents = DataFlow::ContentSet::arrayElement()
)
}
predicate callTarget(InvokeExpr call, Function target) {

View File

@@ -119,10 +119,4 @@ module UnderlyingTypes {
// The caller is responsible for handling the class hierarchy.
)
}
pragma[nomagic]
private predicate classHasGlobalName(DataFlow::ClassNode cls, string name) {
cls.flowsTo(AccessPath::getAnAssignmentTo(name)) and
not cls.getTopLevel().isExterns() // don't propagate externs classes
}
}

View File

@@ -45,20 +45,22 @@ string getKind(MemberDeclaration m) {
/**
* A call-signature that originates from a MethodSignature in the AST.
*/
private class MethodCallSig extends CallSignatureType {
string name;
private class MethodCallSig extends Function {
private MethodSignature signature;
MethodCallSig() {
exists(MethodSignature sig |
this = sig.getBody().getCallSignature() and
name = sig.getName()
)
MethodCallSig() { this = signature.getBody() }
int getNumOptionalParameter() {
result = count(Parameter p | p = this.getParameter(_) and p.isDeclaredOptional())
}
/**
* Gets the name of any member that has this signature.
*/
string getName() { result = name }
int getNumRequiredParameter() {
result = count(Parameter p | p = this.getParameter(_) and not p.isDeclaredOptional())
}
SignatureKind getKind() { result = SignatureKind::function() }
TypeExpr getTypeParameterBound(int i) { result = this.getTypeParameter(i).getBound() }
}
pragma[noinline]
@@ -75,6 +77,7 @@ private MethodCallSig getMethodCallSigWithFingerprint(
/**
* Holds if the two call signatures could be overloads of each other and have the same parameter types.
*/
pragma[inline]
predicate matchingCallSignature(MethodCallSig method, MethodCallSig other) {
other =
getMethodCallSigWithFingerprint(method.getName(), method.getNumOptionalParameter(),
@@ -109,6 +112,16 @@ private MethodSignature getMethodSignatureWithFingerprint(
result.getBody().getNumParameter() = numParameters
}
bindingset[t1, t2]
pragma[inline_late]
private predicate sameType(TypeExpr t1, TypeExpr t2) {
t1.(PredefinedTypeExpr).getName() = t2.(PredefinedTypeExpr).getName()
or
t1 instanceof ThisTypeExpr and t2 instanceof ThisTypeExpr
or
t1.(LocalTypeAccess).getLocalTypeName() = t2.(LocalTypeAccess).getLocalTypeName()
}
/**
* Holds if the two method signatures are overloads of each other and have the same parameter types.
*/
@@ -122,14 +135,13 @@ predicate signaturesMatch(MethodSignature method, MethodSignature other) {
not exists(method.getBody().getThisTypeAnnotation()) and
not exists(other.getBody().getThisTypeAnnotation())
or
method.getBody().getThisTypeAnnotation().getType() =
other.getBody().getThisTypeAnnotation().getType()
sameType(method.getBody().getThisTypeAnnotation(), other.getBody().getThisTypeAnnotation())
) and
// The types are compared in matchingCallSignature. This is a consistency check that the textual representation of the type-annotations are somewhat similar.
forall(int i | i in [0 .. -1 + method.getBody().getNumParameter()] |
getParameterTypeAnnotation(method, i) = getParameterTypeAnnotation(other, i)
) and
matchingCallSignature(method.getBody().getCallSignature(), other.getBody().getCallSignature())
matchingCallSignature(method.getBody(), other.getBody())
}
from ClassOrInterface decl, string name, MethodSignature previous, MethodSignature unreachable

View File

@@ -11,7 +11,6 @@
*/
import javascript
private import semmle.javascript.internal.TypeResolution
/**
* Holds if `call` is a call to an `async` function.
@@ -30,7 +29,7 @@ predicate isPromise(DataFlow::SourceNode node, boolean nullable) {
isAsyncCall(node, nullable)
or
not isAsyncCall(node, _) and
TypeResolution::valueHasPromiseType(node.asExpr()) and
node.asExpr().getTypeBinding().isPromiseType() and
nullable = true
}

View File

@@ -0,0 +1,9 @@
---
category: breaking
---
* The `Type` and `Symbol` classes have been deprecated and will be empty in newly extracted databases, since the TypeScript extractor no longer populates them.
This is a breaking change for custom queries that explicitly relied on these classes.
Such queries will still compile, but with deprecation warnings, and may have different query results due to type information no longer being available.
We expect most custom queries will not be affected, however. If a custom query has no deprecation warnings, it should not be affected by this change.
Uses of `getType()` should be rewritten to use the new `getTypeBinding()` or `getNameBinding()` APIs instead.
If the new API is not sufficient, please consider opening an issue in `github/codeql` describing your use-case.

View File

@@ -0,0 +1,6 @@
---
category: majorAnalysis
---
* The TypeScript extractor no longer relies on the TypeScript compiler for extracting type information.
Instead, the information we need from types is now derived by an algorithm written in QL.
This results in more robust extraction with faster extraction times, in some cases significantly faster.

View File

@@ -1,17 +0,0 @@
/**
* @name Typed expressions
* @description The number of expressions for which the TypeScript extractor could
* extract a type other than 'any'.
* @kind metric
* @metricType project
* @metricAggregate sum
* @tags meta
* @id js/meta/typed-expressions
*/
import javascript
import meta.MetaMetrics
predicate isProperType(Type t) { not t instanceof AnyType }
select projectRoot(), count(Expr e | isProperType(e.getType()))

View File

@@ -1,17 +0,0 @@
arrayTypes
| [number, string] | `string \| number` |
| number[] | `number` |
| readonly T[] | `T` |
| readonly number[] | `number` |
| readonly number[][] | `number[]` |
numberIndexTypes
| NumberIndexable | object |
| [number, string] | string \| number |
| number[] | number |
| readonly T[] | T |
| readonly number[] | number |
| readonly number[][] | number[] |
| string | string |
stringIndexTypes
| StringIndexable | object |
tupleTypes

View File

@@ -1,11 +0,0 @@
import javascript
query predicate arrayTypes(ArrayType array, string elem) {
elem = "`" + array.getArrayElementType() + "`"
}
query predicate numberIndexTypes(Type type, Type numType) { type.getNumberIndexType() = numType }
query predicate stringIndexTypes(Type type, Type strType) { type.getStringIndexType() = strType }
query predicate tupleTypes(TupleType type, Type arrType) { arrType = type.getUnderlyingArrayType() }

View File

@@ -1,19 +0,0 @@
let plain: number[];
let readonly: ReadonlyArray<number>;
let tuple: [number, string];
interface NumberIndexable {
length: number;
[n: number]: object;
}
interface StringIndexable {
length: number;
[n: string]: object;
}
let numberIndexable: NumberIndexable;
let stringIndexable: StringIndexable;
let readonlySyntax: readonly number[];
let readonlySyntax2: readonly number[][];

View File

@@ -1,15 +0,0 @@
| CEverything | CGenericBase |
| CEverything | IBase |
| CEverything | IGenericSub |
| CGenericSub | CGenericBase |
| CImplements | IBase |
| CImplementsGeneric | IGenericBase |
| CImplementsString | IGenericBase |
| CStringSub | CGenericBase |
| CSub | CBase |
| IEmptySub | IEmpty |
| IGenericSub | IGenericBase |
| IMulti | IBase |
| IMulti | IGenericBase |
| IStringSub | IGenericBase |
| ISub | IBase |

View File

@@ -1,4 +0,0 @@
import javascript
from TypeName typename
select typename.getName(), typename.getABaseTypeName().getName()

View File

@@ -1,17 +0,0 @@
| CBase | CBase |
| CEverything | CEverything<S, T> |
| CGenericBase | CGenericBase<T> |
| CGenericSub | CGenericSub<Q> |
| CImplements | CImplements |
| CImplementsGeneric | CImplementsGeneric<Q> |
| CImplementsString | CImplementsString |
| CStringSub | CStringSub |
| CSub | CSub |
| IBase | IBase |
| IEmpty | IEmpty |
| IEmptySub | IEmptySub |
| IGenericBase | IGenericBase<T> |
| IGenericSub | IGenericSub<Q> |
| IMulti | IMulti<T> |
| IStringSub | IStringSub |
| ISub | ISub |

View File

@@ -1,4 +0,0 @@
import javascript
from TypeName tn
select tn.getName(), tn.getType()

View File

@@ -1,39 +0,0 @@
interface IBase {
x: string;
}
interface ISub extends IBase {
y: string;
}
interface IGenericBase<T> {
w: T;
}
interface IStringSub extends IGenericBase<string> {}
interface IGenericSub<Q> extends IGenericBase<Q> {}
class CBase {}
class CSub extends CBase {}
class CGenericBase<T> {}
class CStringSub extends CGenericBase<string> {}
class CGenericSub<Q> extends CGenericBase<Q> {}
interface IMulti<T> extends IBase, IGenericBase<T> {}
abstract class CImplements implements IBase {
x: string;
}
abstract class CImplementsString implements IGenericBase<string> {
w: string;
}
abstract class CImplementsGeneric<Q> implements IGenericBase<Q> {
w: Q;
}
abstract class CEverything<S,T> extends CGenericBase<S> implements IGenericSub<T>, IBase {
x: string;
w: T;
}
interface IEmpty {}
interface IEmptySub extends IEmpty {}

View File

@@ -4,14 +4,7 @@ exprFloatValue
| tst.ts:3:25:3:56 | 1000000 ... 000000n | 1.0E30 |
exprIntValue
| tst.ts:1:25:1:28 | 100n | 100 |
exprWithBigIntType
| tst.ts:1:5:1:11 | hundred |
| tst.ts:2:5:2:12 | bigValue |
| tst.ts:3:5:3:20 | bigNegativeValue |
| tst.ts:5:5:5:14 | bigintType |
literalTypeExprIntValue
| tst.ts:6:24:6:28 | 1000n | 1000 |
typeExpr
| tst.ts:5:24:5:29 | bigint |
typeIntValue
| 1000n | 1000 |

View File

@@ -4,12 +4,8 @@ query predicate exprFloatValue(BigIntLiteral literal, float f) { f = literal.get
query predicate exprIntValue(BigIntLiteral literal, int i) { i = literal.getIntValue() }
query predicate exprWithBigIntType(Expr e) { e.getType() instanceof BigIntType }
query predicate literalTypeExprIntValue(BigIntLiteralTypeExpr type, int val) {
val = type.getIntValue()
}
query predicate typeExpr(TypeExpr type) { type.isBigInt() }
query predicate typeIntValue(BigIntLiteralType type, int i) { type.getIntValue() = i }

View File

@@ -1,24 +0,0 @@
| tst.ts:52:3:52:23 | obj.sim ... od(str) | (x: string): number | 0 |
| tst.ts:53:3:53:24 | obj.gen ... od(str) | (x: string): string | 0 |
| tst.ts:54:3:54:24 | obj.gen ... od(num) | (x: number): number | 0 |
| tst.ts:55:3:55:27 | obj.ove ... od(num) | (x: number): number | 0 |
| tst.ts:56:3:56:27 | obj.ove ... od(str) | (x: string): string | 1 |
| tst.ts:57:3:57:26 | obj.ove ... hod([]) | (x: any): any | 2 |
| tst.ts:58:3:58:36 | obj.gen ... ([num]) | (x: number[]): number | 0 |
| tst.ts:59:3:59:39 | obj.gen ... : str}) | (x: Box<string>): string | 1 |
| tst.ts:60:3:60:34 | obj.gen ... od(num) | (x: any): any | 2 |
| tst.ts:64:3:64:23 | obj.sim ... od(str) | (x: string): number | 0 |
| tst.ts:65:3:65:24 | obj.gen ... od(str) | (x: string): string | 0 |
| tst.ts:66:3:66:24 | obj.gen ... od(num) | (x: number): number | 0 |
| tst.ts:67:3:67:27 | obj.ove ... od(num) | (x: number): number | 0 |
| tst.ts:68:3:68:27 | obj.ove ... od(str) | (x: string): string | 1 |
| tst.ts:69:3:69:36 | obj.gen ... ([num]) | (x: number[]): number | 0 |
| tst.ts:70:3:70:39 | obj.gen ... : str}) | (x: Box<string>): string | 1 |
| tst.ts:74:3:74:28 | new Sim ... or(str) | new (x: string): SimpleConstructor | 0 |
| tst.ts:75:3:75:29 | new Gen ... or(str) | new (x: string): GenericConstructor<string> | 0 |
| tst.ts:76:3:76:29 | new Gen ... or(num) | new (x: number): GenericConstructor<number> | 0 |
| tst.ts:77:3:77:37 | new Ove ... m, num) | new (x: number, y: number): OverloadedConstructor | 0 |
| tst.ts:78:3:78:37 | new Ove ... r, str) | new (x: string, y: string): OverloadedConstructor | 1 |
| tst.ts:79:3:79:48 | new Gen ... [str]) | new (x: string[], y: string[]): GenericOverloadedConstructor<string> | 0 |
| tst.ts:80:3:80:54 | new Gen ... : num}) | new (x: Box<number>, y: Box<number>): GenericOverloadedConstructor<nu... | 1 |
| tst.ts:84:10:84:24 | callback("str") | (x: string): U | 0 |

View File

@@ -1,4 +0,0 @@
import javascript
from InvokeExpr invoke
select invoke, invoke.getResolvedSignature(), invoke.getResolvedOverloadIndex()

View File

@@ -1,36 +0,0 @@
| tst.ts:52:3:52:23 | obj.sim ... od(str) | TestInterface.simpleMethod in global scope | simpleM ... number; |
| tst.ts:53:3:53:24 | obj.gen ... od(str) | TestInterface.genericMethod in global scope | generic ... T): T; |
| tst.ts:54:3:54:24 | obj.gen ... od(num) | TestInterface.genericMethod in global scope | generic ... T): T; |
| tst.ts:55:3:55:27 | obj.ove ... od(num) | TestInterface.overloadedMethod in global scope | overloa ... ): any; |
| tst.ts:55:3:55:27 | obj.ove ... od(num) | TestInterface.overloadedMethod in global scope | overloa ... number; |
| tst.ts:55:3:55:27 | obj.ove ... od(num) | TestInterface.overloadedMethod in global scope | overloa ... string; |
| tst.ts:56:3:56:27 | obj.ove ... od(str) | TestInterface.overloadedMethod in global scope | overloa ... ): any; |
| tst.ts:56:3:56:27 | obj.ove ... od(str) | TestInterface.overloadedMethod in global scope | overloa ... number; |
| tst.ts:56:3:56:27 | obj.ove ... od(str) | TestInterface.overloadedMethod in global scope | overloa ... string; |
| tst.ts:57:3:57:26 | obj.ove ... hod([]) | TestInterface.overloadedMethod in global scope | overloa ... ): any; |
| tst.ts:57:3:57:26 | obj.ove ... hod([]) | TestInterface.overloadedMethod in global scope | overloa ... number; |
| tst.ts:57:3:57:26 | obj.ove ... hod([]) | TestInterface.overloadedMethod in global scope | overloa ... string; |
| tst.ts:58:3:58:36 | obj.gen ... ([num]) | TestInterface.genericOverloadedMethod in global scope | generic ... ): any; |
| tst.ts:58:3:58:36 | obj.gen ... ([num]) | TestInterface.genericOverloadedMethod in global scope | generic ... T>): T; |
| tst.ts:58:3:58:36 | obj.gen ... ([num]) | TestInterface.genericOverloadedMethod in global scope | generic ... []): T; |
| tst.ts:59:3:59:39 | obj.gen ... : str}) | TestInterface.genericOverloadedMethod in global scope | generic ... ): any; |
| tst.ts:59:3:59:39 | obj.gen ... : str}) | TestInterface.genericOverloadedMethod in global scope | generic ... T>): T; |
| tst.ts:59:3:59:39 | obj.gen ... : str}) | TestInterface.genericOverloadedMethod in global scope | generic ... []): T; |
| tst.ts:60:3:60:34 | obj.gen ... od(num) | TestInterface.genericOverloadedMethod in global scope | generic ... ): any; |
| tst.ts:60:3:60:34 | obj.gen ... od(num) | TestInterface.genericOverloadedMethod in global scope | generic ... T>): T; |
| tst.ts:60:3:60:34 | obj.gen ... od(num) | TestInterface.genericOverloadedMethod in global scope | generic ... []): T; |
| tst.ts:64:3:64:23 | obj.sim ... od(str) | TestClass.simpleMethod in global scope | simpleM ... ength } |
| tst.ts:65:3:65:24 | obj.gen ... od(str) | TestClass.genericMethod in global scope | generic ... rn x; } |
| tst.ts:66:3:66:24 | obj.gen ... od(num) | TestClass.genericMethod in global scope | generic ... rn x; } |
| tst.ts:67:3:67:27 | obj.ove ... od(num) | TestClass.overloadedMethod in global scope | overloa ... number; |
| tst.ts:67:3:67:27 | obj.ove ... od(num) | TestClass.overloadedMethod in global scope | overloa ... rn x; } |
| tst.ts:67:3:67:27 | obj.ove ... od(num) | TestClass.overloadedMethod in global scope | overloa ... string; |
| tst.ts:68:3:68:27 | obj.ove ... od(str) | TestClass.overloadedMethod in global scope | overloa ... number; |
| tst.ts:68:3:68:27 | obj.ove ... od(str) | TestClass.overloadedMethod in global scope | overloa ... rn x; } |
| tst.ts:68:3:68:27 | obj.ove ... od(str) | TestClass.overloadedMethod in global scope | overloa ... string; |
| tst.ts:69:3:69:36 | obj.gen ... ([num]) | TestClass.genericOverloadedMethod in global scope | generic ... T>): T; |
| tst.ts:69:3:69:36 | obj.gen ... ([num]) | TestClass.genericOverloadedMethod in global scope | generic ... []): T; |
| tst.ts:69:3:69:36 | obj.gen ... ([num]) | TestClass.genericOverloadedMethod in global scope | generic ... null; } |
| tst.ts:70:3:70:39 | obj.gen ... : str}) | TestClass.genericOverloadedMethod in global scope | generic ... T>): T; |
| tst.ts:70:3:70:39 | obj.gen ... : str}) | TestClass.genericOverloadedMethod in global scope | generic ... []): T; |
| tst.ts:70:3:70:39 | obj.gen ... : str}) | TestClass.genericOverloadedMethod in global scope | generic ... null; } |

View File

@@ -1,11 +0,0 @@
import javascript
string getTarget(InvokeExpr e) {
result = e.getResolvedCallee().toString()
or
not exists(e.getResolvedCallee()) and
result = "no concrete target"
}
from InvokeExpr invoke
select invoke, invoke.getResolvedCalleeName(), getTarget(invoke)

View File

@@ -1,85 +0,0 @@
interface Box<T> { x: T }
interface TestInterface {
simpleMethod(x: string): number;
genericMethod<T>(x: T): T;
overloadedMethod(x: number): number;
overloadedMethod(x: string): string;
overloadedMethod(x: any): any;
genericOverloadedMethod<T>(x: T[]): T;
genericOverloadedMethod<T>(x: Box<T>): T;
genericOverloadedMethod(x: any): any;
}
class TestClass {
simpleMethod(x: string): number { return x.length }
genericMethod<T>(x: T): T { return x; }
overloadedMethod(x: number): number;
overloadedMethod(x: string): string;
overloadedMethod(x: any): any { return x; }
genericOverloadedMethod<T>(x: T[]): T;
genericOverloadedMethod<T>(x: Box<T>): T;
genericOverloadedMethod(x: any): any { return x.x || x[0] || null; }
}
class SimpleConstructor {
constructor(x: string) {}
}
class GenericConstructor<T> {
constructor(x: T) {}
}
class OverloadedConstructor {
constructor(x: number, y: number);
constructor(x: string, y: string);
constructor(x: any, y: any) {}
}
class GenericOverloadedConstructor<T> {
constructor(x: T[], y: T[]);
constructor(x: Box<T>, y: Box<T>);
constructor(x: any, y: any) {}
}
function useTestInterface(obj: TestInterface, str: string, num: number) {
obj.simpleMethod(str);
obj.genericMethod(str);
obj.genericMethod(num);
obj.overloadedMethod(num);
obj.overloadedMethod(str);
obj.overloadedMethod([]);
obj.genericOverloadedMethod([num]);
obj.genericOverloadedMethod({x: str});
obj.genericOverloadedMethod(num);
}
function useTestClass(obj: TestClass, str: string, num: number) {
obj.simpleMethod(str);
obj.genericMethod(str);
obj.genericMethod(num);
obj.overloadedMethod(num);
obj.overloadedMethod(str);
obj.genericOverloadedMethod([num]);
obj.genericOverloadedMethod({x: str});
}
function testConstructors(str: string, num: number) {
new SimpleConstructor(str);
new GenericConstructor(str);
new GenericConstructor(num);
new OverloadedConstructor(num, num);
new OverloadedConstructor(str, str);
new GenericOverloadedConstructor([str], [str]);
new GenericOverloadedConstructor({x: num}, {x: num});
}
function testCallback<U>(callback: (x: string) => U): U {
return callback("str");
}

View File

@@ -1,131 +0,0 @@
test_ExprSignature
| tst.ts:2:4:2:4 | x | number |
| tst.ts:6:4:6:4 | x | number |
| tst.ts:7:4:7:4 | x | string |
| tst.ts:8:4:8:4 | x | any |
| tst.ts:12:8:12:8 | x | number |
| tst.ts:16:8:16:8 | x | number |
| tst.ts:17:8:17:8 | x | any |
| tst.ts:21:3:21:28 | method( ... string; | (x: number): string |
| tst.ts:21:10:21:10 | x | number |
| tst.ts:23:3:23:38 | overloa ... number; | (x: any): any |
| tst.ts:23:3:23:38 | overloa ... number; | (x: number): number |
| tst.ts:23:3:23:38 | overloa ... number; | (x: string): string |
| tst.ts:23:20:23:20 | x | number |
| tst.ts:24:3:24:38 | overloa ... string; | (x: any): any |
| tst.ts:24:3:24:38 | overloa ... string; | (x: number): number |
| tst.ts:24:3:24:38 | overloa ... string; | (x: string): string |
| tst.ts:24:20:24:20 | x | string |
| tst.ts:25:3:25:32 | overloa ... ): any; | (x: any): any |
| tst.ts:25:3:25:32 | overloa ... ): any; | (x: number): number |
| tst.ts:25:3:25:32 | overloa ... ): any; | (x: string): string |
| tst.ts:25:20:25:20 | x | any |
| tst.ts:28:5:28:5 | m | Method |
| tst.ts:29:1:29:1 | m | Method |
| tst.ts:29:1:29:8 | m.method | (x: number): string |
| tst.ts:29:1:29:12 | m.method(42) | string |
| tst.ts:29:10:29:11 | 42 | 42 |
| tst.ts:30:1:30:1 | m | Method |
| tst.ts:30:1:30:18 | m.overloadedMethod | (x: any): any |
| tst.ts:30:1:30:18 | m.overloadedMethod | (x: number): number |
| tst.ts:30:1:30:18 | m.overloadedMethod | (x: string): string |
| tst.ts:30:1:30:25 | m.overl ... ("foo") | string |
| tst.ts:30:20:30:24 | "foo" | "foo" |
| tst.ts:33:3:33:10 | callback | (x: number): string |
| tst.ts:33:13:33:33 | (x: num ... string | (x: number): string |
| tst.ts:33:14:33:14 | x | number |
| tst.ts:37:3:37:18 | method(x: T): T; | (x: T): T |
| tst.ts:37:10:37:10 | x | T |
| tst.ts:40:10:40:12 | foo | (g: Generic<string>): string |
| tst.ts:40:14:40:14 | g | Generic<string> |
| tst.ts:41:10:41:10 | g | Generic<string> |
| tst.ts:41:10:41:17 | g.method | (x: string): string |
| tst.ts:41:10:41:24 | g.method("foo") | string |
| tst.ts:41:19:41:23 | "foo" | "foo" |
| tst.ts:44:15:44:15 | C | C |
| tst.ts:45:3:45:25 | constru ... tring); | any |
| 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 |
| tst.ts:7:3:7:22 | (x: string): string; | (x: string): string |
| tst.ts:8:3:8:16 | (x: any): any; | (x: any): any |
| tst.ts:12:3:12:23 | new (x: ... ): any; | new (x: number): any |
| tst.ts:16:3:16:37 | new (x: ... ewable; | new (x: number): OverloadedNewable |
| tst.ts:17:3:17:20 | new (x: any): any; | new (x: any): any |
| tst.ts:21:3:21:28 | method( ... string; | (x: number): string |
| tst.ts:23:3:23:38 | overloa ... number; | (x: number): number |
| tst.ts:24:3:24:38 | overloa ... string; | (x: string): string |
| tst.ts:25:3:25:32 | overloa ... ): any; | (x: any): any |
| tst.ts:33:13:33:33 | (x: num ... string | (x: number): string |
| tst.ts:37:3:37:18 | method(x: T): T; | (x: T): T |
| 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
| (...y: string[]): any | string |
| (x: number, ...y: string[]): any | string |
| new (...y: string[]): any | string |
| new (x: number, ...y: string[]): any | string |
test_getRestParameterArray
| (...y: string[]): any | string[] |
| (x: number, ...y: string[]): any | string[] |
| new (...y: string[]): any | string[] |
| new (x: number, ...y: string[]): any | string[] |
test_RestSig_getParameter
| (...y: string[]): any | 0 | y | string |
| (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
| (...y: string[]): any | 0 |
| (x: number, ...y: string[]): any | 1 |
| new (...y: string[]): any | 0 |
| new (x: number, ...y: string[]): any | 1 |

View File

@@ -1,41 +0,0 @@
import javascript
string getASignatureOrElseType(Type t) {
result = t.getASignature(_).toString()
or
not exists(t.getASignature(_)) and
result = t.toString()
}
query predicate test_ExprSignature(Expr expr, string type) {
not exists(MethodDeclaration decl | decl.getNameExpr() = expr) and
not exists(DotExpr dot | expr = dot.getPropertyNameExpr()) and
type = getASignatureOrElseType(expr.getType())
}
query predicate test_TypeReferenceSig(
TypeReference type, SignatureKind kind, int n, CallSignatureType sig
) {
sig = type.getSignature(kind, n)
}
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

@@ -1,65 +0,0 @@
interface Callable {
(x: number): string;
}
interface OverloadedCallable {
(x: number): number;
(x: string): string;
(x: any): any;
}
interface Newable {
new (x: number): any;
}
interface OverloadedNewable {
new (x: number): OverloadedNewable;
new (x: any): any;
}
interface Method {
method(x: number): string;
overloadedMethod(x: number): number;
overloadedMethod(x: string): string;
overloadedMethod(x: any): any;
}
let m: Method;
m.method(42);
m.overloadedMethod("foo");
interface FunctionTypeField {
callback: (x: number) => string;
}
interface Generic<T> {
method(x: T): T;
}
function foo(g: Generic<string>) {
return g.method("foo");
}
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

@@ -1,8 +0,0 @@
| client1.ts:4:9:4:19 | F.Component | Component in module 'framework1' |
| client1.ts:5:9:5:29 | Util.De ... mponent | Util.DefaultComponent in global scope |
| client2.ts:4:9:4:19 | F.Component | Component in module 'framework2' |
| client2.ts:5:9:5:30 | Util2.D ... mponent | Util2.DefaultComponent in global scope |
| client2_lazy.ts:4:9:4:19 | F.Component | Component in module 'framework2' |
| client2_lazy.ts:5:9:5:30 | Util2.D ... mponent | Util2.DefaultComponent in global scope |
| declare-module-client2.ts:5:8:5:8 | C | C in module 'foo' |
| declare-module-client.ts:5:8:5:8 | C | C in module 'foo' |

View File

@@ -1,4 +0,0 @@
import javascript
from TypeAccess access
select access, access.getTypeName()

View File

@@ -1,34 +1,6 @@
classDeclaration
| test.vue:3:18:5:3 | class M ... er;\\n } |
| test_tsx.vue:3:18:5:3 | class M ... er;\\n } |
exprType
| htmlfile.html:4:22:4:24 | foo | () => void |
| htmlfile.html:4:33:4:41 | "./other" | any |
| htmlfile.html:5:17:5:22 | result | number[] |
| htmlfile.html:5:26:5:28 | foo | () => void |
| htmlfile.html:5:26:5:30 | foo() | void |
| htmlfile.html:5:26:5:42 | foo() as number[] | number[] |
| other.ts:1:8:1:16 | Component | typeof default in test.vue |
| other.ts:1:23:1:34 | "./test.vue" | any |
| other.ts:2:8:2:19 | ComponentTsx | typeof default in test_tsx.vue |
| other.ts:2:26:2:41 | "./test_tsx.vue" | any |
| other.ts:4:1:4:15 | new Component() | MyComponent |
| other.ts:4:5:4:13 | Component | typeof default in test.vue |
| other.ts:5:1:5:18 | new ComponentTsx() | MyComponentTsx |
| other.ts:5:5:5:16 | ComponentTsx | typeof default in test_tsx.vue |
| other.ts:7:17:7:19 | foo | () => void |
| test.vue:2:15:2:19 | other | typeof other.ts |
| test.vue:2:26:2:34 | "./other" | any |
| test.vue:3:24:3:34 | MyComponent | MyComponent |
| test.vue:4:7:4:7 | x | number |
| test_tsx.vue:2:15:2:19 | other | typeof other.ts |
| test_tsx.vue:2:26:2:34 | "./other" | any |
| test_tsx.vue:3:24:3:37 | MyComponentTsx | MyComponentTsx |
| test_tsx.vue:4:7:4:7 | x | number |
symbols
| other.ts:1:1:8:0 | <toplevel> | other.ts |
| test.vue:2:3:6:0 | <toplevel> | test.vue |
| test_tsx.vue:2:3:6:0 | <toplevel> | test_tsx.vue |
importTarget
| htmlfile.html:4:13:4:42 | import ... other"; | other.ts:1:1:8:0 | <toplevel> |
| other.ts:1:1:1:35 | import ... t.vue"; | test.vue:2:3:6:0 | <toplevel> |

View File

@@ -2,8 +2,4 @@ import javascript
query ClassDefinition classDeclaration() { any() }
query Type exprType(Expr e) { result = e.getType() }
query predicate symbols(Module mod, CanonicalName name) { ast_node_symbol(mod, name) }
query predicate importTarget(Import imprt, Module mod) { imprt.getImportedModule() = mod }

View File

@@ -1,77 +0,0 @@
| After |
| AfterX |
| Before |
| BeforeX |
| Box<Expand<T[]>> |
| Box<S> |
| Box<S> |
| Box<T[]> |
| Box<number> |
| C<T> |
| C<T[]> |
| Expand<T> |
| Expand<T[]> |
| ExpandUsingObjectLiteral<T> |
| ExpandUsingObjectLiteral<T[]> |
| Expansive<T> |
| Expansive<T> |
| Expansive<T[]> |
| Expansive<T[]> |
| Expansive<number> |
| Expansive<string> |
| ExpansiveA<S> |
| ExpansiveA<S> |
| ExpansiveA<T> |
| ExpansiveA<T> |
| ExpansiveB<S> |
| ExpansiveB<S> |
| ExpansiveB<T> |
| ExpansiveB<T[]> |
| ExpansiveB<T[]> |
| ExpansiveB<number> |
| ExpansiveByInference<T> |
| ExpansiveByInference<T[]> |
| ExpansiveC<T> |
| ExpansiveC<T> |
| ExpansiveC<T> |
| ExpansiveC<T[]> |
| ExpansiveC<T[]> |
| ExpansiveC<number> |
| ExpansiveConstructSignature<T> |
| ExpansiveConstructSignature<T[]> |
| ExpansiveD<T> |
| ExpansiveD<T> |
| ExpansiveD<T> |
| ExpansiveD<T> |
| ExpansiveFunctionType<T> |
| ExpansiveFunctionType<T[]> |
| ExpansiveMethod<T> |
| ExpansiveMethod<T[]> |
| ExpansiveParameter<T> |
| ExpansiveParameter<T[]> |
| ExpansiveSignature<T> |
| ExpansiveSignature<T[]> |
| ExpansiveSignatureTypeBound<T> |
| ExpansiveSignatureTypeBound<T[]> |
| ExpansiveX<T> |
| ExpansiveX<T[]> |
| NonExpansive<Box<number>> |
| NonExpansive<T> |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |
| T[] |

View File

@@ -1,4 +0,0 @@
import javascript
from TypeReference type
select type

View File

@@ -1 +0,0 @@
export let x = 1;

View File

@@ -1,8 +0,0 @@
import * as dummy from "./dummy";
class ExpansiveByInference<T> {
x: T;
y = new ExpansiveByInference([this.x]); // Inferred to be `ExpansiveByInference<T[]>`
constructor(arg: T) {}
}

View File

@@ -1,5 +0,0 @@
import * as dummy from "./dummy";
class C<T> {
x: C<T[]>;
}

View File

@@ -1,7 +0,0 @@
import * as dummy from "./dummy";
interface ExpandUsingObjectLiteral<T> {
x: {
foo: ExpandUsingObjectLiteral<T[]>
}
}

View File

@@ -1,25 +0,0 @@
import * as dummy from "./dummy";
interface ExpansiveSignature<T> {
x: { (): ExpansiveSignature<T[]>; }
}
interface ExpansiveParameter<T> {
x: { (param: ExpansiveParameter<T[]>): void; }
}
interface ExpansiveConstructSignature<T> {
x: { new(): ExpansiveConstructSignature<T[]>; }
}
interface ExpansiveMethod<T> {
method(): ExpansiveMethod<T[]>;
}
interface ExpansiveFunctionType<T> {
x: () => ExpansiveFunctionType<T[]>;
}
interface ExpansiveSignatureTypeBound<T> {
foo : { <G extends ExpansiveSignatureTypeBound<T[]>>(x: G): G };
}

View File

@@ -1,13 +0,0 @@
import * as dummy from "./dummy";
interface Before {
x: Expansive<number>;
}
interface Expansive<T> {
x: Expansive<T[]>;
}
interface After {
x: Expansive<string>;
}

View File

@@ -1,17 +0,0 @@
import * as dummy from "./dummy";
interface ExpansiveA<T> {
x: ExpansiveB<T[]>;
}
interface ExpansiveB<S> {
x: ExpansiveA<S>;
}
interface ExpansiveC<T> {
x: ExpansiveD<T>;
}
interface ExpansiveD<T> {
x: ExpansiveC<T[]>;
}

View File

@@ -1,24 +0,0 @@
import * as dummy from "./dummy";
// The expansive edge may be preceded by non-expansive edges.
interface ExpansiveA<T> {
a: ExpansiveB<T>;
b: ExpansiveB<number>;
x: ExpansiveB<T[]>;
}
interface ExpansiveB<S> {
x: ExpansiveA<S>;
}
interface ExpansiveC<T> {
x: ExpansiveD<T>;
}
interface ExpansiveD<T> {
a: ExpansiveC<T>;
b: ExpansiveC<number>;
x: ExpansiveC<T[]>;
}

View File

@@ -1,12 +0,0 @@
import * as dummy from "./dummy";
// Box is not expansive by itself but expansions may go "through" it.
interface Box<S> {
x: S;
}
// A too simple algorithm might classify this as expansive.
interface NonExpansive<T> {
x: NonExpansive<Box<number>>;
y: Box<T[]>;
}

View File

@@ -1,5 +0,0 @@
import * as dummy from "./dummy";
interface Expansive<T> {
x: Expansive<T[]>;
}

View File

@@ -1,10 +0,0 @@
import * as dummy from "./dummy";
interface Expand<T> {
x: Box<Expand<T[]>>
}
// Box is not expansive by itself but expansions may go "through" it.
interface Box<S> {
x: S;
}

View File

@@ -1,15 +0,0 @@
import * as dummy from "./dummy";
interface BeforeX {
x: number;
}
interface ExpansiveX<T> {
a: BeforeX;
x: ExpansiveX<T[]>;
b: BeforeX;
}
interface AfterX {
x: string;
}

View File

@@ -1,3 +0,0 @@
| B in module 'mylib' | A in module 'mylib' |
| C in module 'mylib' | B in module 'mylib' |
| D in module 'mylib' | C in module 'mylib' |

View File

@@ -1,5 +0,0 @@
import javascript
from TypeName tn
where tn.hasQualifiedName("mylib", _)
select tn, tn.getABaseTypeName()

View File

@@ -1,4 +0,0 @@
export interface A {}
export interface B extends A {}
export interface C extends B {}
export interface D extends C {}

View File

@@ -1 +0,0 @@
semmle-extractor-options:--exclude node_modules/**

View File

@@ -1,5 +0,0 @@
{
"compilerOptions": {
"baseUrl": "./"
}
}

View File

@@ -1,3 +0,0 @@
import { D } from "mylib";
export var foo: D = null;

View File

@@ -1,9 +0,0 @@
import { ExternalType1, Augmentation } from "esmodule";
declare module "esmodule" {
export interface Augmentation {
x: ExternalType1;
}
}
let x: Augmentation;

View File

@@ -1,16 +0,0 @@
import { ExternalType1, externalSymbol } from "esmodule";
function f(arg: ExternalType1) {
let y = arg.x; // y should be ExternalType2
}
let foo = 5;
let bar: { x: number };
interface InternalType {
x: number;
[externalSymbol]: number;
}
let symb = externalSymbol;

View File

@@ -1,7 +0,0 @@
import { OtherClass } from "esmodule/otherfile";
import { UtilClass } from "esmodule/util";
import { UtilExtraClass } from "esmodule/util/extra";
let c1 = new OtherClass();
let c2 = new UtilClass();
let c3 = new UtilExtraClass();

View File

@@ -1,3 +0,0 @@
/// <reference types="legacy"/>
let d = new LegacyGlobals.LegacySubclass();

View File

@@ -1,3 +0,0 @@
import { LegacyClass } from "legacy";
let c: LegacyClass;

View File

@@ -1,3 +0,0 @@
/// <reference types="modern"/>
let d = new ModernGlobals.ModernSubclass();

View File

@@ -1,3 +0,0 @@
import { ModernClass } from "modern";
let c: ModernClass;

View File

@@ -1,10 +0,0 @@
export interface ExternalType1 {
x: ExternalType2;
}
export interface ExternalType2 {
x: number;
y: number;
}
export const externalSymbol: unique symbol;

View File

@@ -1 +0,0 @@
export declare class OtherClass {}

View File

@@ -1 +0,0 @@
export declare class UtilExtraClass {}

View File

@@ -1 +0,0 @@
export declare class UtilClass {}

Some files were not shown because too many files have changed in this diff Show More