From 5064cd5d94a2e72fd19f8bd1dfb985293da6c5d6 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 12:52:39 +0200 Subject: [PATCH 01/47] JS: Exclude externs from CallGraph meta-query --- javascript/ql/src/meta/alerts/CallGraph.ql | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/meta/alerts/CallGraph.ql b/javascript/ql/src/meta/alerts/CallGraph.ql index 364d81e32c90..c721e72f6404 100644 --- a/javascript/ql/src/meta/alerts/CallGraph.ql +++ b/javascript/ql/src/meta/alerts/CallGraph.ql @@ -12,7 +12,10 @@ import javascript from DataFlow::Node invoke, Function f, string kind where - invoke.(DataFlow::InvokeNode).getACallee() = f and kind = "Call" - or - invoke.(DataFlow::PropRef).getAnAccessorCallee().getFunction() = f and kind = "Accessor call" + ( + invoke.(DataFlow::InvokeNode).getACallee() = f and kind = "Call" + or + invoke.(DataFlow::PropRef).getAnAccessorCallee().getFunction() = f and kind = "Accessor call" + ) and + not f.getTopLevel().isExterns() select invoke, kind + " to $@", f, f.describe() From 9fc0b8c9ccf616824988bb3a9ddfc4bdba79a974 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 11 Mar 2025 11:50:06 +0100 Subject: [PATCH 02/47] JS: Add ImportSpecifier.getImportDeclaration() --- javascript/ql/lib/semmle/javascript/ES2015Modules.qll | 3 +++ 1 file changed, 3 insertions(+) diff --git a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll index 379403eb0ee7..e7534449f55b 100644 --- a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll +++ b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll @@ -180,6 +180,9 @@ deprecated private class LiteralImportPath extends PathExpr, ConstantString { * ``` */ class ImportSpecifier extends Expr, @import_specifier { + /** Gets the import declaration in which this specifier appears. */ + ImportDeclaration getImportDeclaration() { result.getASpecifier() = this } + /** Gets the imported symbol; undefined for default and namespace import specifiers. */ Identifier getImported() { result = this.getChildExpr(0) } From 50e4ac82985f7315ccac039d9c0487edb1e965dd Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 12:58:54 +0200 Subject: [PATCH 03/47] JS: Do not ignore variables from ambient declarations --- .../extractor/src/com/semmle/js/extractor/ScopeManager.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/ScopeManager.java b/javascript/extractor/src/com/semmle/js/extractor/ScopeManager.java index e7306e77e016..43e70160fe8f 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/ScopeManager.java +++ b/javascript/extractor/src/com/semmle/js/extractor/ScopeManager.java @@ -426,7 +426,6 @@ public Void visit(Identifier nd, Void v) { // cases where we turn on the 'declKind' flags @Override public Void visit(FunctionDeclaration nd, Void v) { - if (nd.hasDeclareKeyword() && !isInTypeScriptDeclarationFile()) return null; // strict mode functions are block-scoped, non-strict mode ones aren't if (blockscope == isStrict) visit(nd.getId(), DeclKind.var); return null; @@ -434,7 +433,6 @@ public Void visit(FunctionDeclaration nd, Void v) { @Override public Void visit(ClassDeclaration nd, Void c) { - if (nd.hasDeclareKeyword() && !isInTypeScriptDeclarationFile()) return null; if (blockscope) visit(nd.getClassDef().getId(), DeclKind.varAndType); return null; } @@ -483,7 +481,6 @@ public Void visit(EnhancedForStatement nd, Void v) { @Override public Void visit(VariableDeclaration nd, Void v) { - if (nd.hasDeclareKeyword() && !isInTypeScriptDeclarationFile()) return null; // in block scoping mode, only process 'let'; in non-block scoping // mode, only process non-'let' if (blockscope == nd.isBlockScoped(ecmaVersion)) visit(nd.getDeclarations()); @@ -518,8 +515,7 @@ public Void visit(ClassBody nd, Void c) { @Override public Void visit(NamespaceDeclaration nd, Void c) { if (blockscope) return null; - boolean isAmbientOutsideDtsFile = nd.hasDeclareKeyword() && !isInTypeScriptDeclarationFile(); - boolean hasVariable = nd.isInstantiated() && !isAmbientOutsideDtsFile; + boolean hasVariable = nd.isInstantiated(); visit(nd.getName(), hasVariable ? DeclKind.varAndNamespace : DeclKind.namespace); return null; } From b5a4fc0041cfaba089b2aa1ba0e8bd87208a2fed Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:00:47 +0200 Subject: [PATCH 04/47] JS: Make Closure concepts based on AST instead --- .../ql/lib/semmle/javascript/Closure.qll | 101 +++++++++++------- .../semmle/javascript/GlobalAccessPaths.qll | 6 +- 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/Closure.qll b/javascript/ql/lib/semmle/javascript/Closure.qll index 40aee266379c..c31698333393 100644 --- a/javascript/ql/lib/semmle/javascript/Closure.qll +++ b/javascript/ql/lib/semmle/javascript/Closure.qll @@ -5,17 +5,49 @@ import javascript module Closure { + /** A call to `goog.require` */ + class RequireCallExpr extends CallExpr { + RequireCallExpr() { this.getCallee().(PropAccess).getQualifiedName() = "goog.require" } + + /** Gets the imported namespace name. */ + string getClosureNamespace() { result = this.getArgument(0).getStringValue() } + } + + /** A call to `goog.provide` */ + class ProvideCallExpr extends CallExpr { + ProvideCallExpr() { this.getCallee().(PropAccess).getQualifiedName() = "goog.provide" } + + /** Gets the imported namespace name. */ + string getClosureNamespace() { result = this.getArgument(0).getStringValue() } + } + + /** A call to `goog.module` or `goog.declareModuleId`. */ + private class ModuleDeclarationCall extends CallExpr { + private string kind; + + ModuleDeclarationCall() { + this.getCallee().(PropAccess).getQualifiedName() = kind and + kind = ["goog.module", "goog.declareModuleId"] + } + + /** Gets the declared namespace. */ + string getClosureNamespace() { result = this.getArgument(0).getStringValue() } + + /** Gets the string `goog.module` or `goog.declareModuleId` depending on which method is being called. */ + string getModuleKind() { result = kind } + } + /** * A reference to a Closure namespace. */ - class ClosureNamespaceRef extends DataFlow::Node instanceof ClosureNamespaceRef::Range { + deprecated class ClosureNamespaceRef extends DataFlow::Node instanceof ClosureNamespaceRef::Range { /** * Gets the namespace being referenced. */ string getClosureNamespace() { result = super.getClosureNamespace() } } - module ClosureNamespaceRef { + deprecated module ClosureNamespaceRef { /** * A reference to a Closure namespace. * @@ -32,10 +64,10 @@ module Closure { /** * A data flow node that returns the value of a closure namespace. */ - class ClosureNamespaceAccess extends ClosureNamespaceRef instanceof ClosureNamespaceAccess::Range { - } + deprecated class ClosureNamespaceAccess extends ClosureNamespaceRef instanceof ClosureNamespaceAccess::Range + { } - module ClosureNamespaceAccess { + deprecated module ClosureNamespaceAccess { /** * A data flow node that returns the value of a closure namespace. * @@ -47,7 +79,7 @@ module Closure { /** * A call to a method on the `goog.` namespace, as a closure reference. */ - abstract private class DefaultNamespaceRef extends DataFlow::MethodCallNode, + abstract deprecated private class DefaultNamespaceRef extends DataFlow::MethodCallNode, ClosureNamespaceRef::Range { DefaultNamespaceRef() { this = DataFlow::globalVarRef("goog").getAMethodCall() } @@ -59,14 +91,14 @@ module Closure { * Holds if `node` is the data flow node corresponding to the expression in * a top-level expression statement. */ - private predicate isTopLevelExpr(DataFlow::Node node) { + deprecated private predicate isTopLevelExpr(DataFlow::Node node) { any(TopLevel tl).getAChildStmt().(ExprStmt).getExpr().flow() = node } /** * A top-level call to `goog.provide`. */ - private class DefaultClosureProvideCall extends DefaultNamespaceRef { + deprecated private class DefaultClosureProvideCall extends DefaultNamespaceRef { DefaultClosureProvideCall() { this.getMethodName() = "provide" and isTopLevelExpr(this) @@ -76,13 +108,14 @@ module Closure { /** * A top-level call to `goog.provide`. */ - class ClosureProvideCall extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureProvideCall + deprecated class ClosureProvideCall extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureProvideCall { } /** * A call to `goog.require`. */ - private class DefaultClosureRequireCall extends DefaultNamespaceRef, ClosureNamespaceAccess::Range + deprecated private class DefaultClosureRequireCall extends DefaultNamespaceRef, + ClosureNamespaceAccess::Range { DefaultClosureRequireCall() { this.getMethodName() = "require" } } @@ -90,13 +123,13 @@ module Closure { /** * A call to `goog.require`. */ - class ClosureRequireCall extends ClosureNamespaceAccess, DataFlow::MethodCallNode instanceof DefaultClosureRequireCall + deprecated class ClosureRequireCall extends ClosureNamespaceAccess, DataFlow::MethodCallNode instanceof DefaultClosureRequireCall { } /** * A top-level call to `goog.module` or `goog.declareModuleId`. */ - private class DefaultClosureModuleDeclaration extends DefaultNamespaceRef { + deprecated private class DefaultClosureModuleDeclaration extends DefaultNamespaceRef { DefaultClosureModuleDeclaration() { (this.getMethodName() = "module" or this.getMethodName() = "declareModuleId") and isTopLevelExpr(this) @@ -106,41 +139,29 @@ module Closure { /** * A top-level call to `goog.module` or `goog.declareModuleId`. */ - class ClosureModuleDeclaration extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureModuleDeclaration + deprecated class ClosureModuleDeclaration extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureModuleDeclaration { } - private GlobalVariable googVariable() { variables(result, "goog", any(GlobalScope sc)) } - - pragma[nomagic] - private MethodCallExpr googModuleDeclExpr() { - result.getReceiver() = googVariable().getAnAccess() and - result.getMethodName() = ["module", "declareModuleId"] - } - - pragma[nomagic] - private MethodCallExpr googModuleDeclExprInContainer(StmtContainer container) { - result = googModuleDeclExpr() and - container = result.getContainer() - } - pragma[noinline] - private ClosureRequireCall getARequireInTopLevel(ClosureModule m) { result.getTopLevel() = m } + private RequireCallExpr getARequireInTopLevel(ClosureModule m) { result.getTopLevel() = m } /** * A module using the Closure module system, declared using `goog.module()` or `goog.declareModuleId()`. */ class ClosureModule extends Module { - ClosureModule() { exists(googModuleDeclExprInContainer(this)) } + private ModuleDeclarationCall decl; + + ClosureModule() { decl.getTopLevel() = this } /** * Gets the call to `goog.module` or `goog.declareModuleId` in this module. */ - ClosureModuleDeclaration getModuleDeclaration() { result.getTopLevel() = this } + deprecated ClosureModuleDeclaration getModuleDeclaration() { result.getTopLevel() = this } /** * Gets the namespace of this module. */ - string getClosureNamespace() { result = this.getModuleDeclaration().getClosureNamespace() } + string getClosureNamespace() { result = decl.getClosureNamespace() } override Module getAnImportedModule() { result.(ClosureModule).getClosureNamespace() = @@ -156,7 +177,7 @@ module Closure { * Has no result for ES6 modules using `goog.declareModuleId`. */ Variable getExportsVariable() { - this.getModuleDeclaration().getMethodName() = "module" and + decl.getModuleKind() = "goog.module" and result = this.getScope().getVariable("exports") } @@ -185,15 +206,15 @@ module Closure { ClosureScript() { not this instanceof ClosureModule and ( - any(ClosureProvideCall provide).getTopLevel() = this + any(ProvideCallExpr provide).getTopLevel() = this or - any(ClosureRequireCall require).getTopLevel() = this + any(RequireCallExpr require).getTopLevel() = this ) } /** Gets the identifier of a namespace required by this module. */ string getARequiredNamespace() { - exists(ClosureRequireCall require | + exists(RequireCallExpr require | require.getTopLevel() = this and result = require.getClosureNamespace() ) @@ -201,7 +222,7 @@ module Closure { /** Gets the identifer of a namespace provided by this module. */ string getAProvidedNamespace() { - exists(ClosureProvideCall require | + exists(ProvideCallExpr require | require.getTopLevel() = this and result = require.getClosureNamespace() ) @@ -213,7 +234,13 @@ module Closure { */ pragma[noinline] predicate isClosureNamespace(string name) { - exists(string namespace | namespace = any(ClosureNamespaceRef ref).getClosureNamespace() | + exists(string namespace | + namespace = + [ + any(RequireCallExpr ref).getClosureNamespace(), + any(ModuleDeclarationCall c).getClosureNamespace() + ] + | name = namespace.substring(0, namespace.indexOf(".")) or name = namespace diff --git a/javascript/ql/lib/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/lib/semmle/javascript/GlobalAccessPaths.qll index 35ba8cfe601a..4a461961f8af 100644 --- a/javascript/ql/lib/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/lib/semmle/javascript/GlobalAccessPaths.qll @@ -34,7 +34,7 @@ module AccessPath { not this.accessesGlobal(_) and not this instanceof DataFlow::PropRead and not this instanceof PropertyProjection and - not this instanceof Closure::ClosureNamespaceAccess and + not this.asExpr() instanceof Closure::RequireCallExpr and not this = DataFlow::parameterNode(any(ImmediatelyInvokedFunctionExpr iife).getAParameter()) and not FlowSteps::identityFunctionStep(_, this) } @@ -139,8 +139,8 @@ module AccessPath { result = join(fromReference(prop.getBase(), root), "[number]") ) or - exists(Closure::ClosureNamespaceAccess acc | node = acc | - result = acc.getClosureNamespace() and + exists(Closure::RequireCallExpr req | node = req.flow() | + result = req.getClosureNamespace() and root.isGlobal() ) or From 4cd6f4557294fbe338ac7ef48be2ea7c37f5d16e Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:01:22 +0200 Subject: [PATCH 05/47] JS: Avoid accidental recursion with API graphs --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 4 +++- .../frameworks/data/internal/ApiGraphModelsSpecific.qll | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 3e882251cbf7..51d17091df11 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -649,11 +649,13 @@ module API { /** Gets a node corresponding to an import of module `m` without taking into account types from models. */ Node getAModuleImportRaw(string m) { result = Impl::MkModuleImport(m) or - result = Impl::MkModuleImport(m).(Node).getMember("default") + result = Impl::MkModuleImport(m).(Node).getMember("default") or + result = Impl::MkTypeUse(m, "") } /** Gets a node whose type has the given qualified name, not including types from models. */ Node getANodeOfTypeRaw(string moduleName, string exportedName) { + exportedName != "" and result = Impl::MkTypeUse(moduleName, exportedName).(Node).getInstance() or exportedName = "" and diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll index c06490b84368..a95c5a30cb60 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -138,7 +138,7 @@ API::Node getExtraNodeFromType(string type) { parseRelevantTypeString(type, package, qualifiedName) | qualifiedName = "" and - result = [API::moduleImport(package), API::moduleExport(package)] + result = [API::Internal::getAModuleImportRaw(package), API::moduleExport(package)] or // Access instance of a type based on type annotations result = API::Internal::getANodeOfTypeRaw(package, qualifiedName) From 9566265356e097320bbf093f55db67311f461d42 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:02:09 +0200 Subject: [PATCH 06/47] JS: Add helper for getting local type names --- javascript/ql/lib/semmle/javascript/Variables.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/javascript/ql/lib/semmle/javascript/Variables.qll b/javascript/ql/lib/semmle/javascript/Variables.qll index 1eeb735124b5..e8abb6ad9819 100644 --- a/javascript/ql/lib/semmle/javascript/Variables.qll +++ b/javascript/ql/lib/semmle/javascript/Variables.qll @@ -27,6 +27,12 @@ class Scope extends @scope { result = this.getAVariable() and result.getName() = name } + + /** Gets the local type name with the given name declared in this scope. */ + LocalTypeName getLocalTypeName(string name) { + result.getScope() = this and + result.getName() = name + } } /** From 4bfb0483a8c3cd0ce3593ffe86c841e7532da3f2 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:06:52 +0200 Subject: [PATCH 07/47] JS: Resolve JSDocLocalTypeAccess to a variable in scope --- javascript/ql/lib/semmle/javascript/JSDoc.qll | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/javascript/ql/lib/semmle/javascript/JSDoc.qll b/javascript/ql/lib/semmle/javascript/JSDoc.qll index 10970a2e8b04..0f95023ce1f5 100644 --- a/javascript/ql/lib/semmle/javascript/JSDoc.qll +++ b/javascript/ql/lib/semmle/javascript/JSDoc.qll @@ -33,6 +33,9 @@ class JSDoc extends @jsdoc, Locatable { result.getTitle() = title } + /** Gets the element to which this JSDoc comment is attached */ + Documentable getDocumentedElement() { result.getDocumentation() = this } + override string toString() { result = this.getComment().toString() } } @@ -299,6 +302,41 @@ class JSDocIdentifierTypeExpr extends @jsdoc_identifier_type_expr, JSDocTypeExpr override predicate isRawFunction() { this.getName() = "Function" } } +private AstNode getAncestorInScope(Documentable doc) { + any(JSDocLocalTypeAccess t).getJSDocComment() = doc.getDocumentation() and // restrict to cases where we need this + result = doc.getParent() + or + exists(AstNode mid | + mid = getAncestorInScope(doc) and + not mid = any(Scope s).getScopeElement() and + result = mid.getParent() + ) +} + +private Scope getScope(Documentable doc) { result.getScopeElement() = getAncestorInScope(doc) } + +pragma[nomagic] +private predicate shouldResolveName(TopLevel top, string name) { + exists(JSDocLocalTypeAccess access | + access.getName() = name and + access.getTopLevel() = top + ) +} + +private LexicalName getOwnLocal(Scope scope, string name, DeclarationSpace space) { + scope = result.getScope() and + name = result.getName() and + space = result.getDeclarationSpace() and + shouldResolveName(scope.getScopeElement().getTopLevel(), name) // restrict size of predicate +} + +private LexicalName resolveLocal(Scope scope, string name, DeclarationSpace space) { + result = getOwnLocal(scope, name, space) + or + result = resolveLocal(scope.getOuterScope(), name, space) and + not exists(getOwnLocal(scope, name, space)) +} + /** * An unqualified identifier in a JSDoc type expression. * @@ -311,6 +349,12 @@ class JSDocIdentifierTypeExpr extends @jsdoc_identifier_type_expr, JSDocTypeExpr */ class JSDocLocalTypeAccess extends JSDocIdentifierTypeExpr { JSDocLocalTypeAccess() { not this = any(JSDocQualifiedTypeAccess a).getNameNode() } + + /** Gets a variable, type-name, or namespace that this expression may resolve to. */ + LexicalName getALexicalName() { + result = + resolveLocal(getScope(this.getJSDocComment().getDocumentedElement()), this.getName(), _) + } } /** From 1051136c079a01b3983b12a0f4be2a4869eec442 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:03:20 +0200 Subject: [PATCH 08/47] JS: Add test --- .../library-tests/UnderlyingTypes/calls.ts | 32 ++++++++++++ .../UnderlyingTypes/closure.es.js | 5 ++ .../UnderlyingTypes/closure.lib.js | 3 ++ .../UnderlyingTypes/closure.use.js | 16 ++++++ .../UnderlyingTypes/contextualTypes.ts | 45 +++++++++++++++++ .../UnderlyingTypes/expressBulkExport.ts | 1 + .../UnderlyingTypes/expressBulkExport.use.ts | 7 +++ .../UnderlyingTypes/expressExportAssign.ts | 2 + .../expressExportAssign.use.ts | 4 ++ .../expressExportAssignWrapper.ts | 5 ++ .../expressExportAssignWrapper.use.ts | 4 ++ .../UnderlyingTypes/expressSelectiveExport.ts | 2 + .../expressSelectiveExport.use.ts | 10 ++++ .../UnderlyingTypes/expressWrapperExport.ts | 1 + .../expressWrapperExport.use.ts | 29 +++++++++++ .../library-tests/UnderlyingTypes/globals.ts | 10 ++++ .../library-tests/UnderlyingTypes/jsdoc.js | 14 ++++++ .../UnderlyingTypes/namedImport.ts | 4 ++ .../UnderlyingTypes/namespaceDecls.ts | 27 ++++++++++ .../UnderlyingTypes/namespaceImport.ts | 15 ++++++ .../library-tests/UnderlyingTypes/props.ts | 16 ++++++ .../library-tests/UnderlyingTypes/subtype.ts | 20 ++++++++ .../UnderlyingTypes/test.expected | 50 +++++++++++++++++++ .../library-tests/UnderlyingTypes/test.ql | 15 ++++++ .../library-tests/UnderlyingTypes/test.qlref | 2 + .../UnderlyingTypes/tsconfig.json | 3 ++ 26 files changed, 342 insertions(+) create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/calls.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/closure.es.js create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/closure.lib.js create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/closure.use.js create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/contextualTypes.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/expressBulkExport.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/expressBulkExport.use.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssign.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssign.use.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssignWrapper.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssignWrapper.use.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/expressSelectiveExport.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/expressSelectiveExport.use.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/expressWrapperExport.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/expressWrapperExport.use.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/globals.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/jsdoc.js create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/namedImport.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/namespaceDecls.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/namespaceImport.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/props.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/subtype.ts create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/test.expected create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/test.ql create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/test.qlref create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/tsconfig.json diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/calls.ts b/javascript/ql/test/library-tests/UnderlyingTypes/calls.ts new file mode 100644 index 000000000000..68509e4a1c63 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/calls.ts @@ -0,0 +1,32 @@ +import * as express from 'express'; + +function getRequest(): express.Request { } + +function t1() { + getRequest(); // $ hasUnderlyingType='express'.Request +} + +declare function getRequestAmbient(): express.Request; + +function t2() { + getRequestAmbient(); // $ hasUnderlyingType='express'.Request +} + +class C { + method(): express.Request { } +} + +function t3(c: C) { + c.method(); // $ hasUnderlyingType='express'.Request + new C().method(); // $ hasUnderlyingType='express'.Request +} + +function callback(fn: (req: express.Request) => void) { // $ SPURIOUS: hasUnderlyingType='express'.Request // req seems to be a SourceNode +} + +function t4() { + callback(function ( + req // $ hasUnderlyingType='express'.Request + ) { } + ); +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/closure.es.js b/javascript/ql/test/library-tests/UnderlyingTypes/closure.es.js new file mode 100644 index 000000000000..cb140ec63c97 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/closure.es.js @@ -0,0 +1,5 @@ +goog.declareModuleId("closure.es") + +const Bar = goog.require('closure.reexported.Bar'); + +export { Bar } diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/closure.lib.js b/javascript/ql/test/library-tests/UnderlyingTypes/closure.lib.js new file mode 100644 index 000000000000..ffd67593202c --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/closure.lib.js @@ -0,0 +1,3 @@ +goog.module("closure.lib") + +exports.Foo = goog.require('closure.reexported.Foo'); diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/closure.use.js b/javascript/ql/test/library-tests/UnderlyingTypes/closure.use.js new file mode 100644 index 000000000000..22fc397cf3d0 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/closure.use.js @@ -0,0 +1,16 @@ +goog.module("closure.use") + +const lib = goog.require("closure.lib"); +const es = goog.require("closure.es"); + +/** + * @param {lib.Foo} x + */ +function t1(x) { // $ hasUnderlyingType=closure.reexported.Foo hasUnderlyingType=closure.lib.Foo +} + +/** + * @param {es.Bar} x + */ +function t2(x) { // $ hasUnderlyingType=closure.reexported.Bar hasUnderlyingType=closure.es.Bar +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/contextualTypes.ts b/javascript/ql/test/library-tests/UnderlyingTypes/contextualTypes.ts new file mode 100644 index 000000000000..cc461c5c7dda --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/contextualTypes.ts @@ -0,0 +1,45 @@ +import * as express from 'express'; + +interface Options { + handle(req: express.Request): void; // $ hasUnderlyingType='express'.Request +} + +declare function doSomething(options: Options); + +function t1() { + doSomething({ + handle(req) { // $ hasUnderlyingType='express'.Request + } + }); +} + +function t2(callback: ((opts: Options) => void) | undefined) { + callback({ + handle(req) { } // $ hasUnderlyingType='express'.Request + }) + callback!({ + handle(req) { } // $ hasUnderlyingType='express'.Request + }) +} + +function t3(): Options { + return { + handle(req) { } // $ hasUnderlyingType='express'.Request + } +} + +function t4(): Options[] { + return [ + { + handle(req) { } // $ hasUnderlyingType='express'.Request + } + ] +} + +async function t5(): Promise { + return { + handle(req) { // $ hasUnderlyingType='express'.Request + + } + } +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/expressBulkExport.ts b/javascript/ql/test/library-tests/UnderlyingTypes/expressBulkExport.ts new file mode 100644 index 000000000000..47ef09acc6e5 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/expressBulkExport.ts @@ -0,0 +1 @@ +export * from 'express'; diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/expressBulkExport.use.ts b/javascript/ql/test/library-tests/UnderlyingTypes/expressBulkExport.use.ts new file mode 100644 index 000000000000..bb94da47faf9 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/expressBulkExport.use.ts @@ -0,0 +1,7 @@ +import { Request, Response } from './expressBulkExport'; + +function t1(req: Request) { // $ hasUnderlyingType='express'.Request +} + +function t2(res: Response) { // $ hasUnderlyingType='express'.Response +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssign.ts b/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssign.ts new file mode 100644 index 000000000000..8aa013bcde09 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssign.ts @@ -0,0 +1,2 @@ +import E = require('express'); +export = E; diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssign.use.ts b/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssign.use.ts new file mode 100644 index 000000000000..da65575a443a --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssign.use.ts @@ -0,0 +1,4 @@ +import { Request } from "./expressExportAssign"; + +function t1(req: Request) { // $ hasUnderlyingType='express'.Request +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssignWrapper.ts b/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssignWrapper.ts new file mode 100644 index 000000000000..23c22e445914 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssignWrapper.ts @@ -0,0 +1,5 @@ +import Express = require('express'); +namespace Wrapper { + export import E = Express; +} +export = Wrapper; diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssignWrapper.use.ts b/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssignWrapper.use.ts new file mode 100644 index 000000000000..7bcf4b419e95 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/expressExportAssignWrapper.use.ts @@ -0,0 +1,4 @@ +import { E } from "./expressExportAssignWrapper"; + +function t1(req: E.Request) { // $ hasUnderlyingType='express'.Request +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/expressSelectiveExport.ts b/javascript/ql/test/library-tests/UnderlyingTypes/expressSelectiveExport.ts new file mode 100644 index 000000000000..c8aaf3bb9956 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/expressSelectiveExport.ts @@ -0,0 +1,2 @@ +export { Request } from 'express'; +export { Response as R } from 'express'; diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/expressSelectiveExport.use.ts b/javascript/ql/test/library-tests/UnderlyingTypes/expressSelectiveExport.use.ts new file mode 100644 index 000000000000..41ce42e3b1fa --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/expressSelectiveExport.use.ts @@ -0,0 +1,10 @@ +import { Request, Response, R } from './expressSelectiveExport'; + +function t1(req: Request) { // $ hasUnderlyingType='express'.Request +} + +function t2(res: Response) { // none, not exported +} + +function t3(res: R) { // $ hasUnderlyingType='express'.Response +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/expressWrapperExport.ts b/javascript/ql/test/library-tests/UnderlyingTypes/expressWrapperExport.ts new file mode 100644 index 000000000000..6fae12f06847 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/expressWrapperExport.ts @@ -0,0 +1 @@ +export * as wrapper from 'express'; diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/expressWrapperExport.use.ts b/javascript/ql/test/library-tests/UnderlyingTypes/expressWrapperExport.use.ts new file mode 100644 index 000000000000..62f7e519ff0e --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/expressWrapperExport.use.ts @@ -0,0 +1,29 @@ +import { Request, Response, wrapper } from './expressWrapperExport'; +import * as w from './expressWrapperExport'; + +function t1(req: Request) { // none +} + +function t2(res: Response) { // none +} + +function t3(req: wrapper.Request) { // $ hasUnderlyingType='express'.Request +} + +function t4(res: wrapper.Response) { // $ hasUnderlyingType='express'.Response +} + +function t5(req: w.wrapper.Request) { // $ hasUnderlyingType='express'.Request +} + +function t6(res: w.wrapper.Response) { // $ hasUnderlyingType='express'.Response +} + +function t7(req: w.Request) { // none +} + +function t8(res: w.Response) { // none +} + +function t9(e: typeof w.wrapper) { // $ hasUnderlyingType='express' +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/globals.ts b/javascript/ql/test/library-tests/UnderlyingTypes/globals.ts new file mode 100644 index 000000000000..8fc6546c70f3 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/globals.ts @@ -0,0 +1,10 @@ +function t1(el: HTMLElement) { } // $ hasUnderlyingType=HTMLElement + +/** + * @param {HTMLInputElement} el + */ +function t2(el) { // $ hasUnderlyingType=HTMLInputElement +} + +function t3(req: Express.Request) { // $ hasUnderlyingType=Express.Request +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/jsdoc.js b/javascript/ql/test/library-tests/UnderlyingTypes/jsdoc.js new file mode 100644 index 000000000000..662faeb52c9a --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/jsdoc.js @@ -0,0 +1,14 @@ +import * as e from 'express'; +import { Response } from 'express'; + +/** + * @param {e.Request} req + */ +function t1(req) { // $ hasUnderlyingType='express'.Request +} + +/** + * @param {Response} res + */ +function t2(res) { // $ hasUnderlyingType='express'.Response +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/namedImport.ts b/javascript/ql/test/library-tests/UnderlyingTypes/namedImport.ts new file mode 100644 index 000000000000..56b1d43d399d --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/namedImport.ts @@ -0,0 +1,4 @@ +import { Request } from 'express'; + +function t1(req: Request) { // $ hasUnderlyingType='express'.Request +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/namespaceDecls.ts b/javascript/ql/test/library-tests/UnderlyingTypes/namespaceDecls.ts new file mode 100644 index 000000000000..bd8811dfe7aa --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/namespaceDecls.ts @@ -0,0 +1,27 @@ +import Express = require('express'); + +namespace A { + export import E = Express; +} +namespace B { + export import Q = A +} +namespace C { + import E = Express; + export const A = E; +} + +function t1(x: A.E.Request) { // $ hasUnderlyingType='express'.Request +} + +function t2(x: B.Q.E.Request) { // $ hasUnderlyingType='express'.Request +} + +function t3(x: typeof Express) { // $ hasUnderlyingType='express' +} + +function t4(x: typeof A.E) { // $ hasUnderlyingType='express' +} + +function t5(x: typeof C.A) { // $ hasUnderlyingType='express' +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/namespaceImport.ts b/javascript/ql/test/library-tests/UnderlyingTypes/namespaceImport.ts new file mode 100644 index 000000000000..f2f96865f390 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/namespaceImport.ts @@ -0,0 +1,15 @@ +import * as express from 'express'; + +function t1(e: typeof express) { // $ hasUnderlyingType='express' +} + +function t2(req: express.Request) { // $ hasUnderlyingType='express'.Request +} + +function t3(req: Request) { // $ hasUnderlyingType=Request // not in scope, refers to a global +} + +type E = typeof express; + +function t4(e: E) { // $ hasUnderlyingType='express' +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/props.ts b/javascript/ql/test/library-tests/UnderlyingTypes/props.ts new file mode 100644 index 000000000000..1aded75ae95f --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/props.ts @@ -0,0 +1,16 @@ +import * as express from 'express'; + +interface Foo { + req: express.Request; + e: typeof express; +} + +function t1(f: Foo) { + f.req; // $ hasUnderlyingType='express'.Request + f.e; // $ hasUnderlyingType='express' + + const { + req, // $ hasUnderlyingType='express'.Request + e // $ hasUnderlyingType='express' + } = f; +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/subtype.ts b/javascript/ql/test/library-tests/UnderlyingTypes/subtype.ts new file mode 100644 index 000000000000..a23b85e3b81e --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/subtype.ts @@ -0,0 +1,20 @@ +import * as express from 'express'; + +interface MyRequest extends express.Request { + +} + +function t1(req: MyRequest) { // $ hasUnderlyingType='express'.Request +} + +class MyRequestClass extends express.Request { +} + +function t2(req: MyRequestClass) { // $ hasUnderlyingType='express'.Request +} + +class MyRequestClass2 implements express.Request { +} + +function t3(req: MyRequestClass2) { // $ hasUnderlyingType='express'.Request +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/test.expected b/javascript/ql/test/library-tests/UnderlyingTypes/test.expected new file mode 100644 index 000000000000..8c8d2716a064 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/test.expected @@ -0,0 +1,50 @@ +| calls.ts:6:5:6:16 | getRequest() | 'express'.Request | +| calls.ts:12:5:12:23 | getRequestAmbient() | 'express'.Request | +| calls.ts:20:5:20:14 | c.method() | 'express'.Request | +| calls.ts:21:5:21:20 | new C().method() | 'express'.Request | +| calls.ts:24:24:24:26 | req | 'express'.Request | +| calls.ts:29:9:29:11 | req | 'express'.Request | +| closure.use.js:9:13:9:13 | x | closure.lib.Foo | +| closure.use.js:9:13:9:13 | x | closure.reexported.Foo | +| closure.use.js:15:13:15:13 | x | closure.es.Bar | +| closure.use.js:15:13:15:13 | x | closure.reexported.Bar | +| contextualTypes.ts:4:12:4:14 | req | 'express'.Request | +| contextualTypes.ts:11:16:11:18 | req | 'express'.Request | +| contextualTypes.ts:18:16:18:18 | req | 'express'.Request | +| contextualTypes.ts:21:16:21:18 | req | 'express'.Request | +| contextualTypes.ts:27:16:27:18 | req | 'express'.Request | +| contextualTypes.ts:34:20:34:22 | req | 'express'.Request | +| contextualTypes.ts:41:16:41:18 | req | 'express'.Request | +| expressBulkExport.use.ts:3:13:3:15 | req | 'express'.Request | +| expressBulkExport.use.ts:6:13:6:15 | res | 'express'.Response | +| expressExportAssign.use.ts:3:13:3:15 | req | 'express'.Request | +| expressExportAssignWrapper.use.ts:3:13:3:15 | req | 'express'.Request | +| expressSelectiveExport.use.ts:3:13:3:15 | req | 'express'.Request | +| expressSelectiveExport.use.ts:9:13:9:15 | res | 'express'.Response | +| expressWrapperExport.use.ts:10:13:10:15 | req | 'express'.Request | +| expressWrapperExport.use.ts:13:13:13:15 | res | 'express'.Response | +| expressWrapperExport.use.ts:16:13:16:15 | req | 'express'.Request | +| expressWrapperExport.use.ts:19:13:19:15 | res | 'express'.Response | +| expressWrapperExport.use.ts:28:13:28:13 | e | 'express' | +| globals.ts:1:13:1:14 | el | HTMLElement | +| globals.ts:6:13:6:14 | el | HTMLInputElement | +| globals.ts:9:13:9:15 | req | Express.Request | +| jsdoc.js:7:13:7:15 | req | 'express'.Request | +| jsdoc.js:13:13:13:15 | res | 'express'.Response | +| namedImport.ts:3:13:3:15 | req | 'express'.Request | +| namespaceDecls.ts:14:13:14:13 | x | 'express'.Request | +| namespaceDecls.ts:17:13:17:13 | x | 'express'.Request | +| namespaceDecls.ts:20:13:20:13 | x | 'express' | +| namespaceDecls.ts:23:13:23:13 | x | 'express' | +| namespaceDecls.ts:26:13:26:13 | x | 'express' | +| namespaceImport.ts:3:13:3:13 | e | 'express' | +| namespaceImport.ts:6:13:6:15 | req | 'express'.Request | +| namespaceImport.ts:9:13:9:15 | req | Request | +| namespaceImport.ts:14:13:14:13 | e | 'express' | +| props.ts:9:5:9:9 | f.req | 'express'.Request | +| props.ts:10:5:10:7 | f.e | 'express' | +| props.ts:13:9:13:11 | req | 'express'.Request | +| props.ts:14:9:14:9 | e | 'express' | +| subtype.ts:7:13:7:15 | req | 'express'.Request | +| subtype.ts:13:13:13:15 | req | 'express'.Request | +| subtype.ts:19:13:19:15 | req | 'express'.Request | diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/test.ql b/javascript/ql/test/library-tests/UnderlyingTypes/test.ql new file mode 100644 index 000000000000..d3074111f919 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/test.ql @@ -0,0 +1,15 @@ +import javascript + +bindingset[x, y] +private string join(string x, string y) { + if x = "" or y = "" then result = x + y else result = x + "." + y +} + +query predicate hasUnderlyingType(DataFlow::SourceNode node, string value) { + node.hasUnderlyingType(value) + or + exists(string mod, string name | + node.hasUnderlyingType(mod, name) and + value = join("'" + mod + "'", name) + ) +} diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/test.qlref b/javascript/ql/test/library-tests/UnderlyingTypes/test.qlref new file mode 100644 index 000000000000..ab6773f15f90 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/test.qlref @@ -0,0 +1,2 @@ +query: test.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/tsconfig.json b/javascript/ql/test/library-tests/UnderlyingTypes/tsconfig.json new file mode 100644 index 000000000000..82194fc7ab06 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/tsconfig.json @@ -0,0 +1,3 @@ +{ + "include": ["."] +} From 1533e134a5e92e44d6a0a753b23b6077558c1907 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:03:38 +0200 Subject: [PATCH 09/47] JS: Add NameResolution.qll --- .../javascript/internal/NameResolution.qll | 468 ++++++++++++++++++ 1 file changed, 468 insertions(+) create mode 100644 javascript/ql/lib/semmle/javascript/internal/NameResolution.qll diff --git a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll new file mode 100644 index 000000000000..f6f36cc7504b --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll @@ -0,0 +1,468 @@ +/** + * Provides name resolution and propagates type information. + */ + +private import javascript + +/** + * Provides name resolution and propagates type information. + */ +module NameResolution { + private class NodeBase = + @expr or @typeexpr or @lexical_name or @toplevel or @function_decl_stmt or @class_decl_stmt or + @namespace_declaration or @enum_declaration or @interface_declaration or + @type_alias_declaration or @jsdoc_type_expr; + + /** + * A node in a graph which we use to perform name and type resolution. + */ + class Node extends NodeBase { + string toString() { + result = this.(AstNode).toString() + or + result = this.(LexicalName).toString() + or + result = this.(JSDocTypeExpr).toString() + } + + Location getLocation() { + result = this.(AstNode).getLocation() + or + result = this.(LocalVariable).getLocation() + or + result = this.(JSDocTypeExpr).getLocation() + } + } + + private signature predicate nodeSig(Node node); + + /** + * A module top-level, or a `module {}` or `enum {}` statement. + */ + private class ModuleLike extends AstNode { + ModuleLike() { + this instanceof Module + or + this instanceof NamespaceDefinition // `module {}` or `enum {}` statement + } + } + + /** + * Holds if values/namespaces/types in `node1` can flow to values/namespaces/types in `node2`. + */ + private predicate commonStep(Node node1, Node node2) { + // Import paths are part of the graph and has an incoming edge from the imported module, if found. + // This ensures we can also use the PathExpr as a source when working with external (unresolved) modules. + exists(Import imprt | + node1 = imprt.getImportedModule() and + node2 = imprt.getImportedPath() + ) + or + exists(ImportNamespaceSpecifier spec | + node1 = spec.getImportDeclaration().getImportedPath() and + node2 = spec.getLocal() + ) + or + exists(ExportNamespaceSpecifier spec | + node1 = spec.getExportDeclaration().(ReExportDeclaration).getImportedPath() and + node2 = spec + ) + or + exists(ExportAssignDeclaration assign | + node1 = assign.getExpression() and + node2 = assign.getContainer() + ) + or + exists(ImportEqualsDeclaration imprt | + node1 = imprt.getImportedEntity() and + node2 = imprt.getIdentifier() + ) + or + exists(ExternalModuleReference ref | + node1 = ref.getImportedPath() and + node2 = ref + ) + or + exists(ImportTypeExpr imprt | + node1 = imprt.getPathExpr() and // TODO: ImportTypeExpr does not seem to be resolved to a Module + node2 = imprt + ) + or + exists(ClassOrInterface cls | + node1 = cls and + node2 = cls.getIdentifier() + ) + or + exists(NamespaceDefinition def | + node1 = def and + node2 = def.getIdentifier() + ) + or + exists(Function fun | + node1 = fun and + node2 = fun.getIdentifier() + ) + or + exists(EnumMember def | + node1 = def.getInitializer() and + node2 = def.getIdentifier() + ) + or + exists(TypeAliasDeclaration alias | + node1 = alias.getDefinition() and + node2 = alias.getIdentifier() + ) + or + exists(VariableDeclarator decl | + node1 = decl.getInit() and + node2 = decl.getBindingPattern() + ) + or + exists(ParenthesizedTypeExpr type | + node1 = type.getElementType() and + node2 = type + ) + or + exists(ParenthesisExpr expr | + node1 = expr.getExpression() and + node2 = expr + ) + or + exists(NonNullAssertion assertion | + // For the time being we don't use this for nullness analysis, so just + // propagate through these assertions. + node1 = assertion.getExpression() and + node2 = assertion + ) + or + exists(FunctionTypeExpr fun | + node1 = fun.getFunction() and + node2 = fun + ) + or + exists(TypeofTypeExpr type | + node1 = type.getExpressionName() and + node2 = type + ) + or + exists(Closure::RequireCallExpr req | + node1.(Closure::ClosureModule).getClosureNamespace() = req.getClosureNamespace() and + node2 = req + ) + or + exists(Closure::ClosureModule mod | + node1 = mod.getExportsVariable() and + node2 = mod + ) + or + exists(ImmediatelyInvokedFunctionExpr fun, int i | + node1 = fun.getArgument(i) and + node2 = fun.getParameter(i) + ) + } + + /** + * Holds if there is a read from `node1` to `node2` that accesses the member `name`. + */ + predicate readStep(Node node1, string name, Node node2) { + exists(QualifiedTypeAccess access | + node1 = access.getQualifier() and + name = access.getIdentifier().getName() and + node2 = access + ) + or + exists(QualifiedNamespaceAccess access | + node1 = access.getQualifier() and + name = access.getIdentifier().getName() and + node2 = access + ) + or + exists(QualifiedVarTypeAccess access | + node1 = access.getQualifier() and + name = access.getIdentifier().getName() and + node2 = access + ) + or + exists(PropAccess access | + node1 = access.getBase() and + name = access.getPropertyName() and + node2 = access + ) + or + exists(ObjectPattern pattern | + node1 = pattern and + node2 = pattern.getPropertyPatternByName(name).getValuePattern() + ) + or + exists(ImportSpecifier spec | + node1 = spec.getImportDeclaration().getImportedPath() and + name = spec.getImportedName() and + node2 = spec.getLocal() + ) + or + exists(SelectiveReExportDeclaration exprt, ExportSpecifier spec | + spec = exprt.getASpecifier() and + node1 = exprt.getImportedPath() and + name = spec.getLocalName() and + node2 = spec.getLocal() + ) + or + exists(JSDocQualifiedTypeAccess expr | + node1 = expr.getBase() and + name = expr.getName() and + node2 = expr + ) + } + + private signature module TypeResolutionInputSig { + /** + * Holds if flow is permitted through the given variable. + */ + predicate isRelevantVariable(LexicalName var); + } + + /** + * A local variable with exactly one definition, not counting implicit initialization. + */ + private class EffectivelyConstantVariable extends LocalVariable { + EffectivelyConstantVariable() { + count(SsaExplicitDefinition ssa | ssa.getSourceVariable() = this) <= 1 // count may be zero if ambient + } + } + + /** Configuration for propagating values and namespaces */ + private module ValueConfig implements TypeResolutionInputSig { + predicate isRelevantVariable(LexicalName var) { + var instanceof EffectivelyConstantVariable + or + // We merge the namespace and value declaration spaces as it seems there is + // no need to distinguish them in practice. + var instanceof LocalNamespaceName + } + } + + /** + * Associates information about values, such as references to a class, module, or namespace. + */ + module ValueFlow = FlowImpl; + + private module TypeConfig implements TypeResolutionInputSig { + predicate isRelevantVariable(LexicalName var) { var instanceof LocalTypeName } + } + + /** + * Associates nodes with information about types. + */ + module TypeFlow = FlowImpl; + + private module FlowImpl { + /** + * Gets the exported member of `mod` named `name`. + */ + Node getModuleExport(ModuleLike mod, string name) { + exists(ExportDeclaration exprt | + mod = exprt.getContainer() and + exprt.exportsAs(result, name) and + S::isRelevantVariable(result) + ) + or + exists(ExportNamespaceSpecifier spec | + result = spec and + mod = spec.getContainer() and + name = spec.getExportedName() + ) + or + exists(SelectiveReExportDeclaration exprt, ExportSpecifier spec | + // `export { A as B } from 'blah'` + // This is not covered by `exportsAs` above because neither A or B is a LexicalName + // (both are property names) so it doesn't fit the interface of `exportsAs`. + spec = exprt.getASpecifier() and + mod = exprt.getContainer() and + name = spec.getExportedName() and + result = spec.getLocal() + ) + or + exists(EnumDeclaration enum | + mod = enum and + result = enum.getMemberByName(name).getIdentifier() + ) + or + storeToVariable(result, name, mod.(Closure::ClosureModule).getExportsVariable()) + } + + /** + * Holds if `value` is stored in `target.prop`. Only needs to recognise assignments + * that are also recognised by JSDoc tooling such as the Closure compiler. + */ + private predicate storeToVariable(Expr value, string prop, LocalVariable target) { + exists(AssignExpr assign | + // exports.name = value + assign.getLhs().(PropAccess).accesses(target.getAnAccess(), prop) and + value = assign.getRhs() + ) + or + // exports = { name: value } + value = target.getAnAssignedExpr().(ObjectExpr).getPropertyByName(prop).getInit() + } + + /** Steps that only apply for this configuration. */ + private predicate specificStep(Node node1, Node node2) { + exists(LexicalName var | S::isRelevantVariable(var) | + node1.(LexicalDecl).getALexicalName() = var and + node2 = var + or + node1 = var and + node2.(LexicalAccess).getALexicalName() = var + or + node1 = var and + node2.(JSDocLocalTypeAccess).getALexicalName() = var + ) + or + exists(Node base, string name, ModuleLike mod | + readStep(base, name, node2) and + base = trackModule(mod) and + node1 = getModuleExport(mod, name) + ) + } + + /** + * Holds if data should propagate from `node1` to `node2`. + */ + pragma[inline] + predicate step(Node node1, Node node2) { + commonStep(node1, node2) + or + specificStep(node1, node2) + } + + /** Helps track flow from a particular set of source nodes. */ + module Track { + /** Gets the set of nodes reachable from `source`. */ + Node track(Node source) { + isSource(source) and + result = source + or + step(track(source), result) + } + } + + signature class AstNodeSig extends AstNode; + + /** Helps track flow from a particular set of source nodes. */ + module TrackNode { + /** Gets the set of nodes reachable from `source`. */ + Node track(Source source) { + result = source + or + step(track(source), result) + } + } + } + + /** + * Gets a node to which the given module flows. + */ + predicate trackModule = ValueFlow::TrackNode::track/1; + + /** + * Holds if `moduleName` appears to start with a package name, as opposed to a relative file import. + */ + bindingset[moduleName] + private predicate isExternalModuleName(string moduleName) { + not moduleName.regexpMatch("^(\\.|/).*") + } + + bindingset[name] + private string normalizeModuleName(string name) { + result = + name.regexpReplaceAll("^node:", "") + .regexpReplaceAll("\\.[jt]sx?$", "") + .regexpReplaceAll("/(index)?$", "") + } + + /** Appends a name onto a qualified name */ + bindingset[a, b] + string append(string a, string b) { + if b = "default" + then result = a + else ( + (if a = "" or b = "" then result = a + b else result = a + "." + b) and + result.length() < 100 + ) + } + + private predicate needsQualifiedName(Node node) { + node = any(JSDocLocalTypeAccess t).getALexicalName().(Variable) + or + exists(Node prev | needsQualifiedName(prev) | + ValueFlow::step(node, prev) + or + readStep(node, _, prev) + ) + } + + /** + * Holds if `node` is a reference to the given module, or a qualified name rooted in that module. + * + * If `qualifiedName` is empty, `node` refers to the module itself. + * + * If `mod` is the string `"global"`, `node` refers to a global access path. + * + * Unlike `trackModule`, this is intended to track uses of external packages. + */ + predicate nodeRefersToModule(Node node, string mod, string qualifiedName) { + exists(PathExpr path | + path = any(Import imprt).getImportedPath() or + path = any(ReExportDeclaration e).getImportedPath() + | + node = path and + mod = normalizeModuleName(path.getValue()) and + isExternalModuleName(mod) and + qualifiedName = "" + ) + or + mod = "global" and + exists(LocalNamespaceAccess access | + node = access and + not exists(access.getLocalNamespaceName()) and + access.getName() = qualifiedName + ) + or + mod = "global" and + exists(JSDocLocalTypeAccess access | + node = access and + not exists(access.getALexicalName()) and + access.getName() = qualifiedName + ) + or + mod = "global" and + exists(GlobalVarAccess access | + node = access and + needsQualifiedName(access) and // restrict number of qualified names we generate + access.getName() = qualifiedName + ) + or + mod = "global" and + qualifiedName = node.(Closure::RequireCallExpr).getClosureNamespace() + or + // Additionally track through bulk re-exports (`export * from 'mod`). + // These are normally handled by 'exportAs' which supports various shadowing rules, + // but has no effect when the ultimate re-exported module is not resolved to a Module. + // We propagate external module refs through bulk re-exports and ignore shadowing rules. + exists(BulkReExportDeclaration reExport | + nodeRefersToModule(reExport.getImportedPath(), mod, qualifiedName) and + node = reExport.getContainer() + ) + or + exists(Node mid | + nodeRefersToModule(mid, mod, qualifiedName) and + ValueFlow::step(mid, node) + ) + or + exists(Node mid, string prefix, string step | + nodeRefersToModule(mid, mod, prefix) and + readStep(mid, step, node) and + qualifiedName = append(prefix, step) + ) + } +} From d61f576324d3a2a0ed0d8ac8617122dc3fca1b94 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:03:47 +0200 Subject: [PATCH 10/47] JS: Add UnderlyingTypes.qll --- .../javascript/internal/UnderlyingTypes.qll | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 javascript/ql/lib/semmle/javascript/internal/UnderlyingTypes.qll diff --git a/javascript/ql/lib/semmle/javascript/internal/UnderlyingTypes.qll b/javascript/ql/lib/semmle/javascript/internal/UnderlyingTypes.qll new file mode 100644 index 000000000000..8f6628278c4f --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/internal/UnderlyingTypes.qll @@ -0,0 +1,128 @@ +/** + * Provides name resolution and propagates type information. + */ + +private import javascript +private import semmle.javascript.internal.NameResolution::NameResolution + +/** + * Provides name resolution and propagates type information. + */ +module UnderlyingTypes { + private predicate subtypeStep(Node node1, Node node2) { + exists(ClassOrInterface cls | + ( + node1 = cls.getSuperClass() or + node1 = cls.getASuperInterface() + ) and + node2 = cls + ) + } + + predicate underlyingTypeStep(Node node1, Node node2) { + exists(UnionOrIntersectionTypeExpr type | + node1 = type.getAnElementType() and + node2 = type + ) + or + exists(ReadonlyTypeExpr type | + node1 = type.getElementType() and + node2 = type + ) + or + exists(OptionalTypeExpr type | + node1 = type.getElementType() and + node2 = type + ) + or + exists(GenericTypeExpr type | + node1 = type.getTypeAccess() and + node2 = type + ) + or + exists(ExpressionWithTypeArguments e | + node1 = e.getExpression() and + node2 = e + ) + or + exists(JSDocUnionTypeExpr type | + node1 = type.getAnAlternative() and + node2 = type + ) + or + exists(JSDocNonNullableTypeExpr type | + node1 = type.getTypeExpr() and + node2 = type + ) + or + exists(JSDocNullableTypeExpr type | + node1 = type.getTypeExpr() and + node2 = type + ) + or + exists(JSDocAppliedTypeExpr type | + node1 = type.getHead() and + node2 = type + ) + or + exists(JSDocOptionalParameterTypeExpr type | + node1 = type.getUnderlyingType() and + node2 = type + ) + } + + predicate nodeHasUnderlyingType(Node node, string mod, string name) { + nodeRefersToModule(node, mod, name) + or + exists(JSDocLocalTypeAccess type | + node = type and + not exists(type.getALexicalName()) and + not type = any(JSDocQualifiedTypeAccess t).getBase() and + name = type.getName() and + mod = "global" + ) + or + exists(LocalTypeAccess type | + node = type and + not exists(type.getLocalTypeName()) and + name = type.getName() and + mod = "global" + ) + or + exists(Node mid | nodeHasUnderlyingType(mid, mod, name) | + TypeFlow::step(mid, node) + or + underlyingTypeStep(mid, node) + or + subtypeStep(mid, node) + ) + } + + pragma[nomagic] + predicate nodeHasUnderlyingType(Node node, string name) { + nodeHasUnderlyingType(node, "global", name) + } + + predicate nodeHasUnderlyingClassType(Node node, DataFlow::ClassNode cls) { + node = cls.getAstNode() + or + exists(string name | + classHasGlobalName(cls, name) and + nodeHasUnderlyingType(node, name) + ) + or + exists(Node mid | nodeHasUnderlyingClassType(mid, cls) | + TypeFlow::step(mid, node) + or + underlyingTypeStep(mid, node) + // Note: unlike for external types, we do not use subtype steps here. + // 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 + } +} From fc580a5f78f56afc004d75ae9aab236a4abd7c6e Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:04:03 +0200 Subject: [PATCH 11/47] JS: Add TypeResolution.qll --- .../javascript/internal/TypeResolution.qll | 384 ++++++++++++++++++ 1 file changed, 384 insertions(+) create mode 100644 javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll diff --git a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll new file mode 100644 index 000000000000..61293f29978d --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll @@ -0,0 +1,384 @@ +private import javascript +private import semmle.javascript.internal.NameResolution::NameResolution +private import semmle.javascript.internal.UnderlyingTypes +private import semmle.javascript.dataflow.internal.sharedlib.SummaryTypeTracker as SummaryTypeTracker + +module TypeResolution { + predicate trackClassValue = ValueFlow::TrackNode::track/1; + + predicate trackType = TypeFlow::TrackNode::track/1; + + Node trackFunctionType(Function fun) { + result = fun + or + exists(Node mid | mid = trackFunctionType(fun) | + TypeFlow::step(mid, result) + or + UnderlyingTypes::underlyingTypeStep(mid, result) + ) + } + + predicate trackFunctionValue = ValueFlow::TrackNode::track/1; + + /** + * Gets the representative for the type containing the given member. + * + * For non-static members this is simply the enclosing type declaration. + * + * For static members we use the class's `Variable` as representative for the type of the class object. + */ + private Node getMemberBase(MemberDeclaration member) { + if member.isStatic() + then result = member.getDeclaringClass().getVariable() + else result = member.getDeclaringType() + } + + /** + * Holds if `host` is a type with a `content` of type `memberType`. + */ + private predicate typeMember(Node host, DataFlow::Content content, Node memberType) { + exists(MemberDeclaration decl | host = getMemberBase(decl) | + exists(FieldDeclaration field | + decl = field and + content.asPropertyName() = field.getName() and + memberType = field.getTypeAnnotation() + ) + or + exists(MethodDeclaration method | + decl = method and + content.asPropertyName() = method.getName() and + memberType = method.getBody() // use the Function as representative for the function type + ) + or + decl instanceof IndexSignature and + memberType = decl.(IndexSignature).getBody().getReturnTypeAnnotation() and + content.isUnknownArrayElement() + ) + or + // Ad-hoc support for array types. We don't support generics in general currently, we just special-case arrays. + content.isUnknownArrayElement() and + ( + memberType = host.(ArrayTypeExpr).getElementType() + or + exists(GenericTypeExpr type | + host = type and + type.getTypeAccess().(LocalTypeAccess).getName() = ["Array", "ReadonlyArray"] and + memberType = type.getTypeArgument(0) + ) + or + exists(JSDocAppliedTypeExpr type | + host = type and + type.getHead().(JSDocLocalTypeAccess).getName() = "Array" and + memberType = type.getArgument(0) + ) + ) + or + // Inherit members from base types + exists(ClassOrInterface baseType | typeMember(baseType, content, memberType) | + host.(ClassDefinition).getSuperClass() = trackClassValue(baseType) + or + host.(ClassOrInterface).getASuperInterface() = trackType(baseType) + ) + } + + /** + * Holds `use` refers to `host`, and `host` has type members. + * + * Currently steps through unions and intersections, which acts as a basic + * approximation to the unions/intersection of objects. + */ + private predicate typeMemberHostReaches(Node host, Node use) { + typeMember(host, _, _) and + use = host + or + exists(Node mid | typeMemberHostReaches(host, mid) | + TypeFlow::step(mid, use) + or + UnderlyingTypes::underlyingTypeStep(mid, use) + ) + } + + /** + * Holds if there is a read from from `object` to `member` that reads `contents`. + */ + private predicate valueReadStep(Node object, DataFlow::ContentSet contents, Node member) { + member.(PropAccess).accesses(object, contents.asPropertyName()) + or + object.(ObjectPattern).getPropertyPatternByName(contents.asPropertyName()).getValuePattern() = + member + or + SummaryTypeTracker::basicLoadStep(object.(AST::ValueNode).flow(), + member.(AST::ValueNode).flow(), contents) + } + + private predicate callTarget(InvokeExpr call, Function target) { + exists(ClassDefinition cls | + valueHasType(call.(NewExpr).getCallee(), trackClassValue(cls)) and + target = cls.getConstructor().getBody() + ) + or + valueHasType(call.(InvokeExpr).getCallee(), trackFunctionValue(target)) + or + valueHasType(call.(InvokeExpr).getCallee(), trackFunctionType(target)) and + ( + call instanceof NewExpr and + target = any(ConstructorTypeExpr t).getFunction() + or + call instanceof CallExpr and + target = any(PlainFunctionTypeExpr t).getFunction() + ) + or + exists(InterfaceDefinition interface, CallSignature sig | + valueHasType(call.(InvokeExpr).getCallee(), trackType(interface)) and + sig = interface.getACallSignature() and + target = sig.getBody() + | + call instanceof NewExpr and + sig instanceof ConstructorCallSignature + or + call instanceof CallExpr and + sig instanceof FunctionCallSignature + ) + } + + private predicate functionReturnType(Function func, Node returnType) { + returnType = func.getReturnTypeAnnotation() + or + not exists(func.getReturnTypeAnnotation()) and + exists(Function functionType | + contextualType(func, trackFunctionType(functionType)) and + returnType = functionType.getReturnTypeAnnotation() + ) + } + + bindingset[name] + private predicate isPromiseTypeName(string name) { + name.regexpMatch(".?(Promise|Thenable)(Like)?") + } + + private Node unwrapPromiseType(Node promiseType) { + exists(GenericTypeExpr type | + promiseType = type and + isPromiseTypeName(type.getTypeAccess().(LocalTypeAccess).getName()) and + result = type.getTypeArgument(0) + ) + or + exists(JSDocAppliedTypeExpr type | + promiseType = type and + isPromiseTypeName(type.getHead().(JSDocLocalTypeAccess).getName()) and + result = type.getArgument(0) + ) + } + + private predicate contextualType(Node value, Node type) { + exists(InvokeExpr call, Function target, int i | + callTarget(call, target) and + value = call.getArgument(i) and + type = target.getParameter(i).getTypeAnnotation() + ) + or + exists(Function lambda, Node returnType | + value = lambda.getAReturnedExpr() and + functionReturnType(lambda, returnType) + | + not lambda.isAsyncOrGenerator() and + type = returnType + or + lambda.isAsync() and + type = unwrapPromiseType(returnType) + ) + or + exists(ObjectExpr object, Node objectType, Node host, string name | + contextualType(object, objectType) and + typeMemberHostReaches(host, objectType) and + typeMember(host, any(DataFlow::Content c | c.asPropertyName() = name), type) and + value = object.getPropertyByName(name).getInit() + ) + or + exists(ArrayExpr array, Node arrayType, Node host | + contextualType(array, arrayType) and + typeMemberHostReaches(host, arrayType) and + typeMember(host, any(DataFlow::Content c | c.isUnknownArrayElement()), type) and + value = array.getAnElement() + ) + } + + /** + * Holds if `value` has the given `type`. + */ + predicate valueHasType(Node value, Node type) { + value.(BindingPattern).getTypeAnnotation() = type + or + exists(VarDecl decl | + // ValueFlow::step is restricted to variables with at most one assignment. Allow the type annotation + // of a variable to propagate to its uses, even if the variable has multiple assignments. + type = decl.getTypeAnnotation() and + value = decl.getVariable().(LocalVariable).getAnAccess() + ) + or + exists(MemberDeclaration member | + value.(ThisExpr).getBindingContainer() = member.getInit() and + type = getMemberBase(member) + ) + or + exists(ClassDefinition cls | + value = cls and + type = cls.getVariable() + ) + or + exists(FunctionDeclStmt fun | + value = fun and + type = fun.getVariable() + ) + or + exists(Function target | callTarget(value, target) | + type = target.getReturnTypeAnnotation() + or + exists(ClassDefinition cls | + target = cls.getConstructor().getBody() and + type = cls + ) + ) + or + // Contextual typing for parameters + exists(Function lambda, Function functionType, int i | + contextualType(lambda, trackFunctionType(functionType)) + or + exists(InterfaceDefinition interface | + contextualType(lambda, trackType(interface)) and + functionType = interface.getACallSignature().getBody() + ) + | + value = lambda.getParameter(i) and + not exists(value.(Parameter).getTypeAnnotation()) and + type = functionType.getParameter(i).getTypeAnnotation() + ) + or + exists(Node mid | valueHasType(mid, type) | ValueFlow::step(mid, value)) + or + exists(Node mid, Node midType, DataFlow::ContentSet contents, Node host | + valueReadStep(mid, contents, value) and + valueHasType(mid, midType) and + typeMemberHostReaches(host, midType) and + typeMember(host, contents.getAReadContent(), type) + ) + } + + signature predicate nodeSig(Node node); + + /** + * Tracks types that have a certain property, in the sense that: + * - an intersection type has the property if any member has the property + * - a union type has the property if all its members have the property + */ + module TrackMustProp { + predicate hasProperty(Node node) { + directlyHasProperty(node) + or + exists(Node mid | + hasProperty(mid) and + TypeFlow::step(mid, node) + ) + or + unionHasProp(node) + or + hasProperty(node.(IntersectionTypeExpr).getAnElementType()) + or + exists(ConditionalTypeExpr cond | + node = cond and + hasProperty(cond.getTrueType()) and + hasProperty(cond.getFalseType()) + ) + } + + private predicate unionHasProp(UnionTypeExpr node, int n) { + hasProperty(node.getElementType(0)) and n = 1 + or + unionHasProp(node, n - 1) and + hasProperty(node.getElementType(n - 1)) + } + + private predicate unionHasProp(UnionTypeExpr node) { + unionHasProp(node, node.getNumElementType()) + } + } + + module ValueHasProperty { + predicate valueHasProperty(Node value) { + exists(Node type | + valueHasType(value, type) and + typeHasProperty(type) + ) + } + } + + private predicate isSanitizingPrimitiveTypeBase(Node node) { + node.(TypeExpr).isNumbery() + or + node.(TypeExpr).isBooleany() + or + node.(TypeExpr).isNull() + or + node.(TypeExpr).isUndefined() + or + node.(TypeExpr).isVoid() + or + node.(TypeExpr).isNever() + or + node instanceof LiteralTypeExpr + or + node = any(EnumMember m).getIdentifier() // enum members are constant + or + node instanceof EnumDeclaration // enums are unions of constants + } + + /** + * Holds if `node` 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 = + TrackMustProp::hasProperty/1; + + /** + * Holds if `value` has a type that is considered untaintable (if actually enforced at runtime). + * + * See `isSanitizingPrimitiveType`. + */ + predicate valueHasSanitizingPrimitiveType = + ValueHasProperty::valueHasProperty/1; + + private predicate isPromiseBase(Node node) { exists(unwrapPromiseType(node)) } + + /** + * Holds if the given type is a Promise object. Does not hold for unions unless all parts of the union are promises. + */ + predicate isPromiseType = TrackMustProp::hasProperty/1; + + /** + * Holds if the given value has a type that implied it is a Promise object. Does not hold for unions unless all parts of the union are promises. + */ + predicate valueHasPromiseType = ValueHasProperty::valueHasProperty/1; + + /** + * Holds if `type` contains `string` or `any`, possibly wrapped in a promise. + */ + predicate hasUnderlyingStringOrAnyType(Node type) { + type.(TypeAnnotation).isStringy() + or + type.(TypeAnnotation).isAny() + or + type instanceof StringLiteralTypeExpr + or + type instanceof TemplateLiteralTypeExpr + or + exists(Node mid | hasUnderlyingStringOrAnyType(mid) | + TypeFlow::step(mid, type) + or + UnderlyingTypes::underlyingTypeStep(mid, type) + or + type = unwrapPromiseType(mid) + ) + } +} From b923eac9be22921e0eef39395300aa4fdea864c5 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:05:19 +0200 Subject: [PATCH 12/47] JS: Use underlying types in DataFlow::Node --- .../semmle/javascript/dataflow/DataFlow.qll | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll index 46801bd1ad7e..df3d0d5ff8ba 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll @@ -27,6 +27,9 @@ private import internal.PreCallGraphStep private import semmle.javascript.internal.CachedStages private import semmle.javascript.dataflow.internal.DataFlowPrivate as Private private import semmle.javascript.dataflow.internal.VariableOrThis +private import semmle.javascript.internal.NameResolution +private import semmle.javascript.internal.UnderlyingTypes +private import semmle.javascript.internal.TypeResolution module DataFlow { /** @@ -189,26 +192,6 @@ module DataFlow { FlowSteps::identityFunctionStep(result, this) } - /** - * Gets the static type of this node as determined by the TypeScript type system. - */ - private Type getType() { - exists(AST::ValueNode node | - this = TValueNode(node) and - ast_node_type(node, result) - ) - or - exists(BindingPattern pattern | - this = lvalueNode(pattern) and - ast_node_type(pattern, result) - ) - or - exists(MethodDefinition def | - this = TThisNode(def.getInit()) and - ast_node_type(def.getDeclaringClass(), result) - ) - } - /** * Gets the type annotation describing the type of this node, * provided that a static type could not be found. @@ -229,6 +212,15 @@ module DataFlow { ) } + private NameResolution::Node getNameResolutionNode() { + this = valueNode(result) + or + exists(PropertyPattern pattern | + result = pattern.getValuePattern() and + this = TPropNode(pattern) + ) + } + /** * Holds if this node is annotated with the given named type, * or is declared as a subtype thereof, or is a union or intersection containing such a type. @@ -236,9 +228,10 @@ module DataFlow { cached predicate hasUnderlyingType(string globalName) { Stages::TypeTracking::ref() and - this.getType().hasUnderlyingType(globalName) - or - this.getFallbackTypeAnnotation().getAnUnderlyingType().hasQualifiedName(globalName) + exists(NameResolution::Node type | + TypeResolution::valueHasType(this.getNameResolutionNode(), type) and + UnderlyingTypes::nodeHasUnderlyingType(type, globalName) + ) } /** @@ -248,9 +241,11 @@ module DataFlow { cached predicate hasUnderlyingType(string moduleName, string typeName) { Stages::TypeTracking::ref() and - this.getType().hasUnderlyingType(moduleName, typeName) - or - this.getFallbackTypeAnnotation().getAnUnderlyingType().hasQualifiedName(moduleName, typeName) + moduleName != "global" and + exists(NameResolution::Node type | + TypeResolution::valueHasType(this.getNameResolutionNode(), type) and + UnderlyingTypes::nodeHasUnderlyingType(type, moduleName, typeName) + ) } /** From cca48c09b919436881d7008b8388f883e9e0ad82 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:08:23 +0200 Subject: [PATCH 13/47] JS: Use in TypeAnnotation.getClass and hasUnderlyingType predicates --- javascript/ql/lib/semmle/javascript/JSDoc.qll | 58 +---------------- .../lib/semmle/javascript/TypeAnnotations.qll | 32 +++++++++- .../ql/lib/semmle/javascript/TypeScript.qll | 62 +------------------ .../lib/semmle/javascript/frameworks/Nest.qll | 2 +- .../JSDoc/NameResolution/test.expected | 6 +- .../JSDoc/NameResolution/test.ql | 2 +- .../JSDoc/JSDocTypeAnnotations.expected | 3 +- .../JSDoc/JSDocTypeAnnotations.ql | 2 +- .../QualifiedNames.ql | 2 +- .../HasQualifiedNameFallback/Test.expected | 10 +-- .../HasQualifiedNameFallback/Test.ql | 10 +-- .../HasUnderlyingType.expected | 1 - 12 files changed, 53 insertions(+), 137 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/JSDoc.qll b/javascript/ql/lib/semmle/javascript/JSDoc.qll index 0f95023ce1f5..85b7695cd70e 100644 --- a/javascript/ql/lib/semmle/javascript/JSDoc.qll +++ b/javascript/ql/lib/semmle/javascript/JSDoc.qll @@ -415,7 +415,7 @@ class JSDocNamedTypeExpr extends JSDocTypeExpr { * - `foo.bar.Baz` has prefix `foo` and suffix `.bar.Baz`. * - `Baz` has prefix `Baz` and an empty suffix. */ - predicate hasNameParts(string prefix, string suffix) { + deprecated predicate hasNameParts(string prefix, string suffix) { not this = any(JSDocQualifiedTypeAccess a).getBase() and // restrict size of predicate exists(string regex, string name | regex = "([^.]+)(.*)" | name = this.getRawName() and @@ -423,46 +423,6 @@ class JSDocNamedTypeExpr extends JSDocTypeExpr { suffix = name.regexpCapture(regex, 2) ) } - - pragma[noinline] - pragma[nomagic] - private predicate hasNamePartsAndEnv(string prefix, string suffix, JSDoc::Environment env) { - // Force join ordering - this.hasNameParts(prefix, suffix) and - env.isContainerInScope(this.getContainer()) - } - - /** - * Gets the qualified name of this name by resolving its prefix, if any. - */ - cached - private string resolvedName() { - exists(string prefix, string suffix, JSDoc::Environment env | - this.hasNamePartsAndEnv(prefix, suffix, env) and - result = env.resolveAlias(prefix) + suffix - ) - } - - override predicate hasQualifiedName(string globalName) { - globalName = this.resolvedName() - or - not exists(this.resolvedName()) and - globalName = this.getRawName() - } - - override DataFlow::ClassNode getClass() { - exists(string name | - this.hasQualifiedName(name) and - result.hasQualifiedName(name) - ) - or - // Handle case where a local variable has a reference to the class, - // but the class doesn't have a globally qualified name. - exists(string alias, JSDoc::Environment env | - this.hasNamePartsAndEnv(alias, "", env) and - result.getAClassReference().flowsTo(env.getNodeFromAlias(alias)) - ) - } } /** @@ -491,12 +451,6 @@ class JSDocAppliedTypeExpr extends @jsdoc_applied_type_expr, JSDocTypeExpr { * For example, in `Array`, `string` is the only argument type. */ JSDocTypeExpr getAnArgument() { result = this.getArgument(_) } - - override predicate hasQualifiedName(string globalName) { - this.getHead().hasQualifiedName(globalName) - } - - override DataFlow::ClassNode getClass() { result = this.getHead().getClass() } } /** @@ -516,8 +470,6 @@ class JSDocNullableTypeExpr extends @jsdoc_nullable_type_expr, JSDocTypeExpr { predicate isPrefix() { jsdoc_prefix_qualifier(this) } override JSDocTypeExpr getAnUnderlyingType() { result = this.getTypeExpr().getAnUnderlyingType() } - - override DataFlow::ClassNode getClass() { result = this.getTypeExpr().getClass() } } /** @@ -537,8 +489,6 @@ class JSDocNonNullableTypeExpr extends @jsdoc_non_nullable_type_expr, JSDocTypeE predicate isPrefix() { jsdoc_prefix_qualifier(this) } override JSDocTypeExpr getAnUnderlyingType() { result = this.getTypeExpr().getAnUnderlyingType() } - - override DataFlow::ClassNode getClass() { result = this.getTypeExpr().getClass() } } /** @@ -643,8 +593,6 @@ class JSDocOptionalParameterTypeExpr extends @jsdoc_optional_type_expr, JSDocTyp override JSDocTypeExpr getAnUnderlyingType() { result = this.getUnderlyingType().getAnUnderlyingType() } - - override DataFlow::ClassNode getClass() { result = this.getUnderlyingType().getClass() } } /** @@ -679,7 +627,7 @@ module JSDoc { /** * A statement container which may declare JSDoc name aliases. */ - class Environment extends StmtContainer { + deprecated class Environment extends StmtContainer { /** * Gets the fully qualified name aliased by the given unqualified name * within this container. @@ -729,7 +677,7 @@ module JSDoc { } pragma[noinline] - private predicate isTypenamePrefix(string name) { + deprecated private predicate isTypenamePrefix(string name) { any(JSDocNamedTypeExpr expr).hasNameParts(name, _) } } diff --git a/javascript/ql/lib/semmle/javascript/TypeAnnotations.qll b/javascript/ql/lib/semmle/javascript/TypeAnnotations.qll index d10b60b92b5b..318ad2f8873e 100644 --- a/javascript/ql/lib/semmle/javascript/TypeAnnotations.qll +++ b/javascript/ql/lib/semmle/javascript/TypeAnnotations.qll @@ -4,6 +4,8 @@ import javascript private import internal.StmtContainers +private import internal.NameResolution +private import internal.UnderlyingTypes /** * A type annotation, either in the form of a TypeScript type or a JSDoc comment. @@ -75,14 +77,38 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer { TypeAnnotation getAnUnderlyingType() { result = this } /** + * DEPRECATED. Use `hasUnderlyingType` instead. + * * Holds if this is a reference to the type with qualified name `globalName` relative to the global scope. */ - predicate hasQualifiedName(string globalName) { none() } + deprecated predicate hasQualifiedName(string globalName) { + UnderlyingTypes::nodeHasUnderlyingType(this, globalName) + } /** + * DEPRECATED. Use `hasUnderlyingType` instead. + * * Holds if this is a reference to the type exported from `moduleName` under the name `exportedName`. */ - predicate hasQualifiedName(string moduleName, string exportedName) { none() } + deprecated predicate hasQualifiedName(string moduleName, string exportedName) { + UnderlyingTypes::nodeHasUnderlyingType(this, moduleName, exportedName) + } + + /** + * Holds if this is a reference to the type with qualified name `globalName` relative to the global scope, + * or is declared as a subtype thereof, or is a union or intersection containing such a type. + */ + final predicate hasUnderlyingType(string globalName) { + UnderlyingTypes::nodeHasUnderlyingType(this, globalName) + } + + /** + * Holds if this is a reference to the type exported from `moduleName` under the name `exportedName`, + * or is declared as a subtype thereof, or is a union or intersection containing such a type. + */ + final predicate hasUnderlyingType(string moduleName, string exportedName) { + UnderlyingTypes::nodeHasUnderlyingType(this, moduleName, exportedName) + } /** Gets the statement in which this type appears. */ Stmt getEnclosingStmt() { none() } @@ -107,5 +133,5 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer { * * This unfolds nullability modifiers and generic type applications. */ - DataFlow::ClassNode getClass() { none() } + final DataFlow::ClassNode getClass() { UnderlyingTypes::nodeHasUnderlyingClassType(this, result) } } diff --git a/javascript/ql/lib/semmle/javascript/TypeScript.qll b/javascript/ql/lib/semmle/javascript/TypeScript.qll index 4be331ed6a50..ab670700c24b 100644 --- a/javascript/ql/lib/semmle/javascript/TypeScript.qll +++ b/javascript/ql/lib/semmle/javascript/TypeScript.qll @@ -1,4 +1,5 @@ import javascript +private import semmle.javascript.internal.UnderlyingTypes /** * A statement that defines a namespace, that is, a namespace declaration or enum declaration. @@ -575,10 +576,6 @@ class TypeExpr extends ExprOrType, @typeexpr, TypeAnnotation { override Function getEnclosingFunction() { result = ExprOrType.super.getEnclosingFunction() } override TopLevel getTopLevel() { result = ExprOrType.super.getTopLevel() } - - override DataFlow::ClassNode getClass() { - result.getAstNode() = this.getType().(ClassType).getClass() - } } /** @@ -698,58 +695,9 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef { */ TypeName getTypeName() { ast_node_symbol(this, result) } - override predicate hasQualifiedName(string globalName) { - this.getTypeName().hasQualifiedName(globalName) - or - exists(LocalTypeAccess local | local = this | - not exists(local.getLocalTypeName()) and // Without a local type name, the type is looked up in the global scope. - globalName = local.getName() - ) - } - - override predicate hasQualifiedName(string moduleName, string exportedName) { - this.getTypeName().hasQualifiedName(moduleName, exportedName) - or - exists(ImportDeclaration imprt, ImportSpecifier spec | - moduleName = getImportName(imprt) and - spec = imprt.getASpecifier() - | - spec.getImportedName() = exportedName and - this = spec.getLocal().(TypeDecl).getLocalTypeName().getAnAccess() - or - (spec instanceof ImportNamespaceSpecifier or spec instanceof ImportDefaultSpecifier) and - this = - spec.getLocal().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName) - ) - or - exists(ImportEqualsDeclaration imprt | - moduleName = getImportName(imprt.getImportedEntity()) and - this = - imprt - .getIdentifier() - .(LocalNamespaceDecl) - .getLocalNamespaceName() - .getAMemberAccess(exportedName) - ) - } - override string getAPrimaryQlClass() { result = "TypeAccess" } } -/** - * Gets a suitable name for the library imported by `imprt`. - * - * For relative imports, this is the snapshot-relative path to the imported module. - * For non-relative imports, it is the import path itself. - */ -private string getImportName(Import imprt) { - exists(string path | path = imprt.getImportedPathString() | - if path.regexpMatch("[./].*") - then result = imprt.getImportedModule().getFile().getRelativePath() - else result = path - ) -} - /** An identifier that is used as part of a type, such as `Date`. */ class LocalTypeAccess extends @local_type_access, TypeAccess, Identifier, LexicalAccess { override predicate isStringy() { this.getName() = "String" } @@ -822,14 +770,6 @@ class GenericTypeExpr extends @generic_typeexpr, TypeExpr { /** Gets the number of type arguments. This is always at least one. */ int getNumTypeArgument() { result = count(this.getATypeArgument()) } - override predicate hasQualifiedName(string globalName) { - this.getTypeAccess().hasQualifiedName(globalName) - } - - override predicate hasQualifiedName(string moduleName, string exportedName) { - this.getTypeAccess().hasQualifiedName(moduleName, exportedName) - } - override string getAPrimaryQlClass() { result = "GenericTypeExpr" } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll index dd6e1a7d9159..89b7fe049974 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll @@ -237,7 +237,7 @@ module NestJS { CustomPipeClass() { exists(ClassDefinition cls | this = cls.flow() and - cls.getASuperInterface().hasQualifiedName("@nestjs/common", "PipeTransform") + cls.getASuperInterface().hasUnderlyingType("@nestjs/common", "PipeTransform") ) } diff --git a/javascript/ql/test/library-tests/JSDoc/NameResolution/test.expected b/javascript/ql/test/library-tests/JSDoc/NameResolution/test.expected index 97730513195b..7c015994aafe 100644 --- a/javascript/ql/test/library-tests/JSDoc/NameResolution/test.expected +++ b/javascript/ql/test/library-tests/JSDoc/NameResolution/test.expected @@ -1,10 +1,10 @@ -| bar.js:5:14:5:14 | x | x | +| bar.js:5:14:5:14 | x | ns.very.long.namespace | | bar.js:5:14:5:18 | x.Foo | ns.very.long.namespace.Foo | -| bar.js:12:14:12:17 | iife | iife | +| bar.js:12:14:12:17 | iife | IIFE | | bar.js:12:14:12:21 | iife.Foo | IIFE.Foo | | closure.js:8:12:8:15 | goog | goog | | closure.js:8:12:8:19 | goog.net | goog.net | | closure.js:8:12:8:28 | goog.net.SomeType | goog.net.SomeType | -| closure.js:9:12:9:14 | net | net | +| closure.js:9:12:9:14 | net | goog.net | | closure.js:9:12:9:23 | net.SomeType | goog.net.SomeType | | closure.js:10:12:10:19 | SomeType | goog.net.SomeType | diff --git a/javascript/ql/test/library-tests/JSDoc/NameResolution/test.ql b/javascript/ql/test/library-tests/JSDoc/NameResolution/test.ql index 1b7ebfdd501a..bb1de953169b 100644 --- a/javascript/ql/test/library-tests/JSDoc/NameResolution/test.ql +++ b/javascript/ql/test/library-tests/JSDoc/NameResolution/test.ql @@ -1,3 +1,3 @@ import javascript -query string test_hasQualifiedName(JSDocNamedTypeExpr expr) { expr.hasQualifiedName(result) } +query string test_hasUnderlyingType(JSDocNamedTypeExpr expr) { expr.hasUnderlyingType(result) } diff --git a/javascript/ql/test/library-tests/TypeAnnotations/JSDoc/JSDocTypeAnnotations.expected b/javascript/ql/test/library-tests/TypeAnnotations/JSDoc/JSDocTypeAnnotations.expected index 8ac3eea2be5f..06afe15ee183 100644 --- a/javascript/ql/test/library-tests/TypeAnnotations/JSDoc/JSDocTypeAnnotations.expected +++ b/javascript/ql/test/library-tests/TypeAnnotations/JSDoc/JSDocTypeAnnotations.expected @@ -2,13 +2,14 @@ test_isString | tst.js:2:12:2:17 | string | test_isNumber | tst.js:3:12:3:17 | number | -test_QualifiedName +test_hasUnderlyingType | VarType | tst.js:9:13:9:19 | VarType | | boolean | tst.js:5:14:5:20 | boolean | | foo | tst.js:4:12:4:14 | foo | | foo.bar | tst.js:4:12:4:18 | foo.bar | | foo.bar.baz | tst.js:4:12:4:22 | foo.bar.baz | | number | tst.js:3:12:3:17 | number | +| number | tst.js:3:12:3:18 | number? | | string | tst.js:2:12:2:17 | string | test_ParameterType | tst.js:7:12:7:12 | x | tst.js:2:12:2:17 | string | diff --git a/javascript/ql/test/library-tests/TypeAnnotations/JSDoc/JSDocTypeAnnotations.ql b/javascript/ql/test/library-tests/TypeAnnotations/JSDoc/JSDocTypeAnnotations.ql index 829435e3220c..fd223ee5a533 100644 --- a/javascript/ql/test/library-tests/TypeAnnotations/JSDoc/JSDocTypeAnnotations.ql +++ b/javascript/ql/test/library-tests/TypeAnnotations/JSDoc/JSDocTypeAnnotations.ql @@ -4,7 +4,7 @@ query TypeAnnotation test_isString() { result.isString() } query TypeAnnotation test_isNumber() { result.isNumber() } -query TypeAnnotation test_QualifiedName(string name) { result.hasQualifiedName(name) } +query TypeAnnotation test_hasUnderlyingType(string name) { result.hasUnderlyingType(name) } query TypeAnnotation test_ParameterType(Parameter p) { result = p.getTypeAnnotation() } diff --git a/javascript/ql/test/library-tests/TypeAnnotations/TSUnresolvedQualifiedName/QualifiedNames.ql b/javascript/ql/test/library-tests/TypeAnnotations/TSUnresolvedQualifiedName/QualifiedNames.ql index e9d66a4afe09..b4d324377bef 100644 --- a/javascript/ql/test/library-tests/TypeAnnotations/TSUnresolvedQualifiedName/QualifiedNames.ql +++ b/javascript/ql/test/library-tests/TypeAnnotations/TSUnresolvedQualifiedName/QualifiedNames.ql @@ -1,5 +1,5 @@ import javascript from TypeAnnotation type, string mod, string name -where type.hasQualifiedName(mod, name) +where type.hasUnderlyingType(mod, name) select type, mod, name diff --git a/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.expected b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.expected index 5ee97e2dfb59..3781aea96e20 100644 --- a/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.expected +++ b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.expected @@ -1,13 +1,15 @@ -hasQualifiedNameModule -| default-import | default | tst.ts:11:9:11:21 | DefaultImport | +hasUnderlyingTypeModule +| default-import | | tst.ts:11:9:11:21 | DefaultImport | +| global | UnresolvedName | tst.ts:12:9:12:22 | UnresolvedName | +| import-assign | | tst.ts:10:9:10:11 | asn | | import-assign | Foo | tst.ts:10:9:10:15 | asn.Foo | | named-import | Name1 | tst.ts:7:9:7:13 | Name1 | | named-import | Name1 | tst.ts:13:9:13:13 | Name1 | | named-import | Name1 | tst.ts:13:9:13:21 | Name1 | | named-import | Name2 | tst.ts:8:9:8:13 | Name2 | +| namespace-import | | tst.ts:9:9:9:17 | namespace | | namespace-import | Foo | tst.ts:9:9:9:21 | namespace.Foo | -| tst.ts | ExportedClass | relative.ts:4:8:4:20 | ExportedClass | -hasQualifiedNameGlobal +hasUnderlyingTypeGlobal | UnresolvedName | tst.ts:12:9:12:22 | UnresolvedName | paramExample | tst.ts:7:5:7:6 | x1 | diff --git a/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.ql b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.ql index 2b63e171f1e9..199749ed3f60 100644 --- a/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.ql +++ b/javascript/ql/test/library-tests/TypeScript/HasQualifiedNameFallback/Test.ql @@ -1,13 +1,13 @@ import javascript -query TypeAnnotation hasQualifiedNameModule(string moduleName, string member) { - result.hasQualifiedName(moduleName, member) +query TypeAnnotation hasUnderlyingTypeModule(string moduleName, string member) { + result.hasUnderlyingType(moduleName, member) } -query TypeAnnotation hasQualifiedNameGlobal(string globalName) { - result.hasQualifiedName(globalName) +query TypeAnnotation hasUnderlyingTypeGlobal(string globalName) { + result.hasUnderlyingType(globalName) } query Parameter paramExample() { - result.getTypeAnnotation().hasQualifiedName("named-import", "Name1") + result.getTypeAnnotation().hasUnderlyingType("named-import", "Name1") } diff --git a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected index a9123b1ef55b..e330bb897b7d 100644 --- a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected +++ b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected @@ -6,5 +6,4 @@ underlyingTypeNode | foo | | file://:0:0:0:0 | use moduleImport("foo").getMember("exports") | | foo | | foo.ts:1:8:1:10 | use moduleImport("foo").getMember("exports").getMember("default") | -| foo | Bar | foo.ts:3:1:5:1 | use moduleImport("foo").getMember("exports").getMember("Bar").getInstance() | | foo | Bar | foo.ts:3:12:3:12 | use moduleImport("foo").getMember("exports").getMember("Bar").getInstance() | From 9fd85c96881228cf0e6a2f1483465303980016c3 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:08:47 +0200 Subject: [PATCH 14/47] JS: Update jQuery model --- javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll b/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll index 4fad4ae1b059..70beadbfa573 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll @@ -60,11 +60,7 @@ private predicate neverReturnsJQuery(string name) { decl.getBaseName() = "jQuery" and decl.getName() = name | - not decl.getDocumentation() - .getATagByTitle("return") - .getType() - .getAnUnderlyingType() - .hasQualifiedName("jQuery") + not decl.getDocumentation().getATagByTitle("return").getType().hasUnderlyingType("jQuery") ) } @@ -414,6 +410,8 @@ module JQuery { this = DataFlow::moduleImport(["jquery", "zepto", "cash-dom"]) or this.hasUnderlyingType("JQueryStatic") + or + this.hasUnderlyingType("jquery", "") } } } From 2d21074598fe0b6682bb86bcc2c5f59623d7e042 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:09:21 +0200 Subject: [PATCH 15/47] JS: Use sanitizing primitive types in ViewComponentInput --- .../semmle/javascript/ViewComponentInput.qll | 30 +++---------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ViewComponentInput.qll b/javascript/ql/lib/semmle/javascript/ViewComponentInput.qll index bc80826de5c9..7ab04ad5bd26 100644 --- a/javascript/ql/lib/semmle/javascript/ViewComponentInput.qll +++ b/javascript/ql/lib/semmle/javascript/ViewComponentInput.qll @@ -3,6 +3,7 @@ */ private import javascript +private import semmle.javascript.internal.TypeResolution /** * An input to a view component, such as React props. @@ -14,34 +15,11 @@ abstract class ViewComponentInput extends DataFlow::Node { private class ViewComponentInputAsThreatModelSource extends ThreatModelSource::Range instanceof ViewComponentInput { - ViewComponentInputAsThreatModelSource() { not isSafeType(this.asExpr().getType()) } + ViewComponentInputAsThreatModelSource() { + not TypeResolution::valueHasSanitizingPrimitiveType(this.asExpr()) + } final override string getThreatModel() { result = "view-component-input" } final override string getSourceType() { result = ViewComponentInput.super.getSourceType() } } - -private predicate isSafeType(Type t) { - t instanceof NumberLikeType - or - t instanceof BooleanLikeType - or - t instanceof UndefinedType - or - t instanceof NullType - or - t instanceof VoidType - or - hasSafeTypes(t, t.(UnionType).getNumElementType()) - or - isSafeType(t.(IntersectionType).getAnElementType()) -} - -/** Hold if the first `n` components of `t` are safe types. */ -private predicate hasSafeTypes(UnionType t, int n) { - isSafeType(t.getElementType(0)) and - n = 1 - or - isSafeType(t.getElementType(n - 1)) and - hasSafeTypes(t, n - 1) -} From 6fdd7feed46dcb3284ba371f18f8e17eb3fda82e Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:10:08 +0200 Subject: [PATCH 16/47] JS: Use sanitizing primitive type in Nest model --- .../lib/semmle/javascript/frameworks/Nest.qll | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll index 89b7fe049974..ed3829d133f1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll @@ -5,6 +5,8 @@ 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/). @@ -133,7 +135,9 @@ module NestJS { hasSanitizingPipe(this, false) or hasSanitizingPipe(this, true) and - isSanitizingType(this.getParameter().getType().unfold()) + // 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()) } } @@ -209,19 +213,6 @@ module NestJS { dependsOnType = true } - /** - * Holds if a parameter of type `t` is considered sanitized, provided it has been checked by `ValidationPipe` - * (which relies on metadata emitted by the TypeScript compiler). - */ - private predicate isSanitizingType(Type t) { - t instanceof NumberType - or - t instanceof BooleanType - // - // 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. - } - /** * A user-defined pipe class, for example: * ```js From 4e44fdaa7b6570bfaf589c599e3393e0e5baabf9 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:10:32 +0200 Subject: [PATCH 17/47] JS: Use hasUnderlyingStringOrAnyType in Nest model --- .../ql/lib/semmle/javascript/frameworks/Nest.qll | 16 ++++------------ .../library-tests/frameworks/Nest/test.expected | 3 +++ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll index ed3829d133f1..d6bcb9ddd400 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll @@ -318,14 +318,6 @@ module NestJS { } } - private predicate isStringType(Type type) { - type instanceof StringType - or - type instanceof AnyType - or - isStringType(type.(PromiseType).getElementType().unfold()) - } - /** * A return value from a route handler, seen as an argument to `res.send()`. * @@ -344,10 +336,10 @@ module NestJS { ReturnValueAsResponseSend() { handler.isReturnValueReflected() and this = handler.getAReturn() and - // Only returned strings are sinks - not exists(Type type | - type = this.asExpr().getType() and - not isStringType(type.unfold()) + // 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) ) } diff --git a/javascript/ql/test/library-tests/frameworks/Nest/test.expected b/javascript/ql/test/library-tests/frameworks/Nest/test.expected index ff12967bec69..db49fc95eba9 100644 --- a/javascript/ql/test/library-tests/frameworks/Nest/test.expected +++ b/javascript/ql/test/library-tests/frameworks/Nest/test.expected @@ -71,6 +71,9 @@ responseSendArgument | local/customPipe.ts:37:16:37:31 | '' + unsanitized | | local/customPipe.ts:42:16:42:31 | '' + unsanitized | | local/customPipe.ts:48:16:48:31 | '' + unsanitized | +| local/routes.ts:7:12:7:16 | 'foo' | +| local/routes.ts:12:12:12:16 | 'foo' | +| local/routes.ts:17:12:17:16 | 'foo' | | local/routes.ts:32:31:32:31 | x | | local/routes.ts:33:31:33:38 | queryObj | | local/routes.ts:34:31:34:34 | name | From 6ac35f1c668b7fc74c2bddfd0d68f434c3843742 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:10:44 +0200 Subject: [PATCH 18/47] JS: Use in MissingAwait --- javascript/ql/src/Expressions/MissingAwait.ql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/Expressions/MissingAwait.ql b/javascript/ql/src/Expressions/MissingAwait.ql index d97c006a7bd1..a16d31ee2a57 100644 --- a/javascript/ql/src/Expressions/MissingAwait.ql +++ b/javascript/ql/src/Expressions/MissingAwait.ql @@ -10,6 +10,7 @@ */ import javascript +private import semmle.javascript.internal.TypeResolution /** * Holds if `call` is a call to an `async` function. @@ -28,7 +29,7 @@ predicate isPromise(DataFlow::SourceNode node, boolean nullable) { isAsyncCall(node, nullable) or not isAsyncCall(node, _) and - node.asExpr().getType() instanceof PromiseType and + TypeResolution::valueHasPromiseType(node.asExpr()) and nullable = true } From 989402d7b7744a7ac427ea5c024eec755eb69789 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:11:49 +0200 Subject: [PATCH 19/47] JS: Remove some dependencies on type extraction --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 8 ++------ .../frameworks/data/internal/ApiGraphModelsSpecific.qll | 4 +--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 51d17091df11..3bb04f2686be 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -751,18 +751,14 @@ module API { MkModuleImport(string m) { imports(_, m) or - any(TypeAnnotation n).hasQualifiedName(m, _) - or - any(Type t).hasUnderlyingType(m, _) + any(TypeAnnotation n).hasUnderlyingType(m, _) } or MkClassInstance(DataFlow::ClassNode cls) { needsDefNode(cls) } or MkDef(DataFlow::Node nd) { rhs(_, _, nd) } or MkUse(DataFlow::Node nd) { use(_, _, nd) } or /** A use of a TypeScript type. */ MkTypeUse(string moduleName, string exportName) { - any(TypeAnnotation n).hasQualifiedName(moduleName, exportName) - or - any(Type t).hasUnderlyingType(moduleName, exportName) + any(TypeAnnotation n).hasUnderlyingType(moduleName, exportName) } or MkSyntheticCallbackArg(DataFlow::Node src, int bound, DataFlow::InvokeNode nd) { trackUseNode(src, true, bound, "").flowsTo(nd.getCalleeNode()) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll index a95c5a30cb60..5e9846e9ad55 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -60,9 +60,7 @@ predicate isPackageUsed(string package) { or package = any(JS::Import imp).getImportedPathString() or - any(JS::TypeName t).hasQualifiedName(package, _) - or - any(JS::TypeAnnotation t).hasQualifiedName(package, _) + any(JS::TypeAnnotation t).hasUnderlyingType(package, _) or exists(JS::PackageJson json | json.getPackageName() = package) } From 57811edc442309d6d7c7f3a836894ccb283cafe6 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Apr 2025 13:12:00 +0200 Subject: [PATCH 20/47] JS: Some test updates --- .../UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected | 4 ---- .../Security/CWE-079/UnsafeHtmlConstruction/typed.ts | 5 ++--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected index 499cf6cce49d..4f757d1a9313 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected @@ -22,7 +22,6 @@ | main.js:111:37:111:37 | x | main.js:98:43:98:43 | x | main.js:111:37:111:37 | x | This markdown rendering which depends on $@ might later allow $@. | main.js:98:43:98:43 | x | library input | main.js:112:24:112:26 | svg | cross-site scripting | | main.js:117:34:117:34 | s | main.js:116:47:116:47 | s | main.js:117:34:117:34 | s | This markdown rendering which depends on $@ might later allow $@. | main.js:116:47:116:47 | s | library input | main.js:118:53:118:56 | html | cross-site scripting | | typed.ts:2:29:2:29 | s | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | This HTML construction which depends on $@ might later allow $@. | typed.ts:1:39:1:39 | s | library input | typed.ts:3:31:3:34 | html | cross-site scripting | -| typed.ts:8:40:8:40 | s | typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | This HTML construction which depends on $@ might later allow $@. | typed.ts:6:43:6:43 | s | library input | typed.ts:8:29:8:52 | " ... /span>" | cross-site scripting | edges | jquery-plugin.js:11:27:11:31 | stuff | jquery-plugin.js:14:31:14:35 | stuff | provenance | | | jquery-plugin.js:11:34:11:40 | options | jquery-plugin.js:12:31:12:37 | options | provenance | | @@ -69,7 +68,6 @@ edges | main.js:98:43:98:43 | x | main.js:111:37:111:37 | x | provenance | | | main.js:116:47:116:47 | s | main.js:117:34:117:34 | s | provenance | | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | provenance | | -| typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | provenance | | nodes | jquery-plugin.js:11:27:11:31 | stuff | semmle.label | stuff | | jquery-plugin.js:11:34:11:40 | options | semmle.label | options | @@ -128,6 +126,4 @@ nodes | main.js:117:34:117:34 | s | semmle.label | s | | typed.ts:1:39:1:39 | s | semmle.label | s | | typed.ts:2:29:2:29 | s | semmle.label | s | -| typed.ts:6:43:6:43 | s | semmle.label | s | -| typed.ts:8:40:8:40 | s | semmle.label | s | subpaths diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts index 1c50460050cf..8c166fb243ff 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts @@ -3,9 +3,9 @@ export function basicHtmlConstruction(s: string) { // $ Source document.body.innerHTML = html; } -export function insertIntoCreatedDocument(s: string) { // $ Source +export function insertIntoCreatedDocument(s: string) { const newDoc = document.implementation.createHTMLDocument(""); - newDoc.body.innerHTML = "" + s + ""; // $ SPURIOUS: Alert - inserted into document disconnected from the main DOM. + newDoc.body.innerHTML = "" + s + ""; // OK - inserted into document disconnected from the main DOM. } export function id(s: string) { @@ -17,4 +17,3 @@ export function notVulnerable() { const html = "" + s + ""; document.body.innerHTML = html; } - \ No newline at end of file From 307715a5cdc2b2aa5831444c014d1eaf1b620daf Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 22 Apr 2025 08:47:00 +0200 Subject: [PATCH 21/47] JS: Use type resolution for CG augmentation --- javascript/ql/lib/semmle/javascript/Expr.qll | 7 ++++++- .../ql/lib/semmle/javascript/internal/TypeResolution.qll | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/Expr.qll b/javascript/ql/lib/semmle/javascript/Expr.qll index 8695c893f815..d7fe610b4f11 100644 --- a/javascript/ql/lib/semmle/javascript/Expr.qll +++ b/javascript/ql/lib/semmle/javascript/Expr.qll @@ -4,6 +4,7 @@ import javascript private import semmle.javascript.internal.CachedStages +private import semmle.javascript.internal.TypeResolution /** * A program element that is either an expression or a type annotation. @@ -1017,7 +1018,11 @@ class InvokeExpr extends @invokeexpr, Expr { * 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() { result = this.getResolvedCalleeName().getImplementation() } + Function getResolvedCallee() { + TypeResolution::callTarget(this, result) + or + result = this.getResolvedCalleeName().getImplementation() + } } /** diff --git a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll index 61293f29978d..f3b90a642da0 100644 --- a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll @@ -111,7 +111,7 @@ module TypeResolution { member.(AST::ValueNode).flow(), contents) } - private predicate callTarget(InvokeExpr call, Function target) { + predicate callTarget(InvokeExpr call, Function target) { exists(ClassDefinition cls | valueHasType(call.(NewExpr).getCallee(), trackClassValue(cls)) and target = cls.getConstructor().getBody() From f06b9a9b2bf0bac69c26efceec474ea4a6ef10eb Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 30 Apr 2025 14:00:50 +0200 Subject: [PATCH 22/47] JS: Add call graph test with types --- .../CallGraphs/AnnotatedTest/types.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 javascript/ql/test/library-tests/CallGraphs/AnnotatedTest/types.ts diff --git a/javascript/ql/test/library-tests/CallGraphs/AnnotatedTest/types.ts b/javascript/ql/test/library-tests/CallGraphs/AnnotatedTest/types.ts new file mode 100644 index 000000000000..5081c4901e41 --- /dev/null +++ b/javascript/ql/test/library-tests/CallGraphs/AnnotatedTest/types.ts @@ -0,0 +1,16 @@ +namespace NS { + export class C { + /** name:NS.C.m */ + m() { } + } + + export class D extends C { } +} + +function t1(c: NS.C, d: NS.D) { + /** calls:NS.C.m */ + c.m(); + + /** calls:NS.C.m */ + d.m(); +} From 500291dd548292a951cb7d9d452855b77f503d5d Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 30 Apr 2025 14:01:11 +0200 Subject: [PATCH 23/47] JS: Hide shadowed inherited members --- .../javascript/internal/TypeResolution.qll | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll index f3b90a642da0..0b1739b67df6 100644 --- a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll @@ -34,9 +34,9 @@ module TypeResolution { } /** - * Holds if `host` is a type with a `content` of type `memberType`. + * Holds if `host` is a type with a `content` of type `memberType`, not counting inherited members. */ - private predicate typeMember(Node host, DataFlow::Content content, Node memberType) { + private predicate typeOwnMember(Node host, DataFlow::Content content, Node memberType) { exists(MemberDeclaration decl | host = getMemberBase(decl) | exists(FieldDeclaration field | decl = field and @@ -46,8 +46,13 @@ module TypeResolution { or exists(MethodDeclaration method | decl = method and - content.asPropertyName() = method.getName() and + content.asPropertyName() = method.getName() + | + not method instanceof AccessorMethodDeclaration and memberType = method.getBody() // use the Function as representative for the function type + or + method instanceof GetterMethodDeclaration and + memberType = method.getBody().getReturnTypeAnnotation() ) or decl instanceof IndexSignature and @@ -72,8 +77,16 @@ module TypeResolution { memberType = type.getArgument(0) ) ) + } + + /** + * Holds if `host` is a type with a `content` of type `memberType`, possible due to inheritance. + */ + private predicate typeMember(Node host, DataFlow::Content content, Node memberType) { + typeOwnMember(host, content, memberType) or // Inherit members from base types + not typeOwnMember(host, content, _) and exists(ClassOrInterface baseType | typeMember(baseType, content, memberType) | host.(ClassDefinition).getSuperClass() = trackClassValue(baseType) or From 167f75230184edca81bc1d6392c5a3ef1f3b2b55 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 30 Apr 2025 14:15:07 +0200 Subject: [PATCH 24/47] JS: Also propagate through promise types --- .../semmle/javascript/dataflow/internal/Contents.qll | 8 ++++++++ .../lib/semmle/javascript/internal/TypeResolution.qll | 8 +++++++- .../library-tests/CallGraphs/AnnotatedTest/types.ts | 11 +++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/Contents.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/Contents.qll index 5cf5bf1e48eb..a5af2737c186 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/Contents.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/Contents.qll @@ -179,6 +179,9 @@ module Public { /** Holds if this represents values stored at an unknown array index. */ predicate isUnknownArrayElement() { this = MkArrayElementUnknown() } + /** Holds if this represents the value of a resolved promise. */ + predicate isPromiseValue() { this = MkPromiseValue() } + /** Holds if this represents values stored in a `Map` at an unknown key. */ predicate isMapValueWithUnknownKey() { this = MkMapValueWithUnknownKey() } @@ -266,6 +269,11 @@ module Public { or this = ContentSet::anyCapturedContent() and result instanceof Private::MkCapturedContent + or + // Although data flow will never use the special `Awaited` ContentSet in a read or store step, + // it may appear in type-tracking and type resolution, and here it helps to treat is as `Awaited[value]`. + this = MkAwaited() and + result = MkPromiseValue() } /** Gets the singleton content to be accessed. */ diff --git a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll index 0b1739b67df6..76e8dd2210fc 100644 --- a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll @@ -60,7 +60,7 @@ module TypeResolution { content.isUnknownArrayElement() ) or - // Ad-hoc support for array types. We don't support generics in general currently, we just special-case arrays. + // Ad-hoc support for array types. We don't support generics in general currently, we just special-case arrays and promises. content.isUnknownArrayElement() and ( memberType = host.(ArrayTypeExpr).getElementType() @@ -77,6 +77,9 @@ module TypeResolution { memberType = type.getArgument(0) ) ) + or + content.isPromiseValue() and + memberType = unwrapPromiseType(host) } /** @@ -120,6 +123,9 @@ module TypeResolution { object.(ObjectPattern).getPropertyPatternByName(contents.asPropertyName()).getValuePattern() = member or + member.(AwaitExpr).getOperand() = object and + contents = DataFlow::ContentSet::promiseValue() + or SummaryTypeTracker::basicLoadStep(object.(AST::ValueNode).flow(), member.(AST::ValueNode).flow(), contents) } diff --git a/javascript/ql/test/library-tests/CallGraphs/AnnotatedTest/types.ts b/javascript/ql/test/library-tests/CallGraphs/AnnotatedTest/types.ts index 5081c4901e41..6dd94bcf1952 100644 --- a/javascript/ql/test/library-tests/CallGraphs/AnnotatedTest/types.ts +++ b/javascript/ql/test/library-tests/CallGraphs/AnnotatedTest/types.ts @@ -14,3 +14,14 @@ function t1(c: NS.C, d: NS.D) { /** calls:NS.C.m */ d.m(); } + +async function t2(cp: Promise) { + const c = await cp; + /** calls:NS.C.m */ + c.m(); + + cp.then(c2 => { + /** calls:NS.C.m */ + c2.m(); + }) +} From 6e82b6eb1f7e734155bd4eccb625d4a501fdfd87 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 30 Apr 2025 14:32:58 +0200 Subject: [PATCH 25/47] JS: Add failing test for assigning a non-SourceNode to a type annotated-value --- .../ql/test/library-tests/UnderlyingTypes/varAssignment.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/varAssignment.ts diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/varAssignment.ts b/javascript/ql/test/library-tests/UnderlyingTypes/varAssignment.ts new file mode 100644 index 000000000000..63f3fe364cc6 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/varAssignment.ts @@ -0,0 +1,5 @@ +import * as express from 'express'; + +function t1(e) { + var req: express.Request = e; // $ MISSING: hasUnderlyingType='express'.Request +} From e07a03619dddfb25ad0b6b2a28033e89d2b4c45b Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 14 Apr 2025 13:47:26 +0200 Subject: [PATCH 26/47] JS: Mark type-annotated nodes as SourceNode --- javascript/ql/lib/semmle/javascript/dataflow/Sources.qll | 8 +++++++- .../ql/lib/semmle/javascript/internal/TypeResolution.qll | 9 ++++++++- .../ql/test/library-tests/UnderlyingTypes/test.expected | 1 + .../test/library-tests/UnderlyingTypes/varAssignment.ts | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll b/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll index 06729815e9a4..ad0be2765115 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll @@ -333,7 +333,13 @@ module SourceNode { astNode instanceof TaggedTemplateExpr or astNode instanceof Templating::PipeRefExpr or astNode instanceof Templating::TemplateVarRefExpr or - astNode instanceof StringLiteral + astNode instanceof StringLiteral or + astNode instanceof TypeAssertion + ) + or + exists(VariableDeclarator decl | + exists(decl.getTypeAnnotation()) and + this = DataFlow::valueNode(decl.getBindingPattern()) ) or DataFlow::parameterNode(this, _) diff --git a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll index 76e8dd2210fc..3df10f594e72 100644 --- a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll @@ -189,7 +189,12 @@ module TypeResolution { ) } - private predicate contextualType(Node value, Node type) { + predicate contextualType(Node value, Node type) { + exists(LocalVariable v | + type = v.getADeclaration().getTypeAnnotation() and + value = v.getAnAssignedExpr() + ) + or exists(InvokeExpr call, Function target, int i | callTarget(call, target) and value = call.getArgument(i) and @@ -228,6 +233,8 @@ module TypeResolution { predicate valueHasType(Node value, Node type) { value.(BindingPattern).getTypeAnnotation() = type or + value.(TypeAssertion).getTypeAnnotation() = type + or exists(VarDecl decl | // ValueFlow::step is restricted to variables with at most one assignment. Allow the type annotation // of a variable to propagate to its uses, even if the variable has multiple assignments. diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/test.expected b/javascript/ql/test/library-tests/UnderlyingTypes/test.expected index 8c8d2716a064..5ed9f42a7a53 100644 --- a/javascript/ql/test/library-tests/UnderlyingTypes/test.expected +++ b/javascript/ql/test/library-tests/UnderlyingTypes/test.expected @@ -48,3 +48,4 @@ | subtype.ts:7:13:7:15 | req | 'express'.Request | | subtype.ts:13:13:13:15 | req | 'express'.Request | | subtype.ts:19:13:19:15 | req | 'express'.Request | +| varAssignment.ts:4:9:4:11 | req | 'express'.Request | diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/varAssignment.ts b/javascript/ql/test/library-tests/UnderlyingTypes/varAssignment.ts index 63f3fe364cc6..c7160e16561e 100644 --- a/javascript/ql/test/library-tests/UnderlyingTypes/varAssignment.ts +++ b/javascript/ql/test/library-tests/UnderlyingTypes/varAssignment.ts @@ -1,5 +1,5 @@ import * as express from 'express'; function t1(e) { - var req: express.Request = e; // $ MISSING: hasUnderlyingType='express'.Request + var req: express.Request = e; // $ hasUnderlyingType='express'.Request } From fbafd6fff1008b1c9151d4beee2c91fce2031178 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 2 May 2025 13:43:12 +0200 Subject: [PATCH 27/47] JS: Update to avoid deprecations after import resolution change --- .../semmle/javascript/internal/NameResolution.qll | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll index f6f36cc7504b..1ac63d107eb3 100644 --- a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll @@ -55,11 +55,11 @@ module NameResolution { // This ensures we can also use the PathExpr as a source when working with external (unresolved) modules. exists(Import imprt | node1 = imprt.getImportedModule() and - node2 = imprt.getImportedPath() + node2 = imprt.getImportedPathExpr() ) or exists(ImportNamespaceSpecifier spec | - node1 = spec.getImportDeclaration().getImportedPath() and + node1 = spec.getImportDeclaration().getImportedPathExpr() and node2 = spec.getLocal() ) or @@ -79,7 +79,7 @@ module NameResolution { ) or exists(ExternalModuleReference ref | - node1 = ref.getImportedPath() and + node1 = ref.getImportedPathExpr() and node2 = ref ) or @@ -195,7 +195,7 @@ module NameResolution { ) or exists(ImportSpecifier spec | - node1 = spec.getImportDeclaration().getImportedPath() and + node1 = spec.getImportDeclaration().getImportedPathExpr() and name = spec.getImportedName() and node2 = spec.getLocal() ) @@ -411,12 +411,12 @@ module NameResolution { * Unlike `trackModule`, this is intended to track uses of external packages. */ predicate nodeRefersToModule(Node node, string mod, string qualifiedName) { - exists(PathExpr path | - path = any(Import imprt).getImportedPath() or + exists(Expr path | + path = any(Import imprt).getImportedPathExpr() or path = any(ReExportDeclaration e).getImportedPath() | node = path and - mod = normalizeModuleName(path.getValue()) and + mod = normalizeModuleName(path.getStringValue()) and isExternalModuleName(mod) and qualifiedName = "" ) From b8dc1b3125e3bb3c830070bca924f3ab7c37bf75 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 2 May 2025 13:47:32 +0200 Subject: [PATCH 28/47] JS: Remove redundant casts --- .../ql/lib/semmle/javascript/internal/TypeResolution.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll index 3df10f594e72..d0420f177e05 100644 --- a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll @@ -136,9 +136,9 @@ module TypeResolution { target = cls.getConstructor().getBody() ) or - valueHasType(call.(InvokeExpr).getCallee(), trackFunctionValue(target)) + valueHasType(call.getCallee(), trackFunctionValue(target)) or - valueHasType(call.(InvokeExpr).getCallee(), trackFunctionType(target)) and + valueHasType(call.getCallee(), trackFunctionType(target)) and ( call instanceof NewExpr and target = any(ConstructorTypeExpr t).getFunction() @@ -148,7 +148,7 @@ module TypeResolution { ) or exists(InterfaceDefinition interface, CallSignature sig | - valueHasType(call.(InvokeExpr).getCallee(), trackType(interface)) and + valueHasType(call.getCallee(), trackType(interface)) and sig = interface.getACallSignature() and target = sig.getBody() | From bba872a3a41211b0333ee767e1cda461d2920d11 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 12 May 2025 10:04:59 +0200 Subject: [PATCH 29/47] JS: Make jump-to-def behave nicer --- javascript/ql/lib/definitions.qll | 50 +++++++++++++++---- .../library-tests/DataFlow/tests.expected | 1 + .../definitions/definitions.expected | 26 +++++----- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/javascript/ql/lib/definitions.qll b/javascript/ql/lib/definitions.qll index 2cc9313d3132..6c9986288212 100644 --- a/javascript/ql/lib/definitions.qll +++ b/javascript/ql/lib/definitions.qll @@ -153,17 +153,7 @@ private predicate jsdocTypeLookup(JSDocNamedTypeExpr ref, AstNode decl, string k kind = "T" } -/** - * Gets an element, of kind `kind`, that element `e` uses, if any. - * - * The `kind` is a string representing what kind of use it is: - * - `"M"` for function and method calls - * - `"T"` for uses of types - * - `"V"` for variable accesses - * - `"I"` for imports - */ -cached -AstNode definitionOf(Locatable e, string kind) { +private AstNode definitionOfRaw(Locatable e, string kind) { variableDefLookup(e, result, kind) or // prefer definitions over declarations @@ -179,3 +169,41 @@ AstNode definitionOf(Locatable e, string kind) { or jsdocTypeLookup(e, result, kind) } + +/** Gets a more useful node to show for something that resolves to `node`. */ +private AstNode redirectOnce(AstNode node) { + exists(ConstructorDeclaration ctor | + ctor.isSynthetic() and + node = ctor.getBody() and + result = ctor.getDeclaringClass() + ) + or + exists(ClassDefinition cls | + node = cls and + result = cls.getIdentifier() + ) + or + exists(MethodDeclaration member | + not member instanceof ConstructorDeclaration and + node = member.getBody() and + result = member.getNameExpr() + ) +} + +private AstNode redirect(AstNode node) { + node = definitionOfRaw(_, _) and + result = redirectOnce*(node) and + not exists(redirectOnce(result)) +} + +/** + * Gets an element, of kind `kind`, that element `e` uses, if any. + * + * The `kind` is a string representing what kind of use it is: + * - `"M"` for function and method calls + * - `"T"` for uses of types + * - `"V"` for variable accesses + * - `"I"` for imports + */ +cached +AstNode definitionOf(Locatable e, string kind) { result = redirect(definitionOfRaw(e, kind)) } diff --git a/javascript/ql/test/library-tests/DataFlow/tests.expected b/javascript/ql/test/library-tests/DataFlow/tests.expected index 55c6771eef02..e3f6073461a0 100644 --- a/javascript/ql/test/library-tests/DataFlow/tests.expected +++ b/javascript/ql/test/library-tests/DataFlow/tests.expected @@ -1514,6 +1514,7 @@ sources | tst2.ts:7:1:9:1 | return of function setX | | tst2.ts:8:3:8:5 | A.x | | tst2.ts:11:11:11:13 | A.x | +| tst2.ts:11:11:11:23 | A.x as number | | tst2.ts:13:1:13:40 | class S ... ing> {} | | tst2.ts:13:26:13:29 | List | | tst2.ts:13:39:13:38 | (...arg ... rgs); } | diff --git a/javascript/ql/test/query-tests/definitions/definitions.expected b/javascript/ql/test/query-tests/definitions/definitions.expected index 081db47c3faa..cdf3136eb386 100644 --- a/javascript/ql/test/query-tests/definitions/definitions.expected +++ b/javascript/ql/test/query-tests/definitions/definitions.expected @@ -2,22 +2,22 @@ | b.js:7:1:7:1 | f | b.js:1:1:5:1 | functio ... ar x;\\n} | M | | b.js:8:1:8:1 | g | a.js:2:1:2:15 | function g() {} | M | | client.ts:1:22:1:30 | "./tslib" | tslib.ts:1:1:10:0 | | I | -| client.ts:7:19:7:19 | C | tslib.ts:1:8:3:1 | class C {\\n m() {}\\n} | T | -| client.ts:8:10:8:10 | C | client.ts:3:1:5:1 | class C {\\n m() {}\\n} | T | -| client.ts:9:16:9:16 | C | client.ts:3:1:5:1 | class C {\\n m() {}\\n} | T | -| client.ts:10:16:10:16 | C | tslib.ts:6:10:8:3 | class C ... {}\\n } | T | -| client.ts:13:25:13:25 | C | client.ts:3:1:5:1 | class C {\\n m() {}\\n} | T | -| client.ts:13:35:13:35 | C | tslib.ts:1:8:3:1 | class C {\\n m() {}\\n} | T | -| client.ts:13:47:13:47 | C | tslib.ts:6:10:8:3 | class C ... {}\\n } | T | +| client.ts:7:19:7:19 | C | tslib.ts:1:14:1:14 | C | T | +| client.ts:8:10:8:10 | C | client.ts:3:7:3:7 | C | T | +| client.ts:9:16:9:16 | C | client.ts:3:7:3:7 | C | T | +| client.ts:10:16:10:16 | C | tslib.ts:6:16:6:16 | C | T | +| client.ts:13:25:13:25 | C | client.ts:3:7:3:7 | C | T | +| client.ts:13:35:13:35 | C | tslib.ts:1:14:1:14 | C | T | +| client.ts:13:47:13:47 | C | tslib.ts:6:16:6:16 | C | T | | client.ts:14:3:14:3 | x | client.ts:13:22:13:22 | x | V | -| client.ts:14:5:14:5 | m | client.ts:4:3:4:8 | m() {} | M | +| client.ts:14:5:14:5 | m | client.ts:4:3:4:3 | m | M | | client.ts:15:3:15:3 | y | client.ts:13:28:13:28 | y | V | -| client.ts:15:5:15:5 | m | tslib.ts:2:3:2:8 | m() {} | M | +| client.ts:15:5:15:5 | m | tslib.ts:2:3:2:3 | m | M | | client.ts:16:3:16:3 | z | client.ts:13:38:13:38 | z | V | -| client.ts:16:5:16:5 | m | tslib.ts:7:5:7:10 | m() {} | M | +| client.ts:16:5:16:5 | m | tslib.ts:7:5:7:5 | m | M | | d.js:1:17:1:21 | './c' | c.js:1:1:1:20 | | I | | d.js:10:1:10:1 | A | d.js:7:1:9:1 | functio ... = 42;\\n} | V | -| d.js:16:19:16:23 | Super | d.js:12:1:14:1 | class S ... () {}\\n} | V | +| d.js:16:19:16:23 | Super | d.js:12:7:12:11 | Super | V | | d.js:16:25:16:24 | args | d.js:16:25:16:24 | args | V | | d.js:20:1:20:1 | o | d.js:3:9:5:1 | {\\n f: ... () {}\\n} | V | | d.js:20:3:20:3 | f | d.js:4:3:4:18 | f: function() {} | M | @@ -26,11 +26,11 @@ | d.js:23:3:23:3 | x | d.js:8:3:8:8 | this.x | M | | d.js:24:1:24:1 | a | d.js:22:5:22:5 | a | V | | d.js:24:3:24:3 | g | d.js:10:1:10:13 | A.prototype.g | M | -| d.js:26:13:26:15 | Sub | d.js:16:1:18:1 | class S ... () {}\\n} | M | +| d.js:26:13:26:15 | Sub | d.js:16:7:16:9 | Sub | M | | d.js:27:1:27:1 | x | d.js:26:5:26:5 | x | V | | d.js:27:3:27:3 | m | d.js:13:3:13:3 | m | M | | d.js:28:1:28:1 | x | d.js:26:5:26:5 | x | V | | d.js:28:3:28:3 | n | d.js:17:3:17:3 | n | M | | tst.js:1:19:1:23 | './m' | m.js:1:1:2:0 | | I | -| tst.js:3:5:3:5 | A | m.js:1:8:1:17 | class A {} | M | +| tst.js:3:5:3:5 | A | m.js:1:14:1:14 | A | M | | tst.js:5:15:5:19 | './m' | m.js:1:1:2:0 | | I | From de7d851195119339fe3a36c9824c11ff0580fe9e Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 12 May 2025 10:31:13 +0200 Subject: [PATCH 30/47] JS: Update output of old HasUnderlyingType test --- .../TypeScript/HasUnderlyingType/HasUnderlyingType.expected | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected index e330bb897b7d..91eb164f394c 100644 --- a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected +++ b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected @@ -5,5 +5,6 @@ | tst.ts:8:14:8:16 | arg | Sub in global scope | underlyingTypeNode | foo | | file://:0:0:0:0 | use moduleImport("foo").getMember("exports") | +| foo | | file://:0:0:0:0 | use moduleImport("foo").getMember("exports").getMember("") | | foo | | foo.ts:1:8:1:10 | use moduleImport("foo").getMember("exports").getMember("default") | | foo | Bar | foo.ts:3:12:3:12 | use moduleImport("foo").getMember("exports").getMember("Bar").getInstance() | From 22a41142de3f74017bc7116c675d99560104302d Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 12 May 2025 10:33:59 +0200 Subject: [PATCH 31/47] JS: Accept regression in overload resolution Overload resolution has little impact on data flow analysis, because there we care about the concrete implementation of the function, which is the same for all overloads. It can affect the return type, which in turn can affect the call graph we generate, but we'll just have to accept this as overload resolution is too hard without negative recursion. --- .../CallResolution/CallTarget.expected | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/javascript/ql/test/library-tests/TypeScript/CallResolution/CallTarget.expected b/javascript/ql/test/library-tests/TypeScript/CallResolution/CallTarget.expected index a91505e3f034..69374cbf4bf8 100644 --- a/javascript/ql/test/library-tests/TypeScript/CallResolution/CallTarget.expected +++ b/javascript/ql/test/library-tests/TypeScript/CallResolution/CallTarget.expected @@ -1,16 +1,36 @@ -| tst.ts:52:3:52:23 | obj.sim ... od(str) | TestInterface.simpleMethod in global scope | no concrete target | -| tst.ts:53:3:53:24 | obj.gen ... od(str) | TestInterface.genericMethod in global scope | no concrete target | -| tst.ts:54:3:54:24 | obj.gen ... od(num) | TestInterface.genericMethod in global scope | no concrete target | -| tst.ts:55:3:55:27 | obj.ove ... od(num) | TestInterface.overloadedMethod in global scope | no concrete target | -| tst.ts:56:3:56:27 | obj.ove ... od(str) | TestInterface.overloadedMethod in global scope | no concrete target | -| tst.ts:57:3:57:26 | obj.ove ... hod([]) | TestInterface.overloadedMethod in global scope | no concrete target | -| tst.ts:58:3:58:36 | obj.gen ... ([num]) | TestInterface.genericOverloadedMethod in global scope | no concrete target | -| tst.ts:59:3:59:39 | obj.gen ... : str}) | TestInterface.genericOverloadedMethod in global scope | no concrete target | -| tst.ts:60:3:60:34 | obj.gen ... od(num) | TestInterface.genericOverloadedMethod in global scope | no concrete target | +| 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; } | From b610e10122dc75f2dbb8bb6f0826a69b4bb53417 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 12 May 2025 10:36:09 +0200 Subject: [PATCH 32/47] JS: Accept change in handling of variable resolution in face of ambient declarations This test enforced the opinion that ambient declarations should have no impact on data flow, which is no longer the case. For now I'm just updating the test output. --- .../library-tests/TypeScript/Ambients/Ambients.expected | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/javascript/ql/test/library-tests/TypeScript/Ambients/Ambients.expected b/javascript/ql/test/library-tests/TypeScript/Ambients/Ambients.expected index 7a60484a5f72..63e749e97be8 100644 --- a/javascript/ql/test/library-tests/TypeScript/Ambients/Ambients.expected +++ b/javascript/ql/test/library-tests/TypeScript/Ambients/Ambients.expected @@ -1 +1,7 @@ -| tst.ts:38:3:38:19 | resolveAmbient(x) | x should not resolve to a global | +| tst.ts:22:3:22:18 | resolveGlobal(x) | x should resolve to a global variable | +| tst.ts:23:3:23:18 | resolveGlobal(y) | y should resolve to a global variable | +| tst.ts:24:3:24:18 | resolveGlobal(z) | z should resolve to a global variable | +| tst.ts:25:3:25:18 | resolveGlobal(w) | w should resolve to a global variable | +| tst.ts:39:3:39:18 | resolveGlobal(y) | y should resolve to a global variable | +| tst.ts:40:3:40:18 | resolveGlobal(z) | z should resolve to a global variable | +| tst.ts:41:3:41:18 | resolveGlobal(w) | w should resolve to a global variable | From 27979c6a2fe7821bbe8e95844900186984c66c71 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 13 May 2025 15:57:51 +0200 Subject: [PATCH 33/47] JS: Add regression tests for declared globals --- .../Security/CWE-079/DomBasedXss/jquery-declare-any.ts | 7 +++++++ .../Security/CWE-079/DomBasedXss/jquery-declare-type.ts | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-any.ts create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-type.ts diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-any.ts b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-any.ts new file mode 100644 index 000000000000..fc0dfbc7390e --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-any.ts @@ -0,0 +1,7 @@ +import 'dummy'; + +declare var $: any; + +function t() { + $(window.name); // $ MISSING: Alert +} diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-type.ts b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-type.ts new file mode 100644 index 000000000000..072f98a53779 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-type.ts @@ -0,0 +1,7 @@ +import 'dummy'; + +declare var $: JQueryStatic; + +function t() { + $(window.name); // $ MISSING: Alert +} From 9bcc62002d2a387a3bf4c985417738f84380523d Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 13 May 2025 16:16:33 +0200 Subject: [PATCH 34/47] JS: Fix regression from global declare vars --- .../ql/lib/semmle/javascript/Variables.qll | 20 ++++++++++++++++- .../javascript/internal/NameResolution.qll | 22 ++++++++++++++++--- .../javascript/internal/TypeResolution.qll | 4 ++-- .../Security/CWE-079/DomBasedXss/Xss.expected | 4 ++++ .../XssWithAdditionalSources.expected | 2 ++ .../CWE-079/DomBasedXss/jquery-declare-any.ts | 2 +- .../DomBasedXss/jquery-declare-type.ts | 2 +- 7 files changed, 48 insertions(+), 8 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/Variables.qll b/javascript/ql/lib/semmle/javascript/Variables.qll index e8abb6ad9819..2f9905f86e17 100644 --- a/javascript/ql/lib/semmle/javascript/Variables.qll +++ b/javascript/ql/lib/semmle/javascript/Variables.qll @@ -134,8 +134,26 @@ class Variable extends @variable, LexicalName { /** Gets the scope this variable is declared in. */ override Scope getScope() { variables(this, _, result) } + /** + * Holds if this variable is declared in the top-level of a module using a `declare` statement. + * + * For example: + * ```js + * declare var $: any; + * ``` + * + * Such variables are generally treated as a global variables, except for type-checking related purposes. + */ + pragma[nomagic] + predicate isTopLevelWithAmbientDeclaration() { + this.getScope() instanceof ModuleScope and + forex(VarDecl decl | decl = this.getADeclaration() | decl.isAmbient()) + } + /** Holds if this is a global variable. */ - predicate isGlobal() { this.getScope() instanceof GlobalScope } + predicate isGlobal() { + this.getScope() instanceof GlobalScope or this.isTopLevelWithAmbientDeclaration() + } /** * Holds if this is a variable exported from a TypeScript namespace. diff --git a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll index 1ac63d107eb3..8a9a1c71edce 100644 --- a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll @@ -28,7 +28,7 @@ module NameResolution { Location getLocation() { result = this.(AstNode).getLocation() or - result = this.(LocalVariable).getLocation() + result = this.(LocalVariableLike).getLocation() or result = this.(JSDocTypeExpr).getLocation() } @@ -47,6 +47,22 @@ module NameResolution { } } + /** + * A local variable, or a top-level variable that acts as a global variable due to an ambient declaration. + */ + class LocalVariableLike extends Variable { + LocalVariableLike() { this.isLocal() or this.isTopLevelWithAmbientDeclaration() } + + Location getLocation() { + result = + min(Location loc | + loc = this.getADeclaration().getLocation() + | + loc order by loc.getStartLine(), loc.getStartColumn() + ) + } + } + /** * Holds if values/namespaces/types in `node1` can flow to values/namespaces/types in `node2`. */ @@ -224,7 +240,7 @@ module NameResolution { /** * A local variable with exactly one definition, not counting implicit initialization. */ - private class EffectivelyConstantVariable extends LocalVariable { + private class EffectivelyConstantVariable extends LocalVariableLike { EffectivelyConstantVariable() { count(SsaExplicitDefinition ssa | ssa.getSourceVariable() = this) <= 1 // count may be zero if ambient } @@ -294,7 +310,7 @@ module NameResolution { * Holds if `value` is stored in `target.prop`. Only needs to recognise assignments * that are also recognised by JSDoc tooling such as the Closure compiler. */ - private predicate storeToVariable(Expr value, string prop, LocalVariable target) { + private predicate storeToVariable(Expr value, string prop, LocalVariableLike target) { exists(AssignExpr assign | // exports.name = value assign.getLhs().(PropAccess).accesses(target.getAnAccess(), prop) and diff --git a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll index d0420f177e05..cb575f20ab14 100644 --- a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll @@ -190,7 +190,7 @@ module TypeResolution { } predicate contextualType(Node value, Node type) { - exists(LocalVariable v | + exists(LocalVariableLike v | type = v.getADeclaration().getTypeAnnotation() and value = v.getAnAssignedExpr() ) @@ -239,7 +239,7 @@ module TypeResolution { // ValueFlow::step is restricted to variables with at most one assignment. Allow the type annotation // of a variable to propagate to its uses, even if the variable has multiple assignments. type = decl.getTypeAnnotation() and - value = decl.getVariable().(LocalVariable).getAnAccess() + value = decl.getVariable().(LocalVariableLike).getAnAccess() ) or exists(MemberDeclaration member | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected index 6ba8ab703bff..0f5659492116 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected @@ -62,6 +62,8 @@ | dragAndDrop.ts:73:29:73:39 | droppedHtml | dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | dragAndDrop.ts:73:29:73:39 | droppedHtml | Cross-site scripting vulnerability due to $@. | dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | user-provided value | | event-handler-receiver.js:2:31:2:83 | '

' | event-handler-receiver.js:2:49:2:61 | location.href | event-handler-receiver.js:2:31:2:83 | '

' | Cross-site scripting vulnerability due to $@. | event-handler-receiver.js:2:49:2:61 | location.href | user-provided value | | express.js:6:15:6:33 | req.param("wobble") | express.js:6:15:6:33 | req.param("wobble") | express.js:6:15:6:33 | req.param("wobble") | Cross-site scripting vulnerability due to $@. | express.js:6:15:6:33 | req.param("wobble") | user-provided value | +| jquery-declare-any.ts:6:7:6:17 | window.name | jquery-declare-any.ts:6:7:6:17 | window.name | jquery-declare-any.ts:6:7:6:17 | window.name | Cross-site scripting vulnerability due to $@. | jquery-declare-any.ts:6:7:6:17 | window.name | user-provided value | +| jquery-declare-type.ts:6:7:6:17 | window.name | jquery-declare-type.ts:6:7:6:17 | window.name | jquery-declare-type.ts:6:7:6:17 | window.name | Cross-site scripting vulnerability due to $@. | jquery-declare-type.ts:6:7:6:17 | window.name | user-provided value | | jquery.js:7:5:7:34 | "
" | jquery.js:2:17:2:40 | documen ... .search | jquery.js:7:5:7:34 | "
" | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:40 | documen ... .search | user-provided value | | jquery.js:8:18:8:34 | "XSS: " + tainted | jquery.js:2:17:2:40 | documen ... .search | jquery.js:8:18:8:34 | "XSS: " + tainted | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:40 | documen ... .search | user-provided value | | jquery.js:10:5:10:40 | "" + ... "" | jquery.js:10:13:10:20 | location | jquery.js:10:5:10:40 | "" + ... "" | Cross-site scripting vulnerability due to $@. | jquery.js:10:13:10:20 | location | user-provided value | @@ -954,6 +956,8 @@ nodes | event-handler-receiver.js:2:31:2:83 | '

' | semmle.label | '

' | | event-handler-receiver.js:2:49:2:61 | location.href | semmle.label | location.href | | express.js:6:15:6:33 | req.param("wobble") | semmle.label | req.param("wobble") | +| jquery-declare-any.ts:6:7:6:17 | window.name | semmle.label | window.name | +| jquery-declare-type.ts:6:7:6:17 | window.name | semmle.label | window.name | | jquery.js:2:7:2:40 | tainted | semmle.label | tainted | | jquery.js:2:17:2:40 | documen ... .search | semmle.label | documen ... .search | | jquery.js:4:5:4:11 | tainted | semmle.label | tainted | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected index 0ed15b8d92ab..c031b7c1810c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected @@ -182,6 +182,8 @@ nodes | hana.js:85:35:85:54 | tableRows[0].comment | semmle.label | tableRows[0].comment | | hana.js:90:33:90:34 | rs | semmle.label | rs | | hana.js:90:33:90:45 | rs[0].comment | semmle.label | rs[0].comment | +| jquery-declare-any.ts:6:7:6:17 | window.name | semmle.label | window.name | +| jquery-declare-type.ts:6:7:6:17 | window.name | semmle.label | window.name | | jquery.js:2:7:2:40 | tainted | semmle.label | tainted | | jquery.js:2:17:2:40 | documen ... .search | semmle.label | documen ... .search | | jquery.js:4:5:4:11 | tainted | semmle.label | tainted | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-any.ts b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-any.ts index fc0dfbc7390e..df8267bba306 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-any.ts +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-any.ts @@ -3,5 +3,5 @@ import 'dummy'; declare var $: any; function t() { - $(window.name); // $ MISSING: Alert + $(window.name); // $ Alert } diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-type.ts b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-type.ts index 072f98a53779..c866f71a1eb9 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-type.ts +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery-declare-type.ts @@ -3,5 +3,5 @@ import 'dummy'; declare var $: JQueryStatic; function t() { - $(window.name); // $ MISSING: Alert + $(window.name); // $ Alert } From 11607e5f621240fe220847968460552849b66891 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 May 2025 13:03:02 +0200 Subject: [PATCH 35/47] JS: Update TRAP after extractor change --- .../ts/output/trap/conditionalTypes.ts.trap | 2286 ++++++++--------- .../tests/ts/output/trap/declareClass.ts.trap | 103 +- .../ts/output/trap/functiondecorators.ts.trap | 131 +- .../tests/ts/output/trap/nobody.ts.trap | 889 +++---- .../ts/output/trap/thisparameter.ts.trap | 272 +- 5 files changed, 1844 insertions(+), 1837 deletions(-) diff --git a/javascript/extractor/tests/ts/output/trap/conditionalTypes.ts.trap b/javascript/extractor/tests/ts/output/trap/conditionalTypes.ts.trap index 0751d283e6cc..2a1c0efbd441 100644 --- a/javascript/extractor/tests/ts/output/trap/conditionalTypes.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/conditionalTypes.ts.trap @@ -8434,1316 +8434,1316 @@ namespacedecl(#22807,#22182) scopes(#22808,9) scopenodes(#22805,#22808) scopenesting(#22808,#20000) -#22809=@"local_type_name;{Unpacked};{#22808}" -local_type_names(#22809,"Unpacked",#22808) -#22810=@"local_type_name;{T0};{#22808}" -local_type_names(#22810,"T0",#22808) -#22811=@"local_type_name;{T1};{#22808}" -local_type_names(#22811,"T1",#22808) -#22812=@"local_type_name;{T2};{#22808}" -local_type_names(#22812,"T2",#22808) -#22813=@"local_type_name;{T3};{#22808}" -local_type_names(#22813,"T3",#22808) -#22814=@"local_type_name;{T4};{#22808}" -local_type_names(#22814,"T4",#22808) -#22815=@"local_type_name;{T5};{#22808}" -local_type_names(#22815,"T5",#22808) -#22816=@"local_type_name;{Foo};{#22808}" -local_type_names(#22816,"Foo",#22808) -#22817=@"local_type_name;{T10};{#22808}" -local_type_names(#22817,"T10",#22808) -#22818=@"local_type_name;{T11};{#22808}" -local_type_names(#22818,"T11",#22808) -#22819=@"local_type_name;{Bar};{#22808}" -local_type_names(#22819,"Bar",#22808) -#22820=@"local_type_name;{T20};{#22808}" -local_type_names(#22820,"T20",#22808) -#22821=@"local_type_name;{T21};{#22808}" -local_type_names(#22821,"T21",#22808) -#22822=@"local_type_name;{T30};{#22808}" -local_type_names(#22822,"T30",#22808) -#22823=@"local_type_name;{AnyFunction};{#22808}" -local_type_names(#22823,"AnyFunction",#22808) -#22824=@"local_type_name;{ReturnType};{#22808}" -local_type_names(#22824,"ReturnType",#22808) -#22825=* -stmts(#22825,35,#22805,0,"type Un ... \n T;") -#22826=@"loc,{#10000},73,3,77,6" -locations_default(#22826,#10000,73,3,77,6) -hasLocation(#22825,#22826) -stmt_containers(#22825,#22805) -#22827=* -typeexprs(#22827,1,#22825,0,"Unpacked") -hasLocation(#22827,#21482) -enclosing_stmt(#22827,#22825) -expr_containers(#22827,#22805) -literals("Unpacked","Unpacked",#22827) -typedecl(#22827,#22809) +#22809=@"var;{foo};{#22808}" +variables(#22809,"foo",#22808) +#22810=@"local_type_name;{Unpacked};{#22808}" +local_type_names(#22810,"Unpacked",#22808) +#22811=@"local_type_name;{T0};{#22808}" +local_type_names(#22811,"T0",#22808) +#22812=@"local_type_name;{T1};{#22808}" +local_type_names(#22812,"T1",#22808) +#22813=@"local_type_name;{T2};{#22808}" +local_type_names(#22813,"T2",#22808) +#22814=@"local_type_name;{T3};{#22808}" +local_type_names(#22814,"T3",#22808) +#22815=@"local_type_name;{T4};{#22808}" +local_type_names(#22815,"T4",#22808) +#22816=@"local_type_name;{T5};{#22808}" +local_type_names(#22816,"T5",#22808) +#22817=@"local_type_name;{Foo};{#22808}" +local_type_names(#22817,"Foo",#22808) +#22818=@"local_type_name;{T10};{#22808}" +local_type_names(#22818,"T10",#22808) +#22819=@"local_type_name;{T11};{#22808}" +local_type_names(#22819,"T11",#22808) +#22820=@"local_type_name;{Bar};{#22808}" +local_type_names(#22820,"Bar",#22808) +#22821=@"local_type_name;{T20};{#22808}" +local_type_names(#22821,"T20",#22808) +#22822=@"local_type_name;{T21};{#22808}" +local_type_names(#22822,"T21",#22808) +#22823=@"local_type_name;{T30};{#22808}" +local_type_names(#22823,"T30",#22808) +#22824=@"local_type_name;{AnyFunction};{#22808}" +local_type_names(#22824,"AnyFunction",#22808) +#22825=@"local_type_name;{ReturnType};{#22808}" +local_type_names(#22825,"ReturnType",#22808) +#22826=* +stmts(#22826,35,#22805,0,"type Un ... \n T;") +#22827=@"loc,{#10000},73,3,77,6" +locations_default(#22827,#10000,73,3,77,6) +hasLocation(#22826,#22827) +stmt_containers(#22826,#22805) #22828=* -scopes(#22828,12) -scopenodes(#22825,#22828) -scopenesting(#22828,#22808) -#22829=@"local_type_name;{T};{#22828}" -local_type_names(#22829,"T",#22828) -#22830=* -typeexprs(#22830,22,#22825,2,"T") -hasLocation(#22830,#21486) -enclosing_stmt(#22830,#22825) -expr_containers(#22830,#22805) +typeexprs(#22828,1,#22826,0,"Unpacked") +hasLocation(#22828,#21482) +enclosing_stmt(#22828,#22826) +expr_containers(#22828,#22805) +literals("Unpacked","Unpacked",#22828) +typedecl(#22828,#22810) +#22829=* +scopes(#22829,12) +scopenodes(#22826,#22829) +scopenesting(#22829,#22808) +#22830=@"local_type_name;{T};{#22829}" +local_type_names(#22830,"T",#22829) #22831=* -typeexprs(#22831,1,#22830,0,"T") +typeexprs(#22831,22,#22826,2,"T") hasLocation(#22831,#21486) -enclosing_stmt(#22831,#22825) +enclosing_stmt(#22831,#22826) expr_containers(#22831,#22805) -literals("T","T",#22831) -typedecl(#22831,#22829) #22832=* -typeexprs(#22832,28,#22825,1,"T exten ... :\n T") -#22833=@"loc,{#10000},74,5,77,5" -locations_default(#22833,#10000,74,5,77,5) -hasLocation(#22832,#22833) -enclosing_stmt(#22832,#22825) +typeexprs(#22832,1,#22831,0,"T") +hasLocation(#22832,#21486) +enclosing_stmt(#22832,#22826) expr_containers(#22832,#22805) -#22834=* -typeexprs(#22834,0,#22832,0,"T") -hasLocation(#22834,#21492) -enclosing_stmt(#22834,#22825) -expr_containers(#22834,#22805) -literals("T","T",#22834) -typebind(#22834,#22829) +literals("T","T",#22832) +typedecl(#22832,#22830) +#22833=* +typeexprs(#22833,28,#22826,1,"T exten ... :\n T") +#22834=@"loc,{#10000},74,5,77,5" +locations_default(#22834,#10000,74,5,77,5) +hasLocation(#22833,#22834) +enclosing_stmt(#22833,#22826) +expr_containers(#22833,#22805) #22835=* -scopes(#22835,16) -scopenodes(#22832,#22835) -scopenesting(#22835,#22828) -#22836=@"local_type_name;{U};{#22835}" -local_type_names(#22836,"U",#22835) -#22837=* -typeexprs(#22837,6,#22832,1,"(infer U)[]") -#22838=@"loc,{#10000},74,15,74,25" -locations_default(#22838,#10000,74,15,74,25) -hasLocation(#22837,#22838) -enclosing_stmt(#22837,#22825) -expr_containers(#22837,#22805) -#22839=* -typeexprs(#22839,10,#22837,0,"(infer U)") -#22840=@"loc,{#10000},74,15,74,23" -locations_default(#22840,#10000,74,15,74,23) -hasLocation(#22839,#22840) -enclosing_stmt(#22839,#22825) -expr_containers(#22839,#22805) -#22841=* -typeexprs(#22841,29,#22839,0,"infer U") -#22842=@"loc,{#10000},74,16,74,22" -locations_default(#22842,#10000,74,16,74,22) -hasLocation(#22841,#22842) -enclosing_stmt(#22841,#22825) -expr_containers(#22841,#22805) -#22843=* -typeexprs(#22843,22,#22841,0,"U") -hasLocation(#22843,#21500) -enclosing_stmt(#22843,#22825) -expr_containers(#22843,#22805) +typeexprs(#22835,0,#22833,0,"T") +hasLocation(#22835,#21492) +enclosing_stmt(#22835,#22826) +expr_containers(#22835,#22805) +literals("T","T",#22835) +typebind(#22835,#22830) +#22836=* +scopes(#22836,16) +scopenodes(#22833,#22836) +scopenesting(#22836,#22829) +#22837=@"local_type_name;{U};{#22836}" +local_type_names(#22837,"U",#22836) +#22838=* +typeexprs(#22838,6,#22833,1,"(infer U)[]") +#22839=@"loc,{#10000},74,15,74,25" +locations_default(#22839,#10000,74,15,74,25) +hasLocation(#22838,#22839) +enclosing_stmt(#22838,#22826) +expr_containers(#22838,#22805) +#22840=* +typeexprs(#22840,10,#22838,0,"(infer U)") +#22841=@"loc,{#10000},74,15,74,23" +locations_default(#22841,#10000,74,15,74,23) +hasLocation(#22840,#22841) +enclosing_stmt(#22840,#22826) +expr_containers(#22840,#22805) +#22842=* +typeexprs(#22842,29,#22840,0,"infer U") +#22843=@"loc,{#10000},74,16,74,22" +locations_default(#22843,#10000,74,16,74,22) +hasLocation(#22842,#22843) +enclosing_stmt(#22842,#22826) +expr_containers(#22842,#22805) #22844=* -typeexprs(#22844,1,#22843,0,"U") +typeexprs(#22844,22,#22842,0,"U") hasLocation(#22844,#21500) -enclosing_stmt(#22844,#22825) +enclosing_stmt(#22844,#22826) expr_containers(#22844,#22805) -literals("U","U",#22844) -typedecl(#22844,#22836) #22845=* -typeexprs(#22845,0,#22832,2,"U") -hasLocation(#22845,#21510) -enclosing_stmt(#22845,#22825) +typeexprs(#22845,1,#22844,0,"U") +hasLocation(#22845,#21500) +enclosing_stmt(#22845,#22826) expr_containers(#22845,#22805) literals("U","U",#22845) -typebind(#22845,#22836) +typedecl(#22845,#22837) #22846=* -typeexprs(#22846,28,#22832,3,"T exten ... :\n T") -#22847=@"loc,{#10000},75,5,77,5" -locations_default(#22847,#10000,75,5,77,5) -hasLocation(#22846,#22847) -enclosing_stmt(#22846,#22825) +typeexprs(#22846,0,#22833,2,"U") +hasLocation(#22846,#21510) +enclosing_stmt(#22846,#22826) expr_containers(#22846,#22805) -#22848=* -typeexprs(#22848,0,#22846,0,"T") -hasLocation(#22848,#21514) -enclosing_stmt(#22848,#22825) -expr_containers(#22848,#22805) -literals("T","T",#22848) -typebind(#22848,#22829) +literals("U","U",#22846) +typebind(#22846,#22837) +#22847=* +typeexprs(#22847,28,#22833,3,"T exten ... :\n T") +#22848=@"loc,{#10000},75,5,77,5" +locations_default(#22848,#10000,75,5,77,5) +hasLocation(#22847,#22848) +enclosing_stmt(#22847,#22826) +expr_containers(#22847,#22805) #22849=* -scopes(#22849,16) -scopenodes(#22846,#22849) -scopenesting(#22849,#22828) -#22850=@"local_type_name;{U};{#22849}" -local_type_names(#22850,"U",#22849) -#22851=* -typeexprs(#22851,23,#22846,1,"(...arg ... infer U") -#22852=@"loc,{#10000},75,15,75,41" -locations_default(#22852,#10000,75,15,75,41) -hasLocation(#22851,#22852) -enclosing_stmt(#22851,#22825) -expr_containers(#22851,#22805) -#22853=* -exprs(#22853,9,#22851,0,"(...arg ... infer U") -hasLocation(#22853,#22852) -enclosing_stmt(#22853,#22825) -expr_containers(#22853,#22805) +typeexprs(#22849,0,#22847,0,"T") +hasLocation(#22849,#21514) +enclosing_stmt(#22849,#22826) +expr_containers(#22849,#22805) +literals("T","T",#22849) +typebind(#22849,#22830) +#22850=* +scopes(#22850,16) +scopenodes(#22847,#22850) +scopenesting(#22850,#22829) +#22851=@"local_type_name;{U};{#22850}" +local_type_names(#22851,"U",#22850) +#22852=* +typeexprs(#22852,23,#22847,1,"(...arg ... infer U") +#22853=@"loc,{#10000},75,15,75,41" +locations_default(#22853,#10000,75,15,75,41) +hasLocation(#22852,#22853) +enclosing_stmt(#22852,#22826) +expr_containers(#22852,#22805) #22854=* -scopes(#22854,1) -scopenodes(#22853,#22854) -scopenesting(#22854,#22849) -#22855=@"var;{args};{#22854}" -variables(#22855,"args",#22854) -#22856=* -exprs(#22856,78,#22853,0,"args") -hasLocation(#22856,#21522) -expr_containers(#22856,#22853) -literals("args","args",#22856) -decl(#22856,#22855) -#22857=@"var;{arguments};{#22854}" -variables(#22857,"arguments",#22854) -is_arguments_object(#22857) -#22858=* -typeexprs(#22858,29,#22853,-3,"infer U") -#22859=@"loc,{#10000},75,35,75,41" -locations_default(#22859,#10000,75,35,75,41) -hasLocation(#22858,#22859) -expr_containers(#22858,#22853) -#22860=* -typeexprs(#22860,22,#22858,0,"U") -hasLocation(#22860,#21538) -expr_containers(#22860,#22853) +exprs(#22854,9,#22852,0,"(...arg ... infer U") +hasLocation(#22854,#22853) +enclosing_stmt(#22854,#22826) +expr_containers(#22854,#22805) +#22855=* +scopes(#22855,1) +scopenodes(#22854,#22855) +scopenesting(#22855,#22850) +#22856=@"var;{args};{#22855}" +variables(#22856,"args",#22855) +#22857=* +exprs(#22857,78,#22854,0,"args") +hasLocation(#22857,#21522) +expr_containers(#22857,#22854) +literals("args","args",#22857) +decl(#22857,#22856) +#22858=@"var;{arguments};{#22855}" +variables(#22858,"arguments",#22855) +is_arguments_object(#22858) +#22859=* +typeexprs(#22859,29,#22854,-3,"infer U") +#22860=@"loc,{#10000},75,35,75,41" +locations_default(#22860,#10000,75,35,75,41) +hasLocation(#22859,#22860) +expr_containers(#22859,#22854) #22861=* -typeexprs(#22861,1,#22860,0,"U") +typeexprs(#22861,22,#22859,0,"U") hasLocation(#22861,#21538) -expr_containers(#22861,#22853) -literals("U","U",#22861) -typedecl(#22861,#22850) +expr_containers(#22861,#22854) #22862=* -typeexprs(#22862,6,#22853,-6,"any[]") -#22863=@"loc,{#10000},75,25,75,29" -locations_default(#22863,#10000,75,25,75,29) -hasLocation(#22862,#22863) -expr_containers(#22862,#22853) -#22864=* -typeexprs(#22864,2,#22862,0,"any") -hasLocation(#22864,#21526) -expr_containers(#22864,#22853) -literals("any","any",#22864) -has_rest_parameter(#22853) +typeexprs(#22862,1,#22861,0,"U") +hasLocation(#22862,#21538) +expr_containers(#22862,#22854) +literals("U","U",#22862) +typedecl(#22862,#22851) +#22863=* +typeexprs(#22863,6,#22854,-6,"any[]") +#22864=@"loc,{#10000},75,25,75,29" +locations_default(#22864,#10000,75,25,75,29) +hasLocation(#22863,#22864) +expr_containers(#22863,#22854) #22865=* -typeexprs(#22865,0,#22846,2,"U") -hasLocation(#22865,#21542) -enclosing_stmt(#22865,#22825) -expr_containers(#22865,#22805) -literals("U","U",#22865) -typebind(#22865,#22850) +typeexprs(#22865,2,#22863,0,"any") +hasLocation(#22865,#21526) +expr_containers(#22865,#22854) +literals("any","any",#22865) +has_rest_parameter(#22854) #22866=* -typeexprs(#22866,28,#22846,3,"T exten ... :\n T") -#22867=@"loc,{#10000},76,5,77,5" -locations_default(#22867,#10000,76,5,77,5) -hasLocation(#22866,#22867) -enclosing_stmt(#22866,#22825) +typeexprs(#22866,0,#22847,2,"U") +hasLocation(#22866,#21542) +enclosing_stmt(#22866,#22826) expr_containers(#22866,#22805) -#22868=* -typeexprs(#22868,0,#22866,0,"T") -hasLocation(#22868,#21546) -enclosing_stmt(#22868,#22825) -expr_containers(#22868,#22805) -literals("T","T",#22868) -typebind(#22868,#22829) +literals("U","U",#22866) +typebind(#22866,#22851) +#22867=* +typeexprs(#22867,28,#22847,3,"T exten ... :\n T") +#22868=@"loc,{#10000},76,5,77,5" +locations_default(#22868,#10000,76,5,77,5) +hasLocation(#22867,#22868) +enclosing_stmt(#22867,#22826) +expr_containers(#22867,#22805) #22869=* -scopes(#22869,16) -scopenodes(#22866,#22869) -scopenesting(#22869,#22828) -#22870=@"local_type_name;{U};{#22869}" -local_type_names(#22870,"U",#22869) -#22871=* -typeexprs(#22871,14,#22866,1,"Promise") -#22872=@"loc,{#10000},76,15,76,30" -locations_default(#22872,#10000,76,15,76,30) -hasLocation(#22871,#22872) -enclosing_stmt(#22871,#22825) -expr_containers(#22871,#22805) -#22873=* -typeexprs(#22873,0,#22871,-1,"Promise") -hasLocation(#22873,#21550) -enclosing_stmt(#22873,#22825) -expr_containers(#22873,#22805) -literals("Promise","Promise",#22873) +typeexprs(#22869,0,#22867,0,"T") +hasLocation(#22869,#21546) +enclosing_stmt(#22869,#22826) +expr_containers(#22869,#22805) +literals("T","T",#22869) +typebind(#22869,#22830) +#22870=* +scopes(#22870,16) +scopenodes(#22867,#22870) +scopenesting(#22870,#22829) +#22871=@"local_type_name;{U};{#22870}" +local_type_names(#22871,"U",#22870) +#22872=* +typeexprs(#22872,14,#22867,1,"Promise") +#22873=@"loc,{#10000},76,15,76,30" +locations_default(#22873,#10000,76,15,76,30) +hasLocation(#22872,#22873) +enclosing_stmt(#22872,#22826) +expr_containers(#22872,#22805) #22874=* -typeexprs(#22874,29,#22871,0,"infer U") -#22875=@"loc,{#10000},76,23,76,29" -locations_default(#22875,#10000,76,23,76,29) -hasLocation(#22874,#22875) -enclosing_stmt(#22874,#22825) +typeexprs(#22874,0,#22872,-1,"Promise") +hasLocation(#22874,#21550) +enclosing_stmt(#22874,#22826) expr_containers(#22874,#22805) -#22876=* -typeexprs(#22876,22,#22874,0,"U") -hasLocation(#22876,#21556) -enclosing_stmt(#22876,#22825) -expr_containers(#22876,#22805) +literals("Promise","Promise",#22874) +#22875=* +typeexprs(#22875,29,#22872,0,"infer U") +#22876=@"loc,{#10000},76,23,76,29" +locations_default(#22876,#10000,76,23,76,29) +hasLocation(#22875,#22876) +enclosing_stmt(#22875,#22826) +expr_containers(#22875,#22805) #22877=* -typeexprs(#22877,1,#22876,0,"U") +typeexprs(#22877,22,#22875,0,"U") hasLocation(#22877,#21556) -enclosing_stmt(#22877,#22825) +enclosing_stmt(#22877,#22826) expr_containers(#22877,#22805) -literals("U","U",#22877) -typedecl(#22877,#22870) #22878=* -typeexprs(#22878,0,#22866,2,"U") -hasLocation(#22878,#21562) -enclosing_stmt(#22878,#22825) +typeexprs(#22878,1,#22877,0,"U") +hasLocation(#22878,#21556) +enclosing_stmt(#22878,#22826) expr_containers(#22878,#22805) literals("U","U",#22878) -typebind(#22878,#22870) +typedecl(#22878,#22871) #22879=* -typeexprs(#22879,0,#22866,3,"T") -hasLocation(#22879,#21566) -enclosing_stmt(#22879,#22825) +typeexprs(#22879,0,#22867,2,"U") +hasLocation(#22879,#21562) +enclosing_stmt(#22879,#22826) expr_containers(#22879,#22805) -literals("T","T",#22879) -typebind(#22879,#22829) +literals("U","U",#22879) +typebind(#22879,#22871) #22880=* -stmts(#22880,35,#22805,1,"type T0 ... tring>;") -#22881=@"loc,{#10000},79,3,79,29" -locations_default(#22881,#10000,79,3,79,29) -hasLocation(#22880,#22881) -stmt_containers(#22880,#22805) -#22882=* -typeexprs(#22882,1,#22880,0,"T0") -hasLocation(#22882,#21572) -enclosing_stmt(#22882,#22880) -expr_containers(#22882,#22805) -literals("T0","T0",#22882) -typedecl(#22882,#22810) +typeexprs(#22880,0,#22867,3,"T") +hasLocation(#22880,#21566) +enclosing_stmt(#22880,#22826) +expr_containers(#22880,#22805) +literals("T","T",#22880) +typebind(#22880,#22830) +#22881=* +stmts(#22881,35,#22805,1,"type T0 ... tring>;") +#22882=@"loc,{#10000},79,3,79,29" +locations_default(#22882,#10000,79,3,79,29) +hasLocation(#22881,#22882) +stmt_containers(#22881,#22805) #22883=* -typeexprs(#22883,14,#22880,1,"Unpacked") -#22884=@"loc,{#10000},79,13,79,28" -locations_default(#22884,#10000,79,13,79,28) -hasLocation(#22883,#22884) -enclosing_stmt(#22883,#22880) +typeexprs(#22883,1,#22881,0,"T0") +hasLocation(#22883,#21572) +enclosing_stmt(#22883,#22881) expr_containers(#22883,#22805) -#22885=* -typeexprs(#22885,0,#22883,-1,"Unpacked") -hasLocation(#22885,#21576) -enclosing_stmt(#22885,#22880) -expr_containers(#22885,#22805) -literals("Unpacked","Unpacked",#22885) -typebind(#22885,#22809) +literals("T0","T0",#22883) +typedecl(#22883,#22811) +#22884=* +typeexprs(#22884,14,#22881,1,"Unpacked") +#22885=@"loc,{#10000},79,13,79,28" +locations_default(#22885,#10000,79,13,79,28) +hasLocation(#22884,#22885) +enclosing_stmt(#22884,#22881) +expr_containers(#22884,#22805) #22886=* -typeexprs(#22886,2,#22883,0,"string") -hasLocation(#22886,#21580) -enclosing_stmt(#22886,#22880) +typeexprs(#22886,0,#22884,-1,"Unpacked") +hasLocation(#22886,#21576) +enclosing_stmt(#22886,#22881) expr_containers(#22886,#22805) -literals("string","string",#22886) +literals("Unpacked","Unpacked",#22886) +typebind(#22886,#22810) #22887=* -stmts(#22887,35,#22805,2,"type T1 ... ing[]>;") -#22888=@"loc,{#10000},80,3,80,31" -locations_default(#22888,#10000,80,3,80,31) -hasLocation(#22887,#22888) -stmt_containers(#22887,#22805) -#22889=* -typeexprs(#22889,1,#22887,0,"T1") -hasLocation(#22889,#21588) -enclosing_stmt(#22889,#22887) -expr_containers(#22889,#22805) -literals("T1","T1",#22889) -typedecl(#22889,#22811) +typeexprs(#22887,2,#22884,0,"string") +hasLocation(#22887,#21580) +enclosing_stmt(#22887,#22881) +expr_containers(#22887,#22805) +literals("string","string",#22887) +#22888=* +stmts(#22888,35,#22805,2,"type T1 ... ing[]>;") +#22889=@"loc,{#10000},80,3,80,31" +locations_default(#22889,#10000,80,3,80,31) +hasLocation(#22888,#22889) +stmt_containers(#22888,#22805) #22890=* -typeexprs(#22890,14,#22887,1,"Unpacked") -#22891=@"loc,{#10000},80,13,80,30" -locations_default(#22891,#10000,80,13,80,30) -hasLocation(#22890,#22891) -enclosing_stmt(#22890,#22887) +typeexprs(#22890,1,#22888,0,"T1") +hasLocation(#22890,#21588) +enclosing_stmt(#22890,#22888) expr_containers(#22890,#22805) -#22892=* -typeexprs(#22892,0,#22890,-1,"Unpacked") -hasLocation(#22892,#21592) -enclosing_stmt(#22892,#22887) -expr_containers(#22892,#22805) -literals("Unpacked","Unpacked",#22892) -typebind(#22892,#22809) +literals("T1","T1",#22890) +typedecl(#22890,#22812) +#22891=* +typeexprs(#22891,14,#22888,1,"Unpacked") +#22892=@"loc,{#10000},80,13,80,30" +locations_default(#22892,#10000,80,13,80,30) +hasLocation(#22891,#22892) +enclosing_stmt(#22891,#22888) +expr_containers(#22891,#22805) #22893=* -typeexprs(#22893,6,#22890,0,"string[]") -#22894=@"loc,{#10000},80,22,80,29" -locations_default(#22894,#10000,80,22,80,29) -hasLocation(#22893,#22894) -enclosing_stmt(#22893,#22887) +typeexprs(#22893,0,#22891,-1,"Unpacked") +hasLocation(#22893,#21592) +enclosing_stmt(#22893,#22888) expr_containers(#22893,#22805) -#22895=* -typeexprs(#22895,2,#22893,0,"string") -hasLocation(#22895,#21596) -enclosing_stmt(#22895,#22887) -expr_containers(#22895,#22805) -literals("string","string",#22895) +literals("Unpacked","Unpacked",#22893) +typebind(#22893,#22810) +#22894=* +typeexprs(#22894,6,#22891,0,"string[]") +#22895=@"loc,{#10000},80,22,80,29" +locations_default(#22895,#10000,80,22,80,29) +hasLocation(#22894,#22895) +enclosing_stmt(#22894,#22888) +expr_containers(#22894,#22805) #22896=* -stmts(#22896,35,#22805,3,"type T2 ... tring>;") -#22897=@"loc,{#10000},81,3,81,35" -locations_default(#22897,#10000,81,3,81,35) -hasLocation(#22896,#22897) -stmt_containers(#22896,#22805) -#22898=* -typeexprs(#22898,1,#22896,0,"T2") -hasLocation(#22898,#21608) -enclosing_stmt(#22898,#22896) -expr_containers(#22898,#22805) -literals("T2","T2",#22898) -typedecl(#22898,#22812) +typeexprs(#22896,2,#22894,0,"string") +hasLocation(#22896,#21596) +enclosing_stmt(#22896,#22888) +expr_containers(#22896,#22805) +literals("string","string",#22896) +#22897=* +stmts(#22897,35,#22805,3,"type T2 ... tring>;") +#22898=@"loc,{#10000},81,3,81,35" +locations_default(#22898,#10000,81,3,81,35) +hasLocation(#22897,#22898) +stmt_containers(#22897,#22805) #22899=* -typeexprs(#22899,14,#22896,1,"Unpacke ... string>") -#22900=@"loc,{#10000},81,13,81,34" -locations_default(#22900,#10000,81,13,81,34) -hasLocation(#22899,#22900) -enclosing_stmt(#22899,#22896) +typeexprs(#22899,1,#22897,0,"T2") +hasLocation(#22899,#21608) +enclosing_stmt(#22899,#22897) expr_containers(#22899,#22805) -#22901=* -typeexprs(#22901,0,#22899,-1,"Unpacked") -hasLocation(#22901,#21612) -enclosing_stmt(#22901,#22896) -expr_containers(#22901,#22805) -literals("Unpacked","Unpacked",#22901) -typebind(#22901,#22809) +literals("T2","T2",#22899) +typedecl(#22899,#22813) +#22900=* +typeexprs(#22900,14,#22897,1,"Unpacke ... string>") +#22901=@"loc,{#10000},81,13,81,34" +locations_default(#22901,#10000,81,13,81,34) +hasLocation(#22900,#22901) +enclosing_stmt(#22900,#22897) +expr_containers(#22900,#22805) #22902=* -typeexprs(#22902,23,#22899,0,"() => string") -#22903=@"loc,{#10000},81,22,81,33" -locations_default(#22903,#10000,81,22,81,33) -hasLocation(#22902,#22903) -enclosing_stmt(#22902,#22896) +typeexprs(#22902,0,#22900,-1,"Unpacked") +hasLocation(#22902,#21612) +enclosing_stmt(#22902,#22897) expr_containers(#22902,#22805) -#22904=* -exprs(#22904,9,#22902,0,"() => string") -hasLocation(#22904,#22903) -enclosing_stmt(#22904,#22896) -expr_containers(#22904,#22805) +literals("Unpacked","Unpacked",#22902) +typebind(#22902,#22810) +#22903=* +typeexprs(#22903,23,#22900,0,"() => string") +#22904=@"loc,{#10000},81,22,81,33" +locations_default(#22904,#10000,81,22,81,33) +hasLocation(#22903,#22904) +enclosing_stmt(#22903,#22897) +expr_containers(#22903,#22805) #22905=* -scopes(#22905,1) -scopenodes(#22904,#22905) -scopenesting(#22905,#22808) -#22906=@"var;{arguments};{#22905}" -variables(#22906,"arguments",#22905) -is_arguments_object(#22906) -#22907=* -typeexprs(#22907,2,#22904,-3,"string") -hasLocation(#22907,#21622) -expr_containers(#22907,#22904) -literals("string","string",#22907) +exprs(#22905,9,#22903,0,"() => string") +hasLocation(#22905,#22904) +enclosing_stmt(#22905,#22897) +expr_containers(#22905,#22805) +#22906=* +scopes(#22906,1) +scopenodes(#22905,#22906) +scopenesting(#22906,#22808) +#22907=@"var;{arguments};{#22906}" +variables(#22907,"arguments",#22906) +is_arguments_object(#22907) #22908=* -stmts(#22908,35,#22805,4,"type T3 ... ring>>;") -#22909=@"loc,{#10000},82,3,82,38" -locations_default(#22909,#10000,82,3,82,38) -hasLocation(#22908,#22909) -stmt_containers(#22908,#22805) -#22910=* -typeexprs(#22910,1,#22908,0,"T3") -hasLocation(#22910,#21630) -enclosing_stmt(#22910,#22908) -expr_containers(#22910,#22805) -literals("T3","T3",#22910) -typedecl(#22910,#22813) +typeexprs(#22908,2,#22905,-3,"string") +hasLocation(#22908,#21622) +expr_containers(#22908,#22905) +literals("string","string",#22908) +#22909=* +stmts(#22909,35,#22805,4,"type T3 ... ring>>;") +#22910=@"loc,{#10000},82,3,82,38" +locations_default(#22910,#10000,82,3,82,38) +hasLocation(#22909,#22910) +stmt_containers(#22909,#22805) #22911=* -typeexprs(#22911,14,#22908,1,"Unpacke ... tring>>") -#22912=@"loc,{#10000},82,13,82,37" -locations_default(#22912,#10000,82,13,82,37) -hasLocation(#22911,#22912) -enclosing_stmt(#22911,#22908) +typeexprs(#22911,1,#22909,0,"T3") +hasLocation(#22911,#21630) +enclosing_stmt(#22911,#22909) expr_containers(#22911,#22805) -#22913=* -typeexprs(#22913,0,#22911,-1,"Unpacked") -hasLocation(#22913,#21634) -enclosing_stmt(#22913,#22908) -expr_containers(#22913,#22805) -literals("Unpacked","Unpacked",#22913) -typebind(#22913,#22809) +literals("T3","T3",#22911) +typedecl(#22911,#22814) +#22912=* +typeexprs(#22912,14,#22909,1,"Unpacke ... tring>>") +#22913=@"loc,{#10000},82,13,82,37" +locations_default(#22913,#10000,82,13,82,37) +hasLocation(#22912,#22913) +enclosing_stmt(#22912,#22909) +expr_containers(#22912,#22805) #22914=* -typeexprs(#22914,14,#22911,0,"Promise") -#22915=@"loc,{#10000},82,22,82,36" -locations_default(#22915,#10000,82,22,82,36) -hasLocation(#22914,#22915) -enclosing_stmt(#22914,#22908) +typeexprs(#22914,0,#22912,-1,"Unpacked") +hasLocation(#22914,#21634) +enclosing_stmt(#22914,#22909) expr_containers(#22914,#22805) -#22916=* -typeexprs(#22916,0,#22914,-1,"Promise") -hasLocation(#22916,#21638) -enclosing_stmt(#22916,#22908) -expr_containers(#22916,#22805) -literals("Promise","Promise",#22916) +literals("Unpacked","Unpacked",#22914) +typebind(#22914,#22810) +#22915=* +typeexprs(#22915,14,#22912,0,"Promise") +#22916=@"loc,{#10000},82,22,82,36" +locations_default(#22916,#10000,82,22,82,36) +hasLocation(#22915,#22916) +enclosing_stmt(#22915,#22909) +expr_containers(#22915,#22805) #22917=* -typeexprs(#22917,2,#22914,0,"string") -hasLocation(#22917,#21642) -enclosing_stmt(#22917,#22908) +typeexprs(#22917,0,#22915,-1,"Promise") +hasLocation(#22917,#21638) +enclosing_stmt(#22917,#22909) expr_containers(#22917,#22805) -literals("string","string",#22917) +literals("Promise","Promise",#22917) #22918=* -stmts(#22918,35,#22805,5,"type T4 ... ng>[]>;") -#22919=@"loc,{#10000},83,3,83,40" -locations_default(#22919,#10000,83,3,83,40) -hasLocation(#22918,#22919) -stmt_containers(#22918,#22805) -#22920=* -typeexprs(#22920,1,#22918,0,"T4") -hasLocation(#22920,#21652) -enclosing_stmt(#22920,#22918) -expr_containers(#22920,#22805) -literals("T4","T4",#22920) -typedecl(#22920,#22814) +typeexprs(#22918,2,#22915,0,"string") +hasLocation(#22918,#21642) +enclosing_stmt(#22918,#22909) +expr_containers(#22918,#22805) +literals("string","string",#22918) +#22919=* +stmts(#22919,35,#22805,5,"type T4 ... ng>[]>;") +#22920=@"loc,{#10000},83,3,83,40" +locations_default(#22920,#10000,83,3,83,40) +hasLocation(#22919,#22920) +stmt_containers(#22919,#22805) #22921=* -typeexprs(#22921,14,#22918,1,"Unpacke ... ing>[]>") -#22922=@"loc,{#10000},83,13,83,39" -locations_default(#22922,#10000,83,13,83,39) -hasLocation(#22921,#22922) -enclosing_stmt(#22921,#22918) +typeexprs(#22921,1,#22919,0,"T4") +hasLocation(#22921,#21652) +enclosing_stmt(#22921,#22919) expr_containers(#22921,#22805) -#22923=* -typeexprs(#22923,0,#22921,-1,"Unpacked") -hasLocation(#22923,#21656) -enclosing_stmt(#22923,#22918) -expr_containers(#22923,#22805) -literals("Unpacked","Unpacked",#22923) -typebind(#22923,#22809) +literals("T4","T4",#22921) +typedecl(#22921,#22815) +#22922=* +typeexprs(#22922,14,#22919,1,"Unpacke ... ing>[]>") +#22923=@"loc,{#10000},83,13,83,39" +locations_default(#22923,#10000,83,13,83,39) +hasLocation(#22922,#22923) +enclosing_stmt(#22922,#22919) +expr_containers(#22922,#22805) #22924=* -typeexprs(#22924,6,#22921,0,"Promise[]") -#22925=@"loc,{#10000},83,22,83,38" -locations_default(#22925,#10000,83,22,83,38) -hasLocation(#22924,#22925) -enclosing_stmt(#22924,#22918) +typeexprs(#22924,0,#22922,-1,"Unpacked") +hasLocation(#22924,#21656) +enclosing_stmt(#22924,#22919) expr_containers(#22924,#22805) -#22926=* -typeexprs(#22926,14,#22924,0,"Promise") -#22927=@"loc,{#10000},83,22,83,36" -locations_default(#22927,#10000,83,22,83,36) -hasLocation(#22926,#22927) -enclosing_stmt(#22926,#22918) -expr_containers(#22926,#22805) -#22928=* -typeexprs(#22928,0,#22926,-1,"Promise") -hasLocation(#22928,#21660) -enclosing_stmt(#22928,#22918) -expr_containers(#22928,#22805) -literals("Promise","Promise",#22928) +literals("Unpacked","Unpacked",#22924) +typebind(#22924,#22810) +#22925=* +typeexprs(#22925,6,#22922,0,"Promise[]") +#22926=@"loc,{#10000},83,22,83,38" +locations_default(#22926,#10000,83,22,83,38) +hasLocation(#22925,#22926) +enclosing_stmt(#22925,#22919) +expr_containers(#22925,#22805) +#22927=* +typeexprs(#22927,14,#22925,0,"Promise") +#22928=@"loc,{#10000},83,22,83,36" +locations_default(#22928,#10000,83,22,83,36) +hasLocation(#22927,#22928) +enclosing_stmt(#22927,#22919) +expr_containers(#22927,#22805) #22929=* -typeexprs(#22929,2,#22926,0,"string") -hasLocation(#22929,#21664) -enclosing_stmt(#22929,#22918) +typeexprs(#22929,0,#22927,-1,"Promise") +hasLocation(#22929,#21660) +enclosing_stmt(#22929,#22919) expr_containers(#22929,#22805) -literals("string","string",#22929) +literals("Promise","Promise",#22929) #22930=* -stmts(#22930,35,#22805,6,"type T5 ... g>[]>>;") -#22931=@"loc,{#10000},84,3,84,50" -locations_default(#22931,#10000,84,3,84,50) -hasLocation(#22930,#22931) -stmt_containers(#22930,#22805) -#22932=* -typeexprs(#22932,1,#22930,0,"T5") -hasLocation(#22932,#21678) -enclosing_stmt(#22932,#22930) -expr_containers(#22932,#22805) -literals("T5","T5",#22932) -typedecl(#22932,#22815) +typeexprs(#22930,2,#22927,0,"string") +hasLocation(#22930,#21664) +enclosing_stmt(#22930,#22919) +expr_containers(#22930,#22805) +literals("string","string",#22930) +#22931=* +stmts(#22931,35,#22805,6,"type T5 ... g>[]>>;") +#22932=@"loc,{#10000},84,3,84,50" +locations_default(#22932,#10000,84,3,84,50) +hasLocation(#22931,#22932) +stmt_containers(#22931,#22805) #22933=* -typeexprs(#22933,14,#22930,1,"Unpacke ... ng>[]>>") -#22934=@"loc,{#10000},84,13,84,49" -locations_default(#22934,#10000,84,13,84,49) -hasLocation(#22933,#22934) -enclosing_stmt(#22933,#22930) +typeexprs(#22933,1,#22931,0,"T5") +hasLocation(#22933,#21678) +enclosing_stmt(#22933,#22931) expr_containers(#22933,#22805) -#22935=* -typeexprs(#22935,0,#22933,-1,"Unpacked") -hasLocation(#22935,#21682) -enclosing_stmt(#22935,#22930) -expr_containers(#22935,#22805) -literals("Unpacked","Unpacked",#22935) -typebind(#22935,#22809) +literals("T5","T5",#22933) +typedecl(#22933,#22816) +#22934=* +typeexprs(#22934,14,#22931,1,"Unpacke ... ng>[]>>") +#22935=@"loc,{#10000},84,13,84,49" +locations_default(#22935,#10000,84,13,84,49) +hasLocation(#22934,#22935) +enclosing_stmt(#22934,#22931) +expr_containers(#22934,#22805) #22936=* -typeexprs(#22936,14,#22933,0,"Unpacke ... ing>[]>") -#22937=@"loc,{#10000},84,22,84,48" -locations_default(#22937,#10000,84,22,84,48) -hasLocation(#22936,#22937) -enclosing_stmt(#22936,#22930) +typeexprs(#22936,0,#22934,-1,"Unpacked") +hasLocation(#22936,#21682) +enclosing_stmt(#22936,#22931) expr_containers(#22936,#22805) -#22938=* -typeexprs(#22938,0,#22936,-1,"Unpacked") -hasLocation(#22938,#21686) -enclosing_stmt(#22938,#22930) -expr_containers(#22938,#22805) -literals("Unpacked","Unpacked",#22938) -typebind(#22938,#22809) +literals("Unpacked","Unpacked",#22936) +typebind(#22936,#22810) +#22937=* +typeexprs(#22937,14,#22934,0,"Unpacke ... ing>[]>") +#22938=@"loc,{#10000},84,22,84,48" +locations_default(#22938,#10000,84,22,84,48) +hasLocation(#22937,#22938) +enclosing_stmt(#22937,#22931) +expr_containers(#22937,#22805) #22939=* -typeexprs(#22939,6,#22936,0,"Promise[]") -#22940=@"loc,{#10000},84,31,84,47" -locations_default(#22940,#10000,84,31,84,47) -hasLocation(#22939,#22940) -enclosing_stmt(#22939,#22930) +typeexprs(#22939,0,#22937,-1,"Unpacked") +hasLocation(#22939,#21686) +enclosing_stmt(#22939,#22931) expr_containers(#22939,#22805) -#22941=* -typeexprs(#22941,14,#22939,0,"Promise") -#22942=@"loc,{#10000},84,31,84,45" -locations_default(#22942,#10000,84,31,84,45) -hasLocation(#22941,#22942) -enclosing_stmt(#22941,#22930) -expr_containers(#22941,#22805) -#22943=* -typeexprs(#22943,0,#22941,-1,"Promise") -hasLocation(#22943,#21690) -enclosing_stmt(#22943,#22930) -expr_containers(#22943,#22805) -literals("Promise","Promise",#22943) +literals("Unpacked","Unpacked",#22939) +typebind(#22939,#22810) +#22940=* +typeexprs(#22940,6,#22937,0,"Promise[]") +#22941=@"loc,{#10000},84,31,84,47" +locations_default(#22941,#10000,84,31,84,47) +hasLocation(#22940,#22941) +enclosing_stmt(#22940,#22931) +expr_containers(#22940,#22805) +#22942=* +typeexprs(#22942,14,#22940,0,"Promise") +#22943=@"loc,{#10000},84,31,84,45" +locations_default(#22943,#10000,84,31,84,45) +hasLocation(#22942,#22943) +enclosing_stmt(#22942,#22931) +expr_containers(#22942,#22805) #22944=* -typeexprs(#22944,2,#22941,0,"string") -hasLocation(#22944,#21694) -enclosing_stmt(#22944,#22930) +typeexprs(#22944,0,#22942,-1,"Promise") +hasLocation(#22944,#21690) +enclosing_stmt(#22944,#22931) expr_containers(#22944,#22805) -literals("string","string",#22944) +literals("Promise","Promise",#22944) #22945=* -stmts(#22945,35,#22805,7,"type Fo ... never;") -#22946=@"loc,{#10000},86,3,86,65" -locations_default(#22946,#10000,86,3,86,65) -hasLocation(#22945,#22946) -stmt_containers(#22945,#22805) -#22947=* -typeexprs(#22947,1,#22945,0,"Foo") -hasLocation(#22947,#21710) -enclosing_stmt(#22947,#22945) -expr_containers(#22947,#22805) -literals("Foo","Foo",#22947) -typedecl(#22947,#22816) +typeexprs(#22945,2,#22942,0,"string") +hasLocation(#22945,#21694) +enclosing_stmt(#22945,#22931) +expr_containers(#22945,#22805) +literals("string","string",#22945) +#22946=* +stmts(#22946,35,#22805,7,"type Fo ... never;") +#22947=@"loc,{#10000},86,3,86,65" +locations_default(#22947,#10000,86,3,86,65) +hasLocation(#22946,#22947) +stmt_containers(#22946,#22805) #22948=* -scopes(#22948,12) -scopenodes(#22945,#22948) -scopenesting(#22948,#22808) -#22949=@"local_type_name;{T};{#22948}" -local_type_names(#22949,"T",#22948) -#22950=* -typeexprs(#22950,22,#22945,2,"T") -hasLocation(#22950,#21714) -enclosing_stmt(#22950,#22945) -expr_containers(#22950,#22805) +typeexprs(#22948,1,#22946,0,"Foo") +hasLocation(#22948,#21710) +enclosing_stmt(#22948,#22946) +expr_containers(#22948,#22805) +literals("Foo","Foo",#22948) +typedecl(#22948,#22817) +#22949=* +scopes(#22949,12) +scopenodes(#22946,#22949) +scopenesting(#22949,#22808) +#22950=@"local_type_name;{T};{#22949}" +local_type_names(#22950,"T",#22949) #22951=* -typeexprs(#22951,1,#22950,0,"T") +typeexprs(#22951,22,#22946,2,"T") hasLocation(#22951,#21714) -enclosing_stmt(#22951,#22945) +enclosing_stmt(#22951,#22946) expr_containers(#22951,#22805) -literals("T","T",#22951) -typedecl(#22951,#22949) #22952=* -typeexprs(#22952,28,#22945,1,"T exten ... : never") -#22953=@"loc,{#10000},86,17,86,64" -locations_default(#22953,#10000,86,17,86,64) -hasLocation(#22952,#22953) -enclosing_stmt(#22952,#22945) +typeexprs(#22952,1,#22951,0,"T") +hasLocation(#22952,#21714) +enclosing_stmt(#22952,#22946) expr_containers(#22952,#22805) -#22954=* -typeexprs(#22954,0,#22952,0,"T") -hasLocation(#22954,#21720) -enclosing_stmt(#22954,#22945) -expr_containers(#22954,#22805) -literals("T","T",#22954) -typebind(#22954,#22949) +literals("T","T",#22952) +typedecl(#22952,#22950) +#22953=* +typeexprs(#22953,28,#22946,1,"T exten ... : never") +#22954=@"loc,{#10000},86,17,86,64" +locations_default(#22954,#10000,86,17,86,64) +hasLocation(#22953,#22954) +enclosing_stmt(#22953,#22946) +expr_containers(#22953,#22805) #22955=* -typeexprs(#22955,21,#22952,1,"{ a: in ... fer U }") -#22956=@"loc,{#10000},86,27,86,52" -locations_default(#22956,#10000,86,27,86,52) -hasLocation(#22955,#22956) -enclosing_stmt(#22955,#22945) +typeexprs(#22955,0,#22953,0,"T") +hasLocation(#22955,#21720) +enclosing_stmt(#22955,#22946) expr_containers(#22955,#22805) -#22957=* -properties(#22957,#22955,0,8,"a: infer U,") -#22958=@"loc,{#10000},86,29,86,39" -locations_default(#22958,#10000,86,29,86,39) -hasLocation(#22957,#22958) -#22959=* -exprs(#22959,0,#22957,0,"a") -hasLocation(#22959,#21726) -enclosing_stmt(#22959,#22945) -expr_containers(#22959,#22805) -literals("a","a",#22959) -is_abstract_member(#22957) +literals("T","T",#22955) +typebind(#22955,#22950) +#22956=* +typeexprs(#22956,21,#22953,1,"{ a: in ... fer U }") +#22957=@"loc,{#10000},86,27,86,52" +locations_default(#22957,#10000,86,27,86,52) +hasLocation(#22956,#22957) +enclosing_stmt(#22956,#22946) +expr_containers(#22956,#22805) +#22958=* +properties(#22958,#22956,0,8,"a: infer U,") +#22959=@"loc,{#10000},86,29,86,39" +locations_default(#22959,#10000,86,29,86,39) +hasLocation(#22958,#22959) #22960=* -typeexprs(#22960,29,#22957,2,"infer U") -#22961=@"loc,{#10000},86,32,86,38" -locations_default(#22961,#10000,86,32,86,38) -hasLocation(#22960,#22961) -enclosing_stmt(#22960,#22945) +exprs(#22960,0,#22958,0,"a") +hasLocation(#22960,#21726) +enclosing_stmt(#22960,#22946) expr_containers(#22960,#22805) -#22962=* -typeexprs(#22962,22,#22960,0,"U") -hasLocation(#22962,#21732) -enclosing_stmt(#22962,#22945) -expr_containers(#22962,#22805) +literals("a","a",#22960) +is_abstract_member(#22958) +#22961=* +typeexprs(#22961,29,#22958,2,"infer U") +#22962=@"loc,{#10000},86,32,86,38" +locations_default(#22962,#10000,86,32,86,38) +hasLocation(#22961,#22962) +enclosing_stmt(#22961,#22946) +expr_containers(#22961,#22805) #22963=* -typeexprs(#22963,1,#22962,0,"U") +typeexprs(#22963,22,#22961,0,"U") hasLocation(#22963,#21732) -enclosing_stmt(#22963,#22945) +enclosing_stmt(#22963,#22946) expr_containers(#22963,#22805) -literals("U","U",#22963) #22964=* -properties(#22964,#22955,1,8,"b: infer U") -#22965=@"loc,{#10000},86,41,86,50" -locations_default(#22965,#10000,86,41,86,50) -hasLocation(#22964,#22965) -#22966=* -exprs(#22966,0,#22964,0,"b") -hasLocation(#22966,#21736) -enclosing_stmt(#22966,#22945) -expr_containers(#22966,#22805) -literals("b","b",#22966) -is_abstract_member(#22964) +typeexprs(#22964,1,#22963,0,"U") +hasLocation(#22964,#21732) +enclosing_stmt(#22964,#22946) +expr_containers(#22964,#22805) +literals("U","U",#22964) +#22965=* +properties(#22965,#22956,1,8,"b: infer U") +#22966=@"loc,{#10000},86,41,86,50" +locations_default(#22966,#10000,86,41,86,50) +hasLocation(#22965,#22966) #22967=* -typeexprs(#22967,29,#22964,2,"infer U") -#22968=@"loc,{#10000},86,44,86,50" -locations_default(#22968,#10000,86,44,86,50) -hasLocation(#22967,#22968) -enclosing_stmt(#22967,#22945) +exprs(#22967,0,#22965,0,"b") +hasLocation(#22967,#21736) +enclosing_stmt(#22967,#22946) expr_containers(#22967,#22805) -#22969=* -typeexprs(#22969,22,#22967,0,"U") -hasLocation(#22969,#21742) -enclosing_stmt(#22969,#22945) -expr_containers(#22969,#22805) +literals("b","b",#22967) +is_abstract_member(#22965) +#22968=* +typeexprs(#22968,29,#22965,2,"infer U") +#22969=@"loc,{#10000},86,44,86,50" +locations_default(#22969,#10000,86,44,86,50) +hasLocation(#22968,#22969) +enclosing_stmt(#22968,#22946) +expr_containers(#22968,#22805) #22970=* -typeexprs(#22970,1,#22969,0,"U") +typeexprs(#22970,22,#22968,0,"U") hasLocation(#22970,#21742) -enclosing_stmt(#22970,#22945) +enclosing_stmt(#22970,#22946) expr_containers(#22970,#22805) -literals("U","U",#22970) #22971=* -typeexprs(#22971,0,#22952,2,"U") -hasLocation(#22971,#21748) -enclosing_stmt(#22971,#22945) +typeexprs(#22971,1,#22970,0,"U") +hasLocation(#22971,#21742) +enclosing_stmt(#22971,#22946) expr_containers(#22971,#22805) literals("U","U",#22971) #22972=* -typeexprs(#22972,2,#22952,3,"never") -hasLocation(#22972,#21752) -enclosing_stmt(#22972,#22945) +typeexprs(#22972,0,#22953,2,"U") +hasLocation(#22972,#21748) +enclosing_stmt(#22972,#22946) expr_containers(#22972,#22805) -literals("never","never",#22972) +literals("U","U",#22972) #22973=* -stmts(#22973,35,#22805,8,"type T1 ... ing }>;") -#22974=@"loc,{#10000},87,3,87,43" -locations_default(#22974,#10000,87,3,87,43) -hasLocation(#22973,#22974) -stmt_containers(#22973,#22805) -#22975=* -typeexprs(#22975,1,#22973,0,"T10") -hasLocation(#22975,#21758) -enclosing_stmt(#22975,#22973) -expr_containers(#22975,#22805) -literals("T10","T10",#22975) -typedecl(#22975,#22817) +typeexprs(#22973,2,#22953,3,"never") +hasLocation(#22973,#21752) +enclosing_stmt(#22973,#22946) +expr_containers(#22973,#22805) +literals("never","never",#22973) +#22974=* +stmts(#22974,35,#22805,8,"type T1 ... ing }>;") +#22975=@"loc,{#10000},87,3,87,43" +locations_default(#22975,#10000,87,3,87,43) +hasLocation(#22974,#22975) +stmt_containers(#22974,#22805) #22976=* -typeexprs(#22976,14,#22973,1,"Foo<{ a ... ring }>") -#22977=@"loc,{#10000},87,14,87,42" -locations_default(#22977,#10000,87,14,87,42) -hasLocation(#22976,#22977) -enclosing_stmt(#22976,#22973) +typeexprs(#22976,1,#22974,0,"T10") +hasLocation(#22976,#21758) +enclosing_stmt(#22976,#22974) expr_containers(#22976,#22805) -#22978=* -typeexprs(#22978,0,#22976,-1,"Foo") -hasLocation(#22978,#21762) -enclosing_stmt(#22978,#22973) -expr_containers(#22978,#22805) -literals("Foo","Foo",#22978) -typebind(#22978,#22816) +literals("T10","T10",#22976) +typedecl(#22976,#22818) +#22977=* +typeexprs(#22977,14,#22974,1,"Foo<{ a ... ring }>") +#22978=@"loc,{#10000},87,14,87,42" +locations_default(#22978,#10000,87,14,87,42) +hasLocation(#22977,#22978) +enclosing_stmt(#22977,#22974) +expr_containers(#22977,#22805) #22979=* -typeexprs(#22979,21,#22976,0,"{ a: st ... tring }") -#22980=@"loc,{#10000},87,18,87,41" -locations_default(#22980,#10000,87,18,87,41) -hasLocation(#22979,#22980) -enclosing_stmt(#22979,#22973) +typeexprs(#22979,0,#22977,-1,"Foo") +hasLocation(#22979,#21762) +enclosing_stmt(#22979,#22974) expr_containers(#22979,#22805) -#22981=* -properties(#22981,#22979,0,8,"a: string,") -#22982=@"loc,{#10000},87,20,87,29" -locations_default(#22982,#10000,87,20,87,29) -hasLocation(#22981,#22982) -#22983=* -exprs(#22983,0,#22981,0,"a") -hasLocation(#22983,#21768) -enclosing_stmt(#22983,#22973) -expr_containers(#22983,#22805) -literals("a","a",#22983) -is_abstract_member(#22981) +literals("Foo","Foo",#22979) +typebind(#22979,#22817) +#22980=* +typeexprs(#22980,21,#22977,0,"{ a: st ... tring }") +#22981=@"loc,{#10000},87,18,87,41" +locations_default(#22981,#10000,87,18,87,41) +hasLocation(#22980,#22981) +enclosing_stmt(#22980,#22974) +expr_containers(#22980,#22805) +#22982=* +properties(#22982,#22980,0,8,"a: string,") +#22983=@"loc,{#10000},87,20,87,29" +locations_default(#22983,#10000,87,20,87,29) +hasLocation(#22982,#22983) #22984=* -typeexprs(#22984,2,#22981,2,"string") -hasLocation(#22984,#21772) -enclosing_stmt(#22984,#22973) +exprs(#22984,0,#22982,0,"a") +hasLocation(#22984,#21768) +enclosing_stmt(#22984,#22974) expr_containers(#22984,#22805) -literals("string","string",#22984) +literals("a","a",#22984) +is_abstract_member(#22982) #22985=* -properties(#22985,#22979,1,8,"b: string") -#22986=@"loc,{#10000},87,31,87,39" -locations_default(#22986,#10000,87,31,87,39) -hasLocation(#22985,#22986) -#22987=* -exprs(#22987,0,#22985,0,"b") -hasLocation(#22987,#21776) -enclosing_stmt(#22987,#22973) -expr_containers(#22987,#22805) -literals("b","b",#22987) -is_abstract_member(#22985) +typeexprs(#22985,2,#22982,2,"string") +hasLocation(#22985,#21772) +enclosing_stmt(#22985,#22974) +expr_containers(#22985,#22805) +literals("string","string",#22985) +#22986=* +properties(#22986,#22980,1,8,"b: string") +#22987=@"loc,{#10000},87,31,87,39" +locations_default(#22987,#10000,87,31,87,39) +hasLocation(#22986,#22987) #22988=* -typeexprs(#22988,2,#22985,2,"string") -hasLocation(#22988,#21780) -enclosing_stmt(#22988,#22973) +exprs(#22988,0,#22986,0,"b") +hasLocation(#22988,#21776) +enclosing_stmt(#22988,#22974) expr_containers(#22988,#22805) -literals("string","string",#22988) +literals("b","b",#22988) +is_abstract_member(#22986) #22989=* -stmts(#22989,35,#22805,9,"type T1 ... ber }>;") -#22990=@"loc,{#10000},88,3,88,43" -locations_default(#22990,#10000,88,3,88,43) -hasLocation(#22989,#22990) -stmt_containers(#22989,#22805) -#22991=* -typeexprs(#22991,1,#22989,0,"T11") -hasLocation(#22991,#21790) -enclosing_stmt(#22991,#22989) -expr_containers(#22991,#22805) -literals("T11","T11",#22991) -typedecl(#22991,#22818) +typeexprs(#22989,2,#22986,2,"string") +hasLocation(#22989,#21780) +enclosing_stmt(#22989,#22974) +expr_containers(#22989,#22805) +literals("string","string",#22989) +#22990=* +stmts(#22990,35,#22805,9,"type T1 ... ber }>;") +#22991=@"loc,{#10000},88,3,88,43" +locations_default(#22991,#10000,88,3,88,43) +hasLocation(#22990,#22991) +stmt_containers(#22990,#22805) #22992=* -typeexprs(#22992,14,#22989,1,"Foo<{ a ... mber }>") -#22993=@"loc,{#10000},88,14,88,42" -locations_default(#22993,#10000,88,14,88,42) -hasLocation(#22992,#22993) -enclosing_stmt(#22992,#22989) +typeexprs(#22992,1,#22990,0,"T11") +hasLocation(#22992,#21790) +enclosing_stmt(#22992,#22990) expr_containers(#22992,#22805) -#22994=* -typeexprs(#22994,0,#22992,-1,"Foo") -hasLocation(#22994,#21794) -enclosing_stmt(#22994,#22989) -expr_containers(#22994,#22805) -literals("Foo","Foo",#22994) -typebind(#22994,#22816) +literals("T11","T11",#22992) +typedecl(#22992,#22819) +#22993=* +typeexprs(#22993,14,#22990,1,"Foo<{ a ... mber }>") +#22994=@"loc,{#10000},88,14,88,42" +locations_default(#22994,#10000,88,14,88,42) +hasLocation(#22993,#22994) +enclosing_stmt(#22993,#22990) +expr_containers(#22993,#22805) #22995=* -typeexprs(#22995,21,#22992,0,"{ a: st ... umber }") -#22996=@"loc,{#10000},88,18,88,41" -locations_default(#22996,#10000,88,18,88,41) -hasLocation(#22995,#22996) -enclosing_stmt(#22995,#22989) +typeexprs(#22995,0,#22993,-1,"Foo") +hasLocation(#22995,#21794) +enclosing_stmt(#22995,#22990) expr_containers(#22995,#22805) -#22997=* -properties(#22997,#22995,0,8,"a: string,") -#22998=@"loc,{#10000},88,20,88,29" -locations_default(#22998,#10000,88,20,88,29) -hasLocation(#22997,#22998) -#22999=* -exprs(#22999,0,#22997,0,"a") -hasLocation(#22999,#21800) -enclosing_stmt(#22999,#22989) -expr_containers(#22999,#22805) -literals("a","a",#22999) -is_abstract_member(#22997) +literals("Foo","Foo",#22995) +typebind(#22995,#22817) +#22996=* +typeexprs(#22996,21,#22993,0,"{ a: st ... umber }") +#22997=@"loc,{#10000},88,18,88,41" +locations_default(#22997,#10000,88,18,88,41) +hasLocation(#22996,#22997) +enclosing_stmt(#22996,#22990) +expr_containers(#22996,#22805) +#22998=* +properties(#22998,#22996,0,8,"a: string,") +#22999=@"loc,{#10000},88,20,88,29" +locations_default(#22999,#10000,88,20,88,29) +hasLocation(#22998,#22999) #23000=* -typeexprs(#23000,2,#22997,2,"string") -hasLocation(#23000,#21804) -enclosing_stmt(#23000,#22989) +exprs(#23000,0,#22998,0,"a") +hasLocation(#23000,#21800) +enclosing_stmt(#23000,#22990) expr_containers(#23000,#22805) -literals("string","string",#23000) +literals("a","a",#23000) +is_abstract_member(#22998) #23001=* -properties(#23001,#22995,1,8,"b: number") -#23002=@"loc,{#10000},88,31,88,39" -locations_default(#23002,#10000,88,31,88,39) -hasLocation(#23001,#23002) -#23003=* -exprs(#23003,0,#23001,0,"b") -hasLocation(#23003,#21808) -enclosing_stmt(#23003,#22989) -expr_containers(#23003,#22805) -literals("b","b",#23003) -is_abstract_member(#23001) +typeexprs(#23001,2,#22998,2,"string") +hasLocation(#23001,#21804) +enclosing_stmt(#23001,#22990) +expr_containers(#23001,#22805) +literals("string","string",#23001) +#23002=* +properties(#23002,#22996,1,8,"b: number") +#23003=@"loc,{#10000},88,31,88,39" +locations_default(#23003,#10000,88,31,88,39) +hasLocation(#23002,#23003) #23004=* -typeexprs(#23004,2,#23001,2,"number") -hasLocation(#23004,#21812) -enclosing_stmt(#23004,#22989) +exprs(#23004,0,#23002,0,"b") +hasLocation(#23004,#21808) +enclosing_stmt(#23004,#22990) expr_containers(#23004,#22805) -literals("number","number",#23004) +literals("b","b",#23004) +is_abstract_member(#23002) #23005=* -stmts(#23005,35,#22805,10,"type Ba ... never;") -#23006=@"loc,{#10000},90,3,90,91" -locations_default(#23006,#10000,90,3,90,91) -hasLocation(#23005,#23006) -stmt_containers(#23005,#22805) -#23007=* -typeexprs(#23007,1,#23005,0,"Bar") -hasLocation(#23007,#21822) -enclosing_stmt(#23007,#23005) -expr_containers(#23007,#22805) -literals("Bar","Bar",#23007) -typedecl(#23007,#22819) +typeexprs(#23005,2,#23002,2,"number") +hasLocation(#23005,#21812) +enclosing_stmt(#23005,#22990) +expr_containers(#23005,#22805) +literals("number","number",#23005) +#23006=* +stmts(#23006,35,#22805,10,"type Ba ... never;") +#23007=@"loc,{#10000},90,3,90,91" +locations_default(#23007,#10000,90,3,90,91) +hasLocation(#23006,#23007) +stmt_containers(#23006,#22805) #23008=* -scopes(#23008,12) -scopenodes(#23005,#23008) -scopenesting(#23008,#22808) -#23009=@"local_type_name;{T};{#23008}" -local_type_names(#23009,"T",#23008) -#23010=* -typeexprs(#23010,22,#23005,2,"T") -hasLocation(#23010,#21826) -enclosing_stmt(#23010,#23005) -expr_containers(#23010,#22805) +typeexprs(#23008,1,#23006,0,"Bar") +hasLocation(#23008,#21822) +enclosing_stmt(#23008,#23006) +expr_containers(#23008,#22805) +literals("Bar","Bar",#23008) +typedecl(#23008,#22820) +#23009=* +scopes(#23009,12) +scopenodes(#23006,#23009) +scopenesting(#23009,#22808) +#23010=@"local_type_name;{T};{#23009}" +local_type_names(#23010,"T",#23009) #23011=* -typeexprs(#23011,1,#23010,0,"T") +typeexprs(#23011,22,#23006,2,"T") hasLocation(#23011,#21826) -enclosing_stmt(#23011,#23005) +enclosing_stmt(#23011,#23006) expr_containers(#23011,#22805) -literals("T","T",#23011) -typedecl(#23011,#23009) #23012=* -typeexprs(#23012,28,#23005,1,"T exten ... : never") -#23013=@"loc,{#10000},90,17,90,90" -locations_default(#23013,#10000,90,17,90,90) -hasLocation(#23012,#23013) -enclosing_stmt(#23012,#23005) +typeexprs(#23012,1,#23011,0,"T") +hasLocation(#23012,#21826) +enclosing_stmt(#23012,#23006) expr_containers(#23012,#22805) -#23014=* -typeexprs(#23014,0,#23012,0,"T") -hasLocation(#23014,#21832) -enclosing_stmt(#23014,#23005) -expr_containers(#23014,#22805) -literals("T","T",#23014) -typebind(#23014,#23009) +literals("T","T",#23012) +typedecl(#23012,#23010) +#23013=* +typeexprs(#23013,28,#23006,1,"T exten ... : never") +#23014=@"loc,{#10000},90,17,90,90" +locations_default(#23014,#10000,90,17,90,90) +hasLocation(#23013,#23014) +enclosing_stmt(#23013,#23006) +expr_containers(#23013,#22805) #23015=* -typeexprs(#23015,21,#23012,1,"{ a: (x ... void }") -#23016=@"loc,{#10000},90,27,90,78" -locations_default(#23016,#10000,90,27,90,78) -hasLocation(#23015,#23016) -enclosing_stmt(#23015,#23005) +typeexprs(#23015,0,#23013,0,"T") +hasLocation(#23015,#21832) +enclosing_stmt(#23015,#23006) expr_containers(#23015,#22805) -#23017=* -properties(#23017,#23015,0,8,"a: (x: ... > void,") -#23018=@"loc,{#10000},90,29,90,52" -locations_default(#23018,#10000,90,29,90,52) -hasLocation(#23017,#23018) -#23019=* -exprs(#23019,0,#23017,0,"a") -hasLocation(#23019,#21838) -enclosing_stmt(#23019,#23005) -expr_containers(#23019,#22805) -literals("a","a",#23019) -is_abstract_member(#23017) +literals("T","T",#23015) +typebind(#23015,#23010) +#23016=* +typeexprs(#23016,21,#23013,1,"{ a: (x ... void }") +#23017=@"loc,{#10000},90,27,90,78" +locations_default(#23017,#10000,90,27,90,78) +hasLocation(#23016,#23017) +enclosing_stmt(#23016,#23006) +expr_containers(#23016,#22805) +#23018=* +properties(#23018,#23016,0,8,"a: (x: ... > void,") +#23019=@"loc,{#10000},90,29,90,52" +locations_default(#23019,#10000,90,29,90,52) +hasLocation(#23018,#23019) #23020=* -typeexprs(#23020,23,#23017,2,"(x: infer U) => void") -#23021=@"loc,{#10000},90,32,90,51" -locations_default(#23021,#10000,90,32,90,51) -hasLocation(#23020,#23021) -enclosing_stmt(#23020,#23005) +exprs(#23020,0,#23018,0,"a") +hasLocation(#23020,#21838) +enclosing_stmt(#23020,#23006) expr_containers(#23020,#22805) -#23022=* -exprs(#23022,9,#23020,0,"(x: infer U) => void") -hasLocation(#23022,#23021) -enclosing_stmt(#23022,#23005) -expr_containers(#23022,#22805) +literals("a","a",#23020) +is_abstract_member(#23018) +#23021=* +typeexprs(#23021,23,#23018,2,"(x: infer U) => void") +#23022=@"loc,{#10000},90,32,90,51" +locations_default(#23022,#10000,90,32,90,51) +hasLocation(#23021,#23022) +enclosing_stmt(#23021,#23006) +expr_containers(#23021,#22805) #23023=* -scopes(#23023,1) -scopenodes(#23022,#23023) -scopenesting(#23023,#23008) -#23024=@"var;{x};{#23023}" -variables(#23024,"x",#23023) -#23025=* -exprs(#23025,78,#23022,0,"x") -hasLocation(#23025,#21844) -expr_containers(#23025,#23022) -literals("x","x",#23025) -decl(#23025,#23024) -#23026=@"var;{arguments};{#23023}" -variables(#23026,"arguments",#23023) -is_arguments_object(#23026) -#23027=* -typeexprs(#23027,2,#23022,-3,"void") -hasLocation(#23027,#21856) -expr_containers(#23027,#23022) -literals("void","void",#23027) +exprs(#23023,9,#23021,0,"(x: infer U) => void") +hasLocation(#23023,#23022) +enclosing_stmt(#23023,#23006) +expr_containers(#23023,#22805) +#23024=* +scopes(#23024,1) +scopenodes(#23023,#23024) +scopenesting(#23024,#23009) +#23025=@"var;{x};{#23024}" +variables(#23025,"x",#23024) +#23026=* +exprs(#23026,78,#23023,0,"x") +hasLocation(#23026,#21844) +expr_containers(#23026,#23023) +literals("x","x",#23026) +decl(#23026,#23025) +#23027=@"var;{arguments};{#23024}" +variables(#23027,"arguments",#23024) +is_arguments_object(#23027) #23028=* -typeexprs(#23028,29,#23022,-6,"infer U") -#23029=@"loc,{#10000},90,36,90,42" -locations_default(#23029,#10000,90,36,90,42) -hasLocation(#23028,#23029) -expr_containers(#23028,#23022) -#23030=* -typeexprs(#23030,22,#23028,0,"U") -hasLocation(#23030,#21850) -expr_containers(#23030,#23022) +typeexprs(#23028,2,#23023,-3,"void") +hasLocation(#23028,#21856) +expr_containers(#23028,#23023) +literals("void","void",#23028) +#23029=* +typeexprs(#23029,29,#23023,-6,"infer U") +#23030=@"loc,{#10000},90,36,90,42" +locations_default(#23030,#10000,90,36,90,42) +hasLocation(#23029,#23030) +expr_containers(#23029,#23023) #23031=* -typeexprs(#23031,1,#23030,0,"U") +typeexprs(#23031,22,#23029,0,"U") hasLocation(#23031,#21850) -expr_containers(#23031,#23022) -literals("U","U",#23031) +expr_containers(#23031,#23023) #23032=* -properties(#23032,#23015,1,8,"b: (x: ... => void") -#23033=@"loc,{#10000},90,54,90,76" -locations_default(#23033,#10000,90,54,90,76) -hasLocation(#23032,#23033) -#23034=* -exprs(#23034,0,#23032,0,"b") -hasLocation(#23034,#21860) -enclosing_stmt(#23034,#23005) -expr_containers(#23034,#22805) -literals("b","b",#23034) -is_abstract_member(#23032) +typeexprs(#23032,1,#23031,0,"U") +hasLocation(#23032,#21850) +expr_containers(#23032,#23023) +literals("U","U",#23032) +#23033=* +properties(#23033,#23016,1,8,"b: (x: ... => void") +#23034=@"loc,{#10000},90,54,90,76" +locations_default(#23034,#10000,90,54,90,76) +hasLocation(#23033,#23034) #23035=* -typeexprs(#23035,23,#23032,2,"(x: infer U) => void") -#23036=@"loc,{#10000},90,57,90,76" -locations_default(#23036,#10000,90,57,90,76) -hasLocation(#23035,#23036) -enclosing_stmt(#23035,#23005) +exprs(#23035,0,#23033,0,"b") +hasLocation(#23035,#21860) +enclosing_stmt(#23035,#23006) expr_containers(#23035,#22805) -#23037=* -exprs(#23037,9,#23035,0,"(x: infer U) => void") -hasLocation(#23037,#23036) -enclosing_stmt(#23037,#23005) -expr_containers(#23037,#22805) +literals("b","b",#23035) +is_abstract_member(#23033) +#23036=* +typeexprs(#23036,23,#23033,2,"(x: infer U) => void") +#23037=@"loc,{#10000},90,57,90,76" +locations_default(#23037,#10000,90,57,90,76) +hasLocation(#23036,#23037) +enclosing_stmt(#23036,#23006) +expr_containers(#23036,#22805) #23038=* -scopes(#23038,1) -scopenodes(#23037,#23038) -scopenesting(#23038,#23008) -#23039=@"var;{x};{#23038}" -variables(#23039,"x",#23038) -#23040=* -exprs(#23040,78,#23037,0,"x") -hasLocation(#23040,#21866) -expr_containers(#23040,#23037) -literals("x","x",#23040) -decl(#23040,#23039) -#23041=@"var;{arguments};{#23038}" -variables(#23041,"arguments",#23038) -is_arguments_object(#23041) -#23042=* -typeexprs(#23042,2,#23037,-3,"void") -hasLocation(#23042,#21878) -expr_containers(#23042,#23037) -literals("void","void",#23042) +exprs(#23038,9,#23036,0,"(x: infer U) => void") +hasLocation(#23038,#23037) +enclosing_stmt(#23038,#23006) +expr_containers(#23038,#22805) +#23039=* +scopes(#23039,1) +scopenodes(#23038,#23039) +scopenesting(#23039,#23009) +#23040=@"var;{x};{#23039}" +variables(#23040,"x",#23039) +#23041=* +exprs(#23041,78,#23038,0,"x") +hasLocation(#23041,#21866) +expr_containers(#23041,#23038) +literals("x","x",#23041) +decl(#23041,#23040) +#23042=@"var;{arguments};{#23039}" +variables(#23042,"arguments",#23039) +is_arguments_object(#23042) #23043=* -typeexprs(#23043,29,#23037,-6,"infer U") -#23044=@"loc,{#10000},90,61,90,67" -locations_default(#23044,#10000,90,61,90,67) -hasLocation(#23043,#23044) -expr_containers(#23043,#23037) -#23045=* -typeexprs(#23045,22,#23043,0,"U") -hasLocation(#23045,#21872) -expr_containers(#23045,#23037) +typeexprs(#23043,2,#23038,-3,"void") +hasLocation(#23043,#21878) +expr_containers(#23043,#23038) +literals("void","void",#23043) +#23044=* +typeexprs(#23044,29,#23038,-6,"infer U") +#23045=@"loc,{#10000},90,61,90,67" +locations_default(#23045,#10000,90,61,90,67) +hasLocation(#23044,#23045) +expr_containers(#23044,#23038) #23046=* -typeexprs(#23046,1,#23045,0,"U") +typeexprs(#23046,22,#23044,0,"U") hasLocation(#23046,#21872) -expr_containers(#23046,#23037) -literals("U","U",#23046) +expr_containers(#23046,#23038) #23047=* -typeexprs(#23047,0,#23012,2,"U") -hasLocation(#23047,#21884) -enclosing_stmt(#23047,#23005) -expr_containers(#23047,#22805) +typeexprs(#23047,1,#23046,0,"U") +hasLocation(#23047,#21872) +expr_containers(#23047,#23038) literals("U","U",#23047) #23048=* -typeexprs(#23048,2,#23012,3,"never") -hasLocation(#23048,#21888) -enclosing_stmt(#23048,#23005) +typeexprs(#23048,0,#23013,2,"U") +hasLocation(#23048,#21884) +enclosing_stmt(#23048,#23006) expr_containers(#23048,#22805) -literals("never","never",#23048) +literals("U","U",#23048) #23049=* -stmts(#23049,35,#22805,11,"type T2 ... oid }>;") -#23050=@"loc,{#10000},91,3,91,69" -locations_default(#23050,#10000,91,3,91,69) -hasLocation(#23049,#23050) -stmt_containers(#23049,#22805) -#23051=* -typeexprs(#23051,1,#23049,0,"T20") -hasLocation(#23051,#21894) -enclosing_stmt(#23051,#23049) -expr_containers(#23051,#22805) -literals("T20","T20",#23051) -typedecl(#23051,#22820) +typeexprs(#23049,2,#23013,3,"never") +hasLocation(#23049,#21888) +enclosing_stmt(#23049,#23006) +expr_containers(#23049,#22805) +literals("never","never",#23049) +#23050=* +stmts(#23050,35,#22805,11,"type T2 ... oid }>;") +#23051=@"loc,{#10000},91,3,91,69" +locations_default(#23051,#10000,91,3,91,69) +hasLocation(#23050,#23051) +stmt_containers(#23050,#22805) #23052=* -typeexprs(#23052,14,#23049,1,"Bar<{ a ... void }>") -#23053=@"loc,{#10000},91,14,91,68" -locations_default(#23053,#10000,91,14,91,68) -hasLocation(#23052,#23053) -enclosing_stmt(#23052,#23049) +typeexprs(#23052,1,#23050,0,"T20") +hasLocation(#23052,#21894) +enclosing_stmt(#23052,#23050) expr_containers(#23052,#22805) -#23054=* -typeexprs(#23054,0,#23052,-1,"Bar") -hasLocation(#23054,#21898) -enclosing_stmt(#23054,#23049) -expr_containers(#23054,#22805) -literals("Bar","Bar",#23054) -typebind(#23054,#22819) +literals("T20","T20",#23052) +typedecl(#23052,#22821) +#23053=* +typeexprs(#23053,14,#23050,1,"Bar<{ a ... void }>") +#23054=@"loc,{#10000},91,14,91,68" +locations_default(#23054,#10000,91,14,91,68) +hasLocation(#23053,#23054) +enclosing_stmt(#23053,#23050) +expr_containers(#23053,#22805) #23055=* -typeexprs(#23055,21,#23052,0,"{ a: (x ... void }") -#23056=@"loc,{#10000},91,18,91,67" -locations_default(#23056,#10000,91,18,91,67) -hasLocation(#23055,#23056) -enclosing_stmt(#23055,#23049) +typeexprs(#23055,0,#23053,-1,"Bar") +hasLocation(#23055,#21898) +enclosing_stmt(#23055,#23050) expr_containers(#23055,#22805) -#23057=* -properties(#23057,#23055,0,8,"a: (x: ... > void,") -#23058=@"loc,{#10000},91,20,91,42" -locations_default(#23058,#10000,91,20,91,42) -hasLocation(#23057,#23058) -#23059=* -exprs(#23059,0,#23057,0,"a") -hasLocation(#23059,#21904) -enclosing_stmt(#23059,#23049) -expr_containers(#23059,#22805) -literals("a","a",#23059) -is_abstract_member(#23057) +literals("Bar","Bar",#23055) +typebind(#23055,#22820) +#23056=* +typeexprs(#23056,21,#23053,0,"{ a: (x ... void }") +#23057=@"loc,{#10000},91,18,91,67" +locations_default(#23057,#10000,91,18,91,67) +hasLocation(#23056,#23057) +enclosing_stmt(#23056,#23050) +expr_containers(#23056,#22805) +#23058=* +properties(#23058,#23056,0,8,"a: (x: ... > void,") +#23059=@"loc,{#10000},91,20,91,42" +locations_default(#23059,#10000,91,20,91,42) +hasLocation(#23058,#23059) #23060=* -typeexprs(#23060,23,#23057,2,"(x: string) => void") -#23061=@"loc,{#10000},91,23,91,41" -locations_default(#23061,#10000,91,23,91,41) -hasLocation(#23060,#23061) -enclosing_stmt(#23060,#23049) +exprs(#23060,0,#23058,0,"a") +hasLocation(#23060,#21904) +enclosing_stmt(#23060,#23050) expr_containers(#23060,#22805) -#23062=* -exprs(#23062,9,#23060,0,"(x: string) => void") -hasLocation(#23062,#23061) -enclosing_stmt(#23062,#23049) -expr_containers(#23062,#22805) +literals("a","a",#23060) +is_abstract_member(#23058) +#23061=* +typeexprs(#23061,23,#23058,2,"(x: string) => void") +#23062=@"loc,{#10000},91,23,91,41" +locations_default(#23062,#10000,91,23,91,41) +hasLocation(#23061,#23062) +enclosing_stmt(#23061,#23050) +expr_containers(#23061,#22805) #23063=* -scopes(#23063,1) -scopenodes(#23062,#23063) -scopenesting(#23063,#22808) -#23064=@"var;{x};{#23063}" -variables(#23064,"x",#23063) -#23065=* -exprs(#23065,78,#23062,0,"x") -hasLocation(#23065,#21910) -expr_containers(#23065,#23062) -literals("x","x",#23065) -decl(#23065,#23064) -#23066=@"var;{arguments};{#23063}" -variables(#23066,"arguments",#23063) -is_arguments_object(#23066) -#23067=* -typeexprs(#23067,2,#23062,-3,"void") -hasLocation(#23067,#21920) -expr_containers(#23067,#23062) -literals("void","void",#23067) +exprs(#23063,9,#23061,0,"(x: string) => void") +hasLocation(#23063,#23062) +enclosing_stmt(#23063,#23050) +expr_containers(#23063,#22805) +#23064=* +scopes(#23064,1) +scopenodes(#23063,#23064) +scopenesting(#23064,#22808) +#23065=@"var;{x};{#23064}" +variables(#23065,"x",#23064) +#23066=* +exprs(#23066,78,#23063,0,"x") +hasLocation(#23066,#21910) +expr_containers(#23066,#23063) +literals("x","x",#23066) +decl(#23066,#23065) +#23067=@"var;{arguments};{#23064}" +variables(#23067,"arguments",#23064) +is_arguments_object(#23067) #23068=* -typeexprs(#23068,2,#23062,-6,"string") -hasLocation(#23068,#21914) -expr_containers(#23068,#23062) -literals("string","string",#23068) +typeexprs(#23068,2,#23063,-3,"void") +hasLocation(#23068,#21920) +expr_containers(#23068,#23063) +literals("void","void",#23068) #23069=* -properties(#23069,#23055,1,8,"b: (x: ... => void") -#23070=@"loc,{#10000},91,44,91,65" -locations_default(#23070,#10000,91,44,91,65) -hasLocation(#23069,#23070) -#23071=* -exprs(#23071,0,#23069,0,"b") -hasLocation(#23071,#21924) -enclosing_stmt(#23071,#23049) -expr_containers(#23071,#22805) -literals("b","b",#23071) -is_abstract_member(#23069) +typeexprs(#23069,2,#23063,-6,"string") +hasLocation(#23069,#21914) +expr_containers(#23069,#23063) +literals("string","string",#23069) +#23070=* +properties(#23070,#23056,1,8,"b: (x: ... => void") +#23071=@"loc,{#10000},91,44,91,65" +locations_default(#23071,#10000,91,44,91,65) +hasLocation(#23070,#23071) #23072=* -typeexprs(#23072,23,#23069,2,"(x: string) => void") -#23073=@"loc,{#10000},91,47,91,65" -locations_default(#23073,#10000,91,47,91,65) -hasLocation(#23072,#23073) -enclosing_stmt(#23072,#23049) +exprs(#23072,0,#23070,0,"b") +hasLocation(#23072,#21924) +enclosing_stmt(#23072,#23050) expr_containers(#23072,#22805) -#23074=* -exprs(#23074,9,#23072,0,"(x: string) => void") -hasLocation(#23074,#23073) -enclosing_stmt(#23074,#23049) -expr_containers(#23074,#22805) +literals("b","b",#23072) +is_abstract_member(#23070) +#23073=* +typeexprs(#23073,23,#23070,2,"(x: string) => void") +#23074=@"loc,{#10000},91,47,91,65" +locations_default(#23074,#10000,91,47,91,65) +hasLocation(#23073,#23074) +enclosing_stmt(#23073,#23050) +expr_containers(#23073,#22805) #23075=* -scopes(#23075,1) -scopenodes(#23074,#23075) -scopenesting(#23075,#22808) -#23076=@"var;{x};{#23075}" -variables(#23076,"x",#23075) -#23077=* -exprs(#23077,78,#23074,0,"x") -hasLocation(#23077,#21930) -expr_containers(#23077,#23074) -literals("x","x",#23077) -decl(#23077,#23076) -#23078=@"var;{arguments};{#23075}" -variables(#23078,"arguments",#23075) -is_arguments_object(#23078) -#23079=* -typeexprs(#23079,2,#23074,-3,"void") -hasLocation(#23079,#21940) -expr_containers(#23079,#23074) -literals("void","void",#23079) +exprs(#23075,9,#23073,0,"(x: string) => void") +hasLocation(#23075,#23074) +enclosing_stmt(#23075,#23050) +expr_containers(#23075,#22805) +#23076=* +scopes(#23076,1) +scopenodes(#23075,#23076) +scopenesting(#23076,#22808) +#23077=@"var;{x};{#23076}" +variables(#23077,"x",#23076) +#23078=* +exprs(#23078,78,#23075,0,"x") +hasLocation(#23078,#21930) +expr_containers(#23078,#23075) +literals("x","x",#23078) +decl(#23078,#23077) +#23079=@"var;{arguments};{#23076}" +variables(#23079,"arguments",#23076) +is_arguments_object(#23079) #23080=* -typeexprs(#23080,2,#23074,-6,"string") -hasLocation(#23080,#21934) -expr_containers(#23080,#23074) -literals("string","string",#23080) +typeexprs(#23080,2,#23075,-3,"void") +hasLocation(#23080,#21940) +expr_containers(#23080,#23075) +literals("void","void",#23080) #23081=* -stmts(#23081,35,#22805,12,"type T2 ... oid }>;") -#23082=@"loc,{#10000},92,3,92,69" -locations_default(#23082,#10000,92,3,92,69) -hasLocation(#23081,#23082) -stmt_containers(#23081,#22805) -#23083=* -typeexprs(#23083,1,#23081,0,"T21") -hasLocation(#23083,#21950) -enclosing_stmt(#23083,#23081) -expr_containers(#23083,#22805) -literals("T21","T21",#23083) -typedecl(#23083,#22821) +typeexprs(#23081,2,#23075,-6,"string") +hasLocation(#23081,#21934) +expr_containers(#23081,#23075) +literals("string","string",#23081) +#23082=* +stmts(#23082,35,#22805,12,"type T2 ... oid }>;") +#23083=@"loc,{#10000},92,3,92,69" +locations_default(#23083,#10000,92,3,92,69) +hasLocation(#23082,#23083) +stmt_containers(#23082,#22805) #23084=* -typeexprs(#23084,14,#23081,1,"Bar<{ a ... void }>") -#23085=@"loc,{#10000},92,14,92,68" -locations_default(#23085,#10000,92,14,92,68) -hasLocation(#23084,#23085) -enclosing_stmt(#23084,#23081) +typeexprs(#23084,1,#23082,0,"T21") +hasLocation(#23084,#21950) +enclosing_stmt(#23084,#23082) expr_containers(#23084,#22805) -#23086=* -typeexprs(#23086,0,#23084,-1,"Bar") -hasLocation(#23086,#21954) -enclosing_stmt(#23086,#23081) -expr_containers(#23086,#22805) -literals("Bar","Bar",#23086) -typebind(#23086,#22819) +literals("T21","T21",#23084) +typedecl(#23084,#22822) +#23085=* +typeexprs(#23085,14,#23082,1,"Bar<{ a ... void }>") +#23086=@"loc,{#10000},92,14,92,68" +locations_default(#23086,#10000,92,14,92,68) +hasLocation(#23085,#23086) +enclosing_stmt(#23085,#23082) +expr_containers(#23085,#22805) #23087=* -typeexprs(#23087,21,#23084,0,"{ a: (x ... void }") -#23088=@"loc,{#10000},92,18,92,67" -locations_default(#23088,#10000,92,18,92,67) -hasLocation(#23087,#23088) -enclosing_stmt(#23087,#23081) +typeexprs(#23087,0,#23085,-1,"Bar") +hasLocation(#23087,#21954) +enclosing_stmt(#23087,#23082) expr_containers(#23087,#22805) -#23089=* -properties(#23089,#23087,0,8,"a: (x: ... > void,") -#23090=@"loc,{#10000},92,20,92,42" -locations_default(#23090,#10000,92,20,92,42) -hasLocation(#23089,#23090) -#23091=* -exprs(#23091,0,#23089,0,"a") -hasLocation(#23091,#21960) -enclosing_stmt(#23091,#23081) -expr_containers(#23091,#22805) -literals("a","a",#23091) -is_abstract_member(#23089) +literals("Bar","Bar",#23087) +typebind(#23087,#22820) +#23088=* +typeexprs(#23088,21,#23085,0,"{ a: (x ... void }") +#23089=@"loc,{#10000},92,18,92,67" +locations_default(#23089,#10000,92,18,92,67) +hasLocation(#23088,#23089) +enclosing_stmt(#23088,#23082) +expr_containers(#23088,#22805) +#23090=* +properties(#23090,#23088,0,8,"a: (x: ... > void,") +#23091=@"loc,{#10000},92,20,92,42" +locations_default(#23091,#10000,92,20,92,42) +hasLocation(#23090,#23091) #23092=* -typeexprs(#23092,23,#23089,2,"(x: string) => void") -#23093=@"loc,{#10000},92,23,92,41" -locations_default(#23093,#10000,92,23,92,41) -hasLocation(#23092,#23093) -enclosing_stmt(#23092,#23081) +exprs(#23092,0,#23090,0,"a") +hasLocation(#23092,#21960) +enclosing_stmt(#23092,#23082) expr_containers(#23092,#22805) -#23094=* -exprs(#23094,9,#23092,0,"(x: string) => void") -hasLocation(#23094,#23093) -enclosing_stmt(#23094,#23081) -expr_containers(#23094,#22805) +literals("a","a",#23092) +is_abstract_member(#23090) +#23093=* +typeexprs(#23093,23,#23090,2,"(x: string) => void") +#23094=@"loc,{#10000},92,23,92,41" +locations_default(#23094,#10000,92,23,92,41) +hasLocation(#23093,#23094) +enclosing_stmt(#23093,#23082) +expr_containers(#23093,#22805) #23095=* -scopes(#23095,1) -scopenodes(#23094,#23095) -scopenesting(#23095,#22808) -#23096=@"var;{x};{#23095}" -variables(#23096,"x",#23095) -#23097=* -exprs(#23097,78,#23094,0,"x") -hasLocation(#23097,#21966) -expr_containers(#23097,#23094) -literals("x","x",#23097) -decl(#23097,#23096) -#23098=@"var;{arguments};{#23095}" -variables(#23098,"arguments",#23095) -is_arguments_object(#23098) -#23099=* -typeexprs(#23099,2,#23094,-3,"void") -hasLocation(#23099,#21976) -expr_containers(#23099,#23094) -literals("void","void",#23099) +exprs(#23095,9,#23093,0,"(x: string) => void") +hasLocation(#23095,#23094) +enclosing_stmt(#23095,#23082) +expr_containers(#23095,#22805) +#23096=* +scopes(#23096,1) +scopenodes(#23095,#23096) +scopenesting(#23096,#22808) +#23097=@"var;{x};{#23096}" +variables(#23097,"x",#23096) +#23098=* +exprs(#23098,78,#23095,0,"x") +hasLocation(#23098,#21966) +expr_containers(#23098,#23095) +literals("x","x",#23098) +decl(#23098,#23097) +#23099=@"var;{arguments};{#23096}" +variables(#23099,"arguments",#23096) +is_arguments_object(#23099) #23100=* -typeexprs(#23100,2,#23094,-6,"string") -hasLocation(#23100,#21970) -expr_containers(#23100,#23094) -literals("string","string",#23100) +typeexprs(#23100,2,#23095,-3,"void") +hasLocation(#23100,#21976) +expr_containers(#23100,#23095) +literals("void","void",#23100) #23101=* -properties(#23101,#23087,1,8,"b: (x: ... => void") -#23102=@"loc,{#10000},92,44,92,65" -locations_default(#23102,#10000,92,44,92,65) -hasLocation(#23101,#23102) -#23103=* -exprs(#23103,0,#23101,0,"b") -hasLocation(#23103,#21980) -enclosing_stmt(#23103,#23081) -expr_containers(#23103,#22805) -literals("b","b",#23103) -is_abstract_member(#23101) +typeexprs(#23101,2,#23095,-6,"string") +hasLocation(#23101,#21970) +expr_containers(#23101,#23095) +literals("string","string",#23101) +#23102=* +properties(#23102,#23088,1,8,"b: (x: ... => void") +#23103=@"loc,{#10000},92,44,92,65" +locations_default(#23103,#10000,92,44,92,65) +hasLocation(#23102,#23103) #23104=* -typeexprs(#23104,23,#23101,2,"(x: number) => void") -#23105=@"loc,{#10000},92,47,92,65" -locations_default(#23105,#10000,92,47,92,65) -hasLocation(#23104,#23105) -enclosing_stmt(#23104,#23081) +exprs(#23104,0,#23102,0,"b") +hasLocation(#23104,#21980) +enclosing_stmt(#23104,#23082) expr_containers(#23104,#22805) -#23106=* -exprs(#23106,9,#23104,0,"(x: number) => void") -hasLocation(#23106,#23105) -enclosing_stmt(#23106,#23081) -expr_containers(#23106,#22805) +literals("b","b",#23104) +is_abstract_member(#23102) +#23105=* +typeexprs(#23105,23,#23102,2,"(x: number) => void") +#23106=@"loc,{#10000},92,47,92,65" +locations_default(#23106,#10000,92,47,92,65) +hasLocation(#23105,#23106) +enclosing_stmt(#23105,#23082) +expr_containers(#23105,#22805) #23107=* -scopes(#23107,1) -scopenodes(#23106,#23107) -scopenesting(#23107,#22808) -#23108=@"var;{x};{#23107}" -variables(#23108,"x",#23107) -#23109=* -exprs(#23109,78,#23106,0,"x") -hasLocation(#23109,#21986) -expr_containers(#23109,#23106) -literals("x","x",#23109) -decl(#23109,#23108) -#23110=@"var;{arguments};{#23107}" -variables(#23110,"arguments",#23107) -is_arguments_object(#23110) -#23111=* -typeexprs(#23111,2,#23106,-3,"void") -hasLocation(#23111,#21996) -expr_containers(#23111,#23106) -literals("void","void",#23111) +exprs(#23107,9,#23105,0,"(x: number) => void") +hasLocation(#23107,#23106) +enclosing_stmt(#23107,#23082) +expr_containers(#23107,#22805) +#23108=* +scopes(#23108,1) +scopenodes(#23107,#23108) +scopenesting(#23108,#22808) +#23109=@"var;{x};{#23108}" +variables(#23109,"x",#23108) +#23110=* +exprs(#23110,78,#23107,0,"x") +hasLocation(#23110,#21986) +expr_containers(#23110,#23107) +literals("x","x",#23110) +decl(#23110,#23109) +#23111=@"var;{arguments};{#23108}" +variables(#23111,"arguments",#23108) +is_arguments_object(#23111) #23112=* -typeexprs(#23112,2,#23106,-6,"number") -hasLocation(#23112,#21990) -expr_containers(#23112,#23106) -literals("number","number",#23112) +typeexprs(#23112,2,#23107,-3,"void") +hasLocation(#23112,#21996) +expr_containers(#23112,#23107) +literals("void","void",#23112) #23113=* -stmts(#23113,17,#22805,13,"declare ... number;") -#23114=@"loc,{#10000},94,3,94,42" -locations_default(#23114,#10000,94,3,94,42) -hasLocation(#23113,#23114) -stmt_containers(#23113,#22805) -has_declare_keyword(#23113) -#23115=* -exprs(#23115,78,#23113,-1,"foo") -hasLocation(#23115,#22008) -expr_containers(#23115,#23113) -literals("foo","foo",#23115) -#23116=@"var;{foo};{#20000}" -variables(#23116,"foo",#20000) -decl(#23115,#23116) +typeexprs(#23113,2,#23107,-6,"number") +hasLocation(#23113,#21990) +expr_containers(#23113,#23107) +literals("number","number",#23113) +#23114=* +stmts(#23114,17,#22805,13,"declare ... number;") +#23115=@"loc,{#10000},94,3,94,42" +locations_default(#23115,#10000,94,3,94,42) +hasLocation(#23114,#23115) +stmt_containers(#23114,#22805) +has_declare_keyword(#23114) +#23116=* +exprs(#23116,78,#23114,-1,"foo") +hasLocation(#23116,#22008) +expr_containers(#23116,#23114) +literals("foo","foo",#23116) +decl(#23116,#22809) #23117=* scopes(#23117,1) -scopenodes(#23113,#23117) +scopenodes(#23114,#23117) scopenesting(#23117,#22808) #23118=@"var;{x};{#23117}" variables(#23118,"x",#23117) #23119=* -exprs(#23119,78,#23113,0,"x") +exprs(#23119,78,#23114,0,"x") hasLocation(#23119,#22012) -expr_containers(#23119,#23113) +expr_containers(#23119,#23114) literals("x","x",#23119) decl(#23119,#23118) #23120=@"var;{arguments};{#23117}" variables(#23120,"arguments",#23117) is_arguments_object(#23120) #23121=* -typeexprs(#23121,2,#23113,-3,"number") +typeexprs(#23121,2,#23114,-3,"number") hasLocation(#23121,#22022) -expr_containers(#23121,#23113) +expr_containers(#23121,#23114) literals("number","number",#23121) #23122=* -typeexprs(#23122,2,#23113,-6,"string") +typeexprs(#23122,2,#23114,-6,"string") hasLocation(#23122,#22016) -expr_containers(#23122,#23113) +expr_containers(#23122,#23114) literals("string","string",#23122) #23123=* stmts(#23123,17,#22805,14,"declare ... string;") @@ -9757,7 +9757,7 @@ exprs(#23125,78,#23123,-1,"foo") hasLocation(#23125,#22030) expr_containers(#23125,#23123) literals("foo","foo",#23125) -decl(#23125,#23116) +decl(#23125,#22809) #23126=* scopes(#23126,1) scopenodes(#23123,#23126) @@ -9795,7 +9795,7 @@ exprs(#23134,78,#23132,-1,"foo") hasLocation(#23134,#22052) expr_containers(#23134,#23132) literals("foo","foo",#23134) -decl(#23134,#23116) +decl(#23134,#22809) #23135=* scopes(#23135,1) scopenodes(#23132,#23135) @@ -9855,7 +9855,7 @@ hasLocation(#23149,#22080) enclosing_stmt(#23149,#23147) expr_containers(#23149,#22805) literals("T30","T30",#23149) -typedecl(#23149,#22822) +typedecl(#23149,#22823) #23150=* typeexprs(#23150,14,#23147,1,"ReturnT ... of foo>") #23151=@"loc,{#10000},97,14,97,35" @@ -9869,7 +9869,7 @@ hasLocation(#23152,#22084) enclosing_stmt(#23152,#23147) expr_containers(#23152,#22805) literals("ReturnType","ReturnType",#23152) -typebind(#23152,#22824) +typebind(#23152,#22825) #23153=* typeexprs(#23153,16,#23150,0,"typeof foo") #23154=@"loc,{#10000},97,25,97,34" @@ -9883,7 +9883,7 @@ hasLocation(#23155,#22090) enclosing_stmt(#23155,#23147) expr_containers(#23155,#22805) literals("foo","foo",#23155) -bind(#23155,#23116) +bind(#23155,#22809) #23156=* stmts(#23156,35,#22805,17,"type An ... => any;") #23157=@"loc,{#10000},99,3,99,45" @@ -9896,7 +9896,7 @@ hasLocation(#23158,#22098) enclosing_stmt(#23158,#23156) expr_containers(#23158,#22805) literals("AnyFunction","AnyFunction",#23158) -typedecl(#23158,#22823) +typedecl(#23158,#22824) #23159=* typeexprs(#23159,23,#23156,1,"(...arg ... => any") #23160=@"loc,{#10000},99,22,99,44" @@ -9953,7 +9953,7 @@ hasLocation(#23172,#22126) enclosing_stmt(#23172,#23170) expr_containers(#23172,#22805) literals("ReturnType","ReturnType",#23172) -typedecl(#23172,#22824) +typedecl(#23172,#22825) #23173=* scopes(#23173,12) scopenodes(#23170,#23173) @@ -9980,7 +9980,7 @@ hasLocation(#23178,#22134) enclosing_stmt(#23178,#23170) expr_containers(#23178,#22805) literals("AnyFunction","AnyFunction",#23178) -typebind(#23178,#22823) +typebind(#23178,#22824) #23179=* typeexprs(#23179,28,#23170,1,"T exten ... R : any") #23180=@"loc,{#10000},100,44,100,90" @@ -10084,21 +10084,21 @@ successor(#23156,#23170) successor(#23147,#23156) successor(#23132,#23147) successor(#23123,#23132) -successor(#23113,#23123) -successor(#23081,#23113) -successor(#23049,#23081) -successor(#23005,#23049) -successor(#22989,#23005) -successor(#22973,#22989) -successor(#22945,#22973) -successor(#22930,#22945) -successor(#22918,#22930) -successor(#22908,#22918) -successor(#22896,#22908) -successor(#22887,#22896) -successor(#22880,#22887) -successor(#22825,#22880) -successor(#22805,#22825) +successor(#23114,#23123) +successor(#23082,#23114) +successor(#23050,#23082) +successor(#23006,#23050) +successor(#22990,#23006) +successor(#22974,#22990) +successor(#22946,#22974) +successor(#22931,#22946) +successor(#22919,#22931) +successor(#22909,#22919) +successor(#22897,#22909) +successor(#22888,#22897) +successor(#22881,#22888) +successor(#22826,#22881) +successor(#22805,#22826) successor(#22185,#22183) successor(#22777,#22807) successor(#22770,#22777) diff --git a/javascript/extractor/tests/ts/output/trap/declareClass.ts.trap b/javascript/extractor/tests/ts/output/trap/declareClass.ts.trap index 6bc9fb5ffb25..83ada22ef89a 100644 --- a/javascript/extractor/tests/ts/output/trap/declareClass.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/declareClass.ts.trap @@ -50,61 +50,64 @@ toplevels(#20001,0) #20016=@"loc,{#10000},1,1,2,0" locations_default(#20016,#10000,1,1,2,0) hasLocation(#20001,#20016) -#20017=* -stmts(#20017,26,#20001,0,"declare class C {}") -hasLocation(#20017,#20003) -stmt_containers(#20017,#20001) -has_declare_keyword(#20017) -#20018=* -exprs(#20018,78,#20017,0,"C") -hasLocation(#20018,#20009) -enclosing_stmt(#20018,#20017) -expr_containers(#20018,#20001) -literals("C","C",#20018) -#20019=@"var;{C};{#20000}" -variables(#20019,"C",#20000) -decl(#20018,#20019) +#20017=@"var;{C};{#20000}" +variables(#20017,"C",#20000) +#20018=@"local_type_name;{C};{#20000}" +local_type_names(#20018,"C",#20000) +#20019=* +stmts(#20019,26,#20001,0,"declare class C {}") +hasLocation(#20019,#20003) +stmt_containers(#20019,#20001) +has_declare_keyword(#20019) #20020=* -scopes(#20020,10) -scopenodes(#20017,#20020) -scopenesting(#20020,#20000) +exprs(#20020,78,#20019,0,"C") +hasLocation(#20020,#20009) +enclosing_stmt(#20020,#20019) +expr_containers(#20020,#20001) +literals("C","C",#20020) +decl(#20020,#20017) +typedecl(#20020,#20018) #20021=* -properties(#20021,#20017,2,0,"constructor() {}") -#20022=@"loc,{#10000},1,17,1,16" -locations_default(#20022,#10000,1,17,1,16) -hasLocation(#20021,#20022) -#20023=* -exprs(#20023,0,#20021,0,"constructor") -hasLocation(#20023,#20022) -enclosing_stmt(#20023,#20017) -expr_containers(#20023,#20001) -literals("constructor","constructor",#20023) +scopes(#20021,10) +scopenodes(#20019,#20021) +scopenesting(#20021,#20000) +#20022=* +properties(#20022,#20019,2,0,"constructor() {}") +#20023=@"loc,{#10000},1,17,1,16" +locations_default(#20023,#10000,1,17,1,16) +hasLocation(#20022,#20023) #20024=* -exprs(#20024,9,#20021,1,"() {}") -hasLocation(#20024,#20022) -enclosing_stmt(#20024,#20017) +exprs(#20024,0,#20022,0,"constructor") +hasLocation(#20024,#20023) +enclosing_stmt(#20024,#20019) expr_containers(#20024,#20001) +literals("constructor","constructor",#20024) #20025=* -scopes(#20025,1) -scopenodes(#20024,#20025) -scopenesting(#20025,#20020) -#20026=@"var;{arguments};{#20025}" -variables(#20026,"arguments",#20025) -is_arguments_object(#20026) -#20027=* -stmts(#20027,1,#20024,-2,"{}") -hasLocation(#20027,#20022) -stmt_containers(#20027,#20024) -is_method(#20021) +exprs(#20025,9,#20022,1,"() {}") +hasLocation(#20025,#20023) +enclosing_stmt(#20025,#20019) +expr_containers(#20025,#20001) +#20026=* +scopes(#20026,1) +scopenodes(#20025,#20026) +scopenesting(#20026,#20021) +#20027=@"var;{arguments};{#20026}" +variables(#20027,"arguments",#20026) +is_arguments_object(#20027) #20028=* -entry_cfg_node(#20028,#20001) -#20029=@"loc,{#10000},1,1,1,0" -locations_default(#20029,#10000,1,1,1,0) -hasLocation(#20028,#20029) -#20030=* -exit_cfg_node(#20030,#20001) -hasLocation(#20030,#20015) -successor(#20017,#20030) -successor(#20028,#20017) +stmts(#20028,1,#20025,-2,"{}") +hasLocation(#20028,#20023) +stmt_containers(#20028,#20025) +is_method(#20022) +#20029=* +entry_cfg_node(#20029,#20001) +#20030=@"loc,{#10000},1,1,1,0" +locations_default(#20030,#10000,1,1,1,0) +hasLocation(#20029,#20030) +#20031=* +exit_cfg_node(#20031,#20001) +hasLocation(#20031,#20015) +successor(#20019,#20031) +successor(#20029,#20019) numlines(#10000,1,1,0) filetype(#10000,"typescript") diff --git a/javascript/extractor/tests/ts/output/trap/functiondecorators.ts.trap b/javascript/extractor/tests/ts/output/trap/functiondecorators.ts.trap index 89cd17604433..00276bb64641 100644 --- a/javascript/extractor/tests/ts/output/trap/functiondecorators.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/functiondecorators.ts.trap @@ -584,62 +584,63 @@ toplevels(#20001,0) #20221=@"loc,{#10000},1,1,16,0" locations_default(#20221,#10000,1,1,16,0) hasLocation(#20001,#20221) -#20222=@"var;{C};{#20000}" -variables(#20222,"C",#20000) -#20223=@"local_type_name;{C};{#20000}" -local_type_names(#20223,"C",#20000) -#20224=* -stmts(#20224,18,#20001,0,"declare var A : any;") -hasLocation(#20224,#20003) -stmt_containers(#20224,#20001) -has_declare_keyword(#20224) -#20225=* -exprs(#20225,64,#20224,0,"A : any") -#20226=@"loc,{#10000},1,13,1,19" -locations_default(#20226,#10000,1,13,1,19) -hasLocation(#20225,#20226) -enclosing_stmt(#20225,#20224) -expr_containers(#20225,#20001) +#20222=@"var;{A};{#20000}" +variables(#20222,"A",#20000) +#20223=@"var;{B};{#20000}" +variables(#20223,"B",#20000) +#20224=@"var;{C};{#20000}" +variables(#20224,"C",#20000) +variables(#20224,"C",#20000) +#20225=@"local_type_name;{C};{#20000}" +local_type_names(#20225,"C",#20000) +#20226=* +stmts(#20226,18,#20001,0,"declare var A : any;") +hasLocation(#20226,#20003) +stmt_containers(#20226,#20001) +has_declare_keyword(#20226) #20227=* -exprs(#20227,78,#20225,0,"A") -hasLocation(#20227,#20037) -enclosing_stmt(#20227,#20224) +exprs(#20227,64,#20226,0,"A : any") +#20228=@"loc,{#10000},1,13,1,19" +locations_default(#20228,#10000,1,13,1,19) +hasLocation(#20227,#20228) +enclosing_stmt(#20227,#20226) expr_containers(#20227,#20001) -literals("A","A",#20227) -#20228=@"var;{A};{#20000}" -variables(#20228,"A",#20000) -decl(#20227,#20228) #20229=* -typeexprs(#20229,2,#20225,2,"any") -hasLocation(#20229,#20041) -enclosing_stmt(#20229,#20224) +exprs(#20229,78,#20227,0,"A") +hasLocation(#20229,#20037) +enclosing_stmt(#20229,#20226) expr_containers(#20229,#20001) -literals("any","any",#20229) +literals("A","A",#20229) +decl(#20229,#20222) #20230=* -stmts(#20230,18,#20001,1,"declare var B : any;") -hasLocation(#20230,#20005) -stmt_containers(#20230,#20001) -has_declare_keyword(#20230) +typeexprs(#20230,2,#20227,2,"any") +hasLocation(#20230,#20041) +enclosing_stmt(#20230,#20226) +expr_containers(#20230,#20001) +literals("any","any",#20230) #20231=* -exprs(#20231,64,#20230,0,"B : any") -#20232=@"loc,{#10000},2,13,2,19" -locations_default(#20232,#10000,2,13,2,19) -hasLocation(#20231,#20232) -enclosing_stmt(#20231,#20230) -expr_containers(#20231,#20001) -#20233=* -exprs(#20233,78,#20231,0,"B") -hasLocation(#20233,#20049) -enclosing_stmt(#20233,#20230) -expr_containers(#20233,#20001) -literals("B","B",#20233) -#20234=@"var;{B};{#20000}" -variables(#20234,"B",#20000) -decl(#20233,#20234) +stmts(#20231,18,#20001,1,"declare var B : any;") +hasLocation(#20231,#20005) +stmt_containers(#20231,#20001) +has_declare_keyword(#20231) +#20232=* +exprs(#20232,64,#20231,0,"B : any") +#20233=@"loc,{#10000},2,13,2,19" +locations_default(#20233,#10000,2,13,2,19) +hasLocation(#20232,#20233) +enclosing_stmt(#20232,#20231) +expr_containers(#20232,#20001) +#20234=* +exprs(#20234,78,#20232,0,"B") +hasLocation(#20234,#20049) +enclosing_stmt(#20234,#20231) +expr_containers(#20234,#20001) +literals("B","B",#20234) +decl(#20234,#20223) #20235=* -typeexprs(#20235,2,#20231,2,"any") +typeexprs(#20235,2,#20232,2,"any") hasLocation(#20235,#20053) -enclosing_stmt(#20235,#20230) +enclosing_stmt(#20235,#20231) expr_containers(#20235,#20001) literals("any","any",#20235) #20236=* @@ -660,7 +661,7 @@ hasLocation(#20239,#20061) enclosing_stmt(#20239,#20236) expr_containers(#20239,#20001) literals("C","C",#20239) -decl(#20239,#20222) +decl(#20239,#20224) #20240=* typeexprs(#20240,2,#20237,2,"any") hasLocation(#20240,#20065) @@ -679,8 +680,8 @@ hasLocation(#20243,#20071) enclosing_stmt(#20243,#20241) expr_containers(#20243,#20001) literals("C","C",#20243) -decl(#20243,#20222) -typedecl(#20243,#20223) +decl(#20243,#20224) +typedecl(#20243,#20225) #20244=* scopes(#20244,10) scopenodes(#20241,#20244) @@ -769,7 +770,7 @@ exprs(#20266,79,#20265,0,"A") hasLocation(#20266,#20093) expr_containers(#20266,#20258) literals("A","A",#20266) -bind(#20266,#20228) +bind(#20266,#20222) #20267=* stmts(#20267,1,#20258,-2,"{}") #20268=@"loc,{#10000},7,12,7,13" @@ -825,7 +826,7 @@ exprs(#20281,79,#20279,0,"A") hasLocation(#20281,#20109) expr_containers(#20281,#20272) literals("A","A",#20281) -bind(#20281,#20228) +bind(#20281,#20222) #20282=* exprs(#20282,94,#20277,1,"@B") #20283=@"loc,{#10000},8,9,8,10" @@ -837,7 +838,7 @@ exprs(#20284,79,#20282,0,"B") hasLocation(#20284,#20113) expr_containers(#20284,#20272) literals("B","B",#20284) -bind(#20284,#20234) +bind(#20284,#20223) #20285=* stmts(#20285,1,#20272,-2,"{}") #20286=@"loc,{#10000},8,16,8,17" @@ -899,7 +900,7 @@ exprs(#20300,79,#20299,0,"A") hasLocation(#20300,#20133) expr_containers(#20300,#20290) literals("A","A",#20300) -bind(#20300,#20228) +bind(#20300,#20222) #20301=* stmts(#20301,1,#20290,-2,"{}") #20302=@"loc,{#10000},10,18,10,19" @@ -963,7 +964,7 @@ exprs(#20317,79,#20315,0,"A") hasLocation(#20317,#20153) expr_containers(#20317,#20306) literals("A","A",#20317) -bind(#20317,#20228) +bind(#20317,#20222) #20318=* exprs(#20318,94,#20313,1,"@B") #20319=@"loc,{#10000},11,15,11,16" @@ -975,7 +976,7 @@ exprs(#20320,79,#20318,0,"B") hasLocation(#20320,#20157) expr_containers(#20320,#20306) literals("B","B",#20320) -bind(#20320,#20234) +bind(#20320,#20223) #20321=* stmts(#20321,1,#20306,-2,"{}") #20322=@"loc,{#10000},11,22,11,23" @@ -1037,7 +1038,7 @@ exprs(#20336,79,#20335,0,"A") hasLocation(#20336,#20173) expr_containers(#20336,#20326) literals("A","A",#20336) -bind(#20336,#20228) +bind(#20336,#20222) #20337=* exprs(#20337,104,#20326,-12,"@B") #20338=@"loc,{#10000},13,12,13,13" @@ -1053,7 +1054,7 @@ exprs(#20340,79,#20339,0,"B") hasLocation(#20340,#20181) expr_containers(#20340,#20326) literals("B","B",#20340) -bind(#20340,#20234) +bind(#20340,#20223) #20341=* stmts(#20341,1,#20326,-2,"{}") #20342=@"loc,{#10000},13,18,13,19" @@ -1115,7 +1116,7 @@ exprs(#20356,79,#20355,0,"A") hasLocation(#20356,#20197) expr_containers(#20356,#20346) literals("A","A",#20356) -bind(#20356,#20228) +bind(#20356,#20222) #20357=* exprs(#20357,104,#20346,-12,"@B @C") #20358=@"loc,{#10000},14,12,14,16" @@ -1133,7 +1134,7 @@ exprs(#20361,79,#20359,0,"B") hasLocation(#20361,#20205) expr_containers(#20361,#20346) literals("B","B",#20361) -bind(#20361,#20234) +bind(#20361,#20223) #20362=* exprs(#20362,94,#20357,1,"@C") #20363=@"loc,{#10000},14,15,14,16" @@ -1145,7 +1146,7 @@ exprs(#20364,79,#20362,0,"C") hasLocation(#20364,#20209) expr_containers(#20364,#20346) literals("C","C",#20364) -bind(#20364,#20222) +bind(#20364,#20224) #20365=* stmts(#20365,1,#20346,-2,"{}") #20366=@"loc,{#10000},14,22,14,23" @@ -1349,8 +1350,8 @@ successor(#20265,#20263) successor(#20263,#20281) successor(#20241,#20266) successor(#20236,#20243) -successor(#20230,#20236) -successor(#20224,#20230) -successor(#20374,#20224) +successor(#20231,#20236) +successor(#20226,#20231) +successor(#20374,#20226) numlines(#10000,15,12,0) filetype(#10000,"typescript") diff --git a/javascript/extractor/tests/ts/output/trap/nobody.ts.trap b/javascript/extractor/tests/ts/output/trap/nobody.ts.trap index 44a32603e097..717d79423abd 100644 --- a/javascript/extractor/tests/ts/output/trap/nobody.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/nobody.ts.trap @@ -694,506 +694,509 @@ toplevels(#20001,0) #20252=@"loc,{#10000},2,1,30,0" locations_default(#20252,#10000,2,1,30,0) hasLocation(#20001,#20252) -#20253=@"var;{C};{#20000}" -variables(#20253,"C",#20000) -#20254=@"local_type_name;{C};{#20000}" -local_type_names(#20254,"C",#20000) -#20255=* -stmts(#20255,17,#20001,0,"declare ... on f();") -hasLocation(#20255,#20020) -stmt_containers(#20255,#20001) -has_declare_keyword(#20255) -#20256=* -exprs(#20256,78,#20255,-1,"f") -hasLocation(#20256,#20079) -expr_containers(#20256,#20255) -literals("f","f",#20256) -#20257=@"var;{f};{#20000}" -variables(#20257,"f",#20000) -decl(#20256,#20257) +#20253=@"var;{f};{#20000}" +variables(#20253,"f",#20000) +#20254=@"var;{C};{#20000}" +variables(#20254,"C",#20000) +#20255=@"var;{D};{#20000}" +variables(#20255,"D",#20000) +#20256=@"local_type_name;{C};{#20000}" +local_type_names(#20256,"C",#20000) +#20257=@"local_type_name;{D};{#20000}" +local_type_names(#20257,"D",#20000) #20258=* -scopes(#20258,1) -scopenodes(#20255,#20258) -scopenesting(#20258,#20000) -#20259=@"var;{arguments};{#20258}" -variables(#20259,"arguments",#20258) -is_arguments_object(#20259) +stmts(#20258,17,#20001,0,"declare ... on f();") +hasLocation(#20258,#20020) +stmt_containers(#20258,#20001) +has_declare_keyword(#20258) +#20259=* +exprs(#20259,78,#20258,-1,"f") +hasLocation(#20259,#20079) +expr_containers(#20259,#20258) +literals("f","f",#20259) +decl(#20259,#20253) #20260=* -stmts(#20260,26,#20001,1,"abstrac ... mber;\n}") -#20261=@"loc,{#10000},4,1,15,1" -locations_default(#20261,#10000,4,1,15,1) -hasLocation(#20260,#20261) -stmt_containers(#20260,#20001) -is_abstract_class(#20260) +scopes(#20260,1) +scopenodes(#20258,#20260) +scopenesting(#20260,#20000) +#20261=@"var;{arguments};{#20260}" +variables(#20261,"arguments",#20260) +is_arguments_object(#20261) #20262=* -exprs(#20262,78,#20260,0,"C") -hasLocation(#20262,#20091) -enclosing_stmt(#20262,#20260) -expr_containers(#20262,#20001) -literals("C","C",#20262) -decl(#20262,#20253) -typedecl(#20262,#20254) -#20263=* -scopes(#20263,10) -scopenodes(#20260,#20263) -scopenesting(#20263,#20000) +stmts(#20262,26,#20001,1,"abstrac ... mber;\n}") +#20263=@"loc,{#10000},4,1,15,1" +locations_default(#20263,#10000,4,1,15,1) +hasLocation(#20262,#20263) +stmt_containers(#20262,#20001) +is_abstract_class(#20262) #20264=* -properties(#20264,#20260,2,0,"abstract h();") -#20265=@"loc,{#10000},6,3,6,15" -locations_default(#20265,#10000,6,3,6,15) -hasLocation(#20264,#20265) +exprs(#20264,78,#20262,0,"C") +hasLocation(#20264,#20091) +enclosing_stmt(#20264,#20262) +expr_containers(#20264,#20001) +literals("C","C",#20264) +decl(#20264,#20254) +typedecl(#20264,#20256) +#20265=* +scopes(#20265,10) +scopenodes(#20262,#20265) +scopenesting(#20265,#20000) #20266=* -exprs(#20266,0,#20264,0,"h") -hasLocation(#20266,#20097) -enclosing_stmt(#20266,#20260) -expr_containers(#20266,#20001) -literals("h","h",#20266) -#20267=* -exprs(#20267,9,#20264,1,"abstract h();") -hasLocation(#20267,#20265) -enclosing_stmt(#20267,#20260) -expr_containers(#20267,#20001) +properties(#20266,#20262,2,0,"abstract h();") +#20267=@"loc,{#10000},6,3,6,15" +locations_default(#20267,#10000,6,3,6,15) +hasLocation(#20266,#20267) #20268=* -scopes(#20268,1) -scopenodes(#20267,#20268) -scopenesting(#20268,#20263) -#20269=@"var;{arguments};{#20268}" -variables(#20269,"arguments",#20268) -is_arguments_object(#20269) -is_method(#20264) -is_abstract_member(#20264) +exprs(#20268,0,#20266,0,"h") +hasLocation(#20268,#20097) +enclosing_stmt(#20268,#20262) +expr_containers(#20268,#20001) +literals("h","h",#20268) +#20269=* +exprs(#20269,9,#20266,1,"abstract h();") +hasLocation(#20269,#20267) +enclosing_stmt(#20269,#20262) +expr_containers(#20269,#20001) #20270=* -properties(#20270,#20260,3,0,"g(x: nu ... number;") -#20271=@"loc,{#10000},9,3,9,23" -locations_default(#20271,#10000,9,3,9,23) -hasLocation(#20270,#20271) +scopes(#20270,1) +scopenodes(#20269,#20270) +scopenesting(#20270,#20265) +#20271=@"var;{arguments};{#20270}" +variables(#20271,"arguments",#20270) +is_arguments_object(#20271) +is_method(#20266) +is_abstract_member(#20266) #20272=* -exprs(#20272,0,#20270,0,"g") -hasLocation(#20272,#20105) -enclosing_stmt(#20272,#20260) -expr_containers(#20272,#20001) -literals("g","g",#20272) -#20273=* -exprs(#20273,9,#20270,1,"g(x: nu ... number;") -hasLocation(#20273,#20271) -enclosing_stmt(#20273,#20260) -expr_containers(#20273,#20001) +properties(#20272,#20262,3,0,"g(x: nu ... number;") +#20273=@"loc,{#10000},9,3,9,23" +locations_default(#20273,#10000,9,3,9,23) +hasLocation(#20272,#20273) #20274=* -scopes(#20274,1) -scopenodes(#20273,#20274) -scopenesting(#20274,#20263) -#20275=@"var;{x};{#20274}" -variables(#20275,"x",#20274) +exprs(#20274,0,#20272,0,"g") +hasLocation(#20274,#20105) +enclosing_stmt(#20274,#20262) +expr_containers(#20274,#20001) +literals("g","g",#20274) +#20275=* +exprs(#20275,9,#20272,1,"g(x: nu ... number;") +hasLocation(#20275,#20273) +enclosing_stmt(#20275,#20262) +expr_containers(#20275,#20001) #20276=* -exprs(#20276,78,#20273,0,"x") -hasLocation(#20276,#20109) -expr_containers(#20276,#20273) -literals("x","x",#20276) -decl(#20276,#20275) -#20277=@"var;{arguments};{#20274}" -variables(#20277,"arguments",#20274) -is_arguments_object(#20277) +scopes(#20276,1) +scopenodes(#20275,#20276) +scopenesting(#20276,#20265) +#20277=@"var;{x};{#20276}" +variables(#20277,"x",#20276) #20278=* -typeexprs(#20278,2,#20273,-3,"number") -hasLocation(#20278,#20119) -expr_containers(#20278,#20273) -literals("number","number",#20278) -#20279=* -typeexprs(#20279,2,#20273,-6,"number") -hasLocation(#20279,#20113) -expr_containers(#20279,#20273) -literals("number","number",#20279) -is_method(#20270) +exprs(#20278,78,#20275,0,"x") +hasLocation(#20278,#20109) +expr_containers(#20278,#20275) +literals("x","x",#20278) +decl(#20278,#20277) +#20279=@"var;{arguments};{#20276}" +variables(#20279,"arguments",#20276) +is_arguments_object(#20279) #20280=* -properties(#20280,#20260,4,0,"g(x: st ... string;") -#20281=@"loc,{#10000},10,3,10,23" -locations_default(#20281,#10000,10,3,10,23) -hasLocation(#20280,#20281) +typeexprs(#20280,2,#20275,-3,"number") +hasLocation(#20280,#20119) +expr_containers(#20280,#20275) +literals("number","number",#20280) +#20281=* +typeexprs(#20281,2,#20275,-6,"number") +hasLocation(#20281,#20113) +expr_containers(#20281,#20275) +literals("number","number",#20281) +is_method(#20272) #20282=* -exprs(#20282,0,#20280,0,"g") -hasLocation(#20282,#20123) -enclosing_stmt(#20282,#20260) -expr_containers(#20282,#20001) -literals("g","g",#20282) -#20283=* -exprs(#20283,9,#20280,1,"g(x: st ... string;") -hasLocation(#20283,#20281) -enclosing_stmt(#20283,#20260) -expr_containers(#20283,#20001) +properties(#20282,#20262,4,0,"g(x: st ... string;") +#20283=@"loc,{#10000},10,3,10,23" +locations_default(#20283,#10000,10,3,10,23) +hasLocation(#20282,#20283) #20284=* -scopes(#20284,1) -scopenodes(#20283,#20284) -scopenesting(#20284,#20263) -#20285=@"var;{x};{#20284}" -variables(#20285,"x",#20284) +exprs(#20284,0,#20282,0,"g") +hasLocation(#20284,#20123) +enclosing_stmt(#20284,#20262) +expr_containers(#20284,#20001) +literals("g","g",#20284) +#20285=* +exprs(#20285,9,#20282,1,"g(x: st ... string;") +hasLocation(#20285,#20283) +enclosing_stmt(#20285,#20262) +expr_containers(#20285,#20001) #20286=* -exprs(#20286,78,#20283,0,"x") -hasLocation(#20286,#20127) -expr_containers(#20286,#20283) -literals("x","x",#20286) -decl(#20286,#20285) -#20287=@"var;{arguments};{#20284}" -variables(#20287,"arguments",#20284) -is_arguments_object(#20287) +scopes(#20286,1) +scopenodes(#20285,#20286) +scopenesting(#20286,#20265) +#20287=@"var;{x};{#20286}" +variables(#20287,"x",#20286) #20288=* -typeexprs(#20288,2,#20283,-3,"string") -hasLocation(#20288,#20137) -expr_containers(#20288,#20283) -literals("string","string",#20288) -#20289=* -typeexprs(#20289,2,#20283,-6,"string") -hasLocation(#20289,#20131) -expr_containers(#20289,#20283) -literals("string","string",#20289) -is_method(#20280) +exprs(#20288,78,#20285,0,"x") +hasLocation(#20288,#20127) +expr_containers(#20288,#20285) +literals("x","x",#20288) +decl(#20288,#20287) +#20289=@"var;{arguments};{#20286}" +variables(#20289,"arguments",#20286) +is_arguments_object(#20289) #20290=* -properties(#20290,#20260,5,0,"g(x: any) {}") -#20291=@"loc,{#10000},11,3,11,14" -locations_default(#20291,#10000,11,3,11,14) -hasLocation(#20290,#20291) +typeexprs(#20290,2,#20285,-3,"string") +hasLocation(#20290,#20137) +expr_containers(#20290,#20285) +literals("string","string",#20290) +#20291=* +typeexprs(#20291,2,#20285,-6,"string") +hasLocation(#20291,#20131) +expr_containers(#20291,#20285) +literals("string","string",#20291) +is_method(#20282) #20292=* -exprs(#20292,0,#20290,0,"g") -hasLocation(#20292,#20141) -enclosing_stmt(#20292,#20260) -expr_containers(#20292,#20001) -literals("g","g",#20292) -#20293=* -exprs(#20293,9,#20290,1,"g(x: any) {}") -hasLocation(#20293,#20291) -enclosing_stmt(#20293,#20260) -expr_containers(#20293,#20001) +properties(#20292,#20262,5,0,"g(x: any) {}") +#20293=@"loc,{#10000},11,3,11,14" +locations_default(#20293,#10000,11,3,11,14) +hasLocation(#20292,#20293) #20294=* -scopes(#20294,1) -scopenodes(#20293,#20294) -scopenesting(#20294,#20263) -#20295=@"var;{x};{#20294}" -variables(#20295,"x",#20294) +exprs(#20294,0,#20292,0,"g") +hasLocation(#20294,#20141) +enclosing_stmt(#20294,#20262) +expr_containers(#20294,#20001) +literals("g","g",#20294) +#20295=* +exprs(#20295,9,#20292,1,"g(x: any) {}") +hasLocation(#20295,#20293) +enclosing_stmt(#20295,#20262) +expr_containers(#20295,#20001) #20296=* -exprs(#20296,78,#20293,0,"x") -hasLocation(#20296,#20145) -expr_containers(#20296,#20293) -literals("x","x",#20296) -decl(#20296,#20295) -#20297=@"var;{arguments};{#20294}" -variables(#20297,"arguments",#20294) -is_arguments_object(#20297) +scopes(#20296,1) +scopenodes(#20295,#20296) +scopenesting(#20296,#20265) +#20297=@"var;{x};{#20296}" +variables(#20297,"x",#20296) #20298=* -typeexprs(#20298,2,#20293,-6,"any") -hasLocation(#20298,#20149) -expr_containers(#20298,#20293) -literals("any","any",#20298) -#20299=* -stmts(#20299,1,#20293,-2,"{}") -#20300=@"loc,{#10000},11,13,11,14" -locations_default(#20300,#10000,11,13,11,14) -hasLocation(#20299,#20300) -stmt_containers(#20299,#20293) -is_method(#20290) +exprs(#20298,78,#20295,0,"x") +hasLocation(#20298,#20145) +expr_containers(#20298,#20295) +literals("x","x",#20298) +decl(#20298,#20297) +#20299=@"var;{arguments};{#20296}" +variables(#20299,"arguments",#20296) +is_arguments_object(#20299) +#20300=* +typeexprs(#20300,2,#20295,-6,"any") +hasLocation(#20300,#20149) +expr_containers(#20300,#20295) +literals("any","any",#20300) #20301=* -properties(#20301,#20260,6,8,"abstract x: number;") -#20302=@"loc,{#10000},14,3,14,21" -locations_default(#20302,#10000,14,3,14,21) +stmts(#20301,1,#20295,-2,"{}") +#20302=@"loc,{#10000},11,13,11,14" +locations_default(#20302,#10000,11,13,11,14) hasLocation(#20301,#20302) +stmt_containers(#20301,#20295) +is_method(#20292) #20303=* -#20304=* -exprs(#20304,0,#20301,0,"x") -hasLocation(#20304,#20159) -expr_containers(#20304,#20303) -literals("x","x",#20304) -is_abstract_member(#20301) +properties(#20303,#20262,6,8,"abstract x: number;") +#20304=@"loc,{#10000},14,3,14,21" +locations_default(#20304,#10000,14,3,14,21) +hasLocation(#20303,#20304) #20305=* -typeexprs(#20305,2,#20301,2,"number") -hasLocation(#20305,#20163) -enclosing_stmt(#20305,#20260) -expr_containers(#20305,#20001) -literals("number","number",#20305) #20306=* -properties(#20306,#20260,7,0,"constructor() {}") -#20307=@"loc,{#10000},4,18,4,17" -locations_default(#20307,#10000,4,18,4,17) -hasLocation(#20306,#20307) +exprs(#20306,0,#20303,0,"x") +hasLocation(#20306,#20159) +expr_containers(#20306,#20305) +literals("x","x",#20306) +is_abstract_member(#20303) +#20307=* +typeexprs(#20307,2,#20303,2,"number") +hasLocation(#20307,#20163) +enclosing_stmt(#20307,#20262) +expr_containers(#20307,#20001) +literals("number","number",#20307) #20308=* -exprs(#20308,0,#20306,0,"constructor") -hasLocation(#20308,#20307) -enclosing_stmt(#20308,#20260) -expr_containers(#20308,#20001) -literals("constructor","constructor",#20308) -exprs(#20303,9,#20306,1,"() {}") -hasLocation(#20303,#20307) -enclosing_stmt(#20303,#20260) -expr_containers(#20303,#20001) -#20309=* -scopes(#20309,1) -scopenodes(#20303,#20309) -scopenesting(#20309,#20263) -#20310=@"var;{arguments};{#20309}" -variables(#20310,"arguments",#20309) -is_arguments_object(#20310) +properties(#20308,#20262,7,0,"constructor() {}") +#20309=@"loc,{#10000},4,18,4,17" +locations_default(#20309,#10000,4,18,4,17) +hasLocation(#20308,#20309) +#20310=* +exprs(#20310,0,#20308,0,"constructor") +hasLocation(#20310,#20309) +enclosing_stmt(#20310,#20262) +expr_containers(#20310,#20001) +literals("constructor","constructor",#20310) +exprs(#20305,9,#20308,1,"() {}") +hasLocation(#20305,#20309) +enclosing_stmt(#20305,#20262) +expr_containers(#20305,#20001) #20311=* -stmts(#20311,1,#20303,-2,"{}") -hasLocation(#20311,#20307) -stmt_containers(#20311,#20303) -is_method(#20306) -#20312=* -stmts(#20312,26,#20001,2,"declare ... mber;\n}") -#20313=@"loc,{#10000},18,1,29,1" -locations_default(#20313,#10000,18,1,29,1) -hasLocation(#20312,#20313) -stmt_containers(#20312,#20001) -has_declare_keyword(#20312) -is_abstract_class(#20312) +scopes(#20311,1) +scopenodes(#20305,#20311) +scopenesting(#20311,#20265) +#20312=@"var;{arguments};{#20311}" +variables(#20312,"arguments",#20311) +is_arguments_object(#20312) +#20313=* +stmts(#20313,1,#20305,-2,"{}") +hasLocation(#20313,#20309) +stmt_containers(#20313,#20305) +is_method(#20308) #20314=* -exprs(#20314,78,#20312,0,"D") -hasLocation(#20314,#20174) -enclosing_stmt(#20314,#20312) -expr_containers(#20314,#20001) -literals("D","D",#20314) -#20315=@"var;{D};{#20000}" -variables(#20315,"D",#20000) -decl(#20314,#20315) +stmts(#20314,26,#20001,2,"declare ... mber;\n}") +#20315=@"loc,{#10000},18,1,29,1" +locations_default(#20315,#10000,18,1,29,1) +hasLocation(#20314,#20315) +stmt_containers(#20314,#20001) +has_declare_keyword(#20314) +is_abstract_class(#20314) #20316=* -scopes(#20316,10) -scopenodes(#20312,#20316) -scopenesting(#20316,#20000) +exprs(#20316,78,#20314,0,"D") +hasLocation(#20316,#20174) +enclosing_stmt(#20316,#20314) +expr_containers(#20316,#20001) +literals("D","D",#20316) +decl(#20316,#20255) +typedecl(#20316,#20257) #20317=* -properties(#20317,#20312,2,0,"abstract h();") -#20318=@"loc,{#10000},20,3,20,15" -locations_default(#20318,#10000,20,3,20,15) -hasLocation(#20317,#20318) -#20319=* -exprs(#20319,0,#20317,0,"h") -hasLocation(#20319,#20180) -enclosing_stmt(#20319,#20312) -expr_containers(#20319,#20001) -literals("h","h",#20319) +scopes(#20317,10) +scopenodes(#20314,#20317) +scopenesting(#20317,#20000) +#20318=* +properties(#20318,#20314,2,0,"abstract h();") +#20319=@"loc,{#10000},20,3,20,15" +locations_default(#20319,#10000,20,3,20,15) +hasLocation(#20318,#20319) #20320=* -exprs(#20320,9,#20317,1,"abstract h();") -hasLocation(#20320,#20318) -enclosing_stmt(#20320,#20312) +exprs(#20320,0,#20318,0,"h") +hasLocation(#20320,#20180) +enclosing_stmt(#20320,#20314) expr_containers(#20320,#20001) +literals("h","h",#20320) #20321=* -scopes(#20321,1) -scopenodes(#20320,#20321) -scopenesting(#20321,#20316) -#20322=@"var;{arguments};{#20321}" -variables(#20322,"arguments",#20321) -is_arguments_object(#20322) -is_method(#20317) -is_abstract_member(#20317) -#20323=* -properties(#20323,#20312,3,0,"g(x: nu ... number;") -#20324=@"loc,{#10000},23,3,23,23" -locations_default(#20324,#10000,23,3,23,23) -hasLocation(#20323,#20324) -#20325=* -exprs(#20325,0,#20323,0,"g") -hasLocation(#20325,#20188) -enclosing_stmt(#20325,#20312) -expr_containers(#20325,#20001) -literals("g","g",#20325) +exprs(#20321,9,#20318,1,"abstract h();") +hasLocation(#20321,#20319) +enclosing_stmt(#20321,#20314) +expr_containers(#20321,#20001) +#20322=* +scopes(#20322,1) +scopenodes(#20321,#20322) +scopenesting(#20322,#20317) +#20323=@"var;{arguments};{#20322}" +variables(#20323,"arguments",#20322) +is_arguments_object(#20323) +is_method(#20318) +is_abstract_member(#20318) +#20324=* +properties(#20324,#20314,3,0,"g(x: nu ... number;") +#20325=@"loc,{#10000},23,3,23,23" +locations_default(#20325,#10000,23,3,23,23) +hasLocation(#20324,#20325) #20326=* -exprs(#20326,9,#20323,1,"g(x: nu ... number;") -hasLocation(#20326,#20324) -enclosing_stmt(#20326,#20312) +exprs(#20326,0,#20324,0,"g") +hasLocation(#20326,#20188) +enclosing_stmt(#20326,#20314) expr_containers(#20326,#20001) +literals("g","g",#20326) #20327=* -scopes(#20327,1) -scopenodes(#20326,#20327) -scopenesting(#20327,#20316) -#20328=@"var;{x};{#20327}" -variables(#20328,"x",#20327) -#20329=* -exprs(#20329,78,#20326,0,"x") -hasLocation(#20329,#20192) -expr_containers(#20329,#20326) -literals("x","x",#20329) -decl(#20329,#20328) -#20330=@"var;{arguments};{#20327}" -variables(#20330,"arguments",#20327) -is_arguments_object(#20330) -#20331=* -typeexprs(#20331,2,#20326,-3,"number") -hasLocation(#20331,#20202) -expr_containers(#20331,#20326) -literals("number","number",#20331) +exprs(#20327,9,#20324,1,"g(x: nu ... number;") +hasLocation(#20327,#20325) +enclosing_stmt(#20327,#20314) +expr_containers(#20327,#20001) +#20328=* +scopes(#20328,1) +scopenodes(#20327,#20328) +scopenesting(#20328,#20317) +#20329=@"var;{x};{#20328}" +variables(#20329,"x",#20328) +#20330=* +exprs(#20330,78,#20327,0,"x") +hasLocation(#20330,#20192) +expr_containers(#20330,#20327) +literals("x","x",#20330) +decl(#20330,#20329) +#20331=@"var;{arguments};{#20328}" +variables(#20331,"arguments",#20328) +is_arguments_object(#20331) #20332=* -typeexprs(#20332,2,#20326,-6,"number") -hasLocation(#20332,#20196) -expr_containers(#20332,#20326) +typeexprs(#20332,2,#20327,-3,"number") +hasLocation(#20332,#20202) +expr_containers(#20332,#20327) literals("number","number",#20332) -is_method(#20323) #20333=* -properties(#20333,#20312,4,0,"g(x: st ... string;") -#20334=@"loc,{#10000},24,3,24,23" -locations_default(#20334,#10000,24,3,24,23) -hasLocation(#20333,#20334) -#20335=* -exprs(#20335,0,#20333,0,"g") -hasLocation(#20335,#20206) -enclosing_stmt(#20335,#20312) -expr_containers(#20335,#20001) -literals("g","g",#20335) +typeexprs(#20333,2,#20327,-6,"number") +hasLocation(#20333,#20196) +expr_containers(#20333,#20327) +literals("number","number",#20333) +is_method(#20324) +#20334=* +properties(#20334,#20314,4,0,"g(x: st ... string;") +#20335=@"loc,{#10000},24,3,24,23" +locations_default(#20335,#10000,24,3,24,23) +hasLocation(#20334,#20335) #20336=* -exprs(#20336,9,#20333,1,"g(x: st ... string;") -hasLocation(#20336,#20334) -enclosing_stmt(#20336,#20312) +exprs(#20336,0,#20334,0,"g") +hasLocation(#20336,#20206) +enclosing_stmt(#20336,#20314) expr_containers(#20336,#20001) +literals("g","g",#20336) #20337=* -scopes(#20337,1) -scopenodes(#20336,#20337) -scopenesting(#20337,#20316) -#20338=@"var;{x};{#20337}" -variables(#20338,"x",#20337) -#20339=* -exprs(#20339,78,#20336,0,"x") -hasLocation(#20339,#20210) -expr_containers(#20339,#20336) -literals("x","x",#20339) -decl(#20339,#20338) -#20340=@"var;{arguments};{#20337}" -variables(#20340,"arguments",#20337) -is_arguments_object(#20340) -#20341=* -typeexprs(#20341,2,#20336,-3,"string") -hasLocation(#20341,#20220) -expr_containers(#20341,#20336) -literals("string","string",#20341) +exprs(#20337,9,#20334,1,"g(x: st ... string;") +hasLocation(#20337,#20335) +enclosing_stmt(#20337,#20314) +expr_containers(#20337,#20001) +#20338=* +scopes(#20338,1) +scopenodes(#20337,#20338) +scopenesting(#20338,#20317) +#20339=@"var;{x};{#20338}" +variables(#20339,"x",#20338) +#20340=* +exprs(#20340,78,#20337,0,"x") +hasLocation(#20340,#20210) +expr_containers(#20340,#20337) +literals("x","x",#20340) +decl(#20340,#20339) +#20341=@"var;{arguments};{#20338}" +variables(#20341,"arguments",#20338) +is_arguments_object(#20341) #20342=* -typeexprs(#20342,2,#20336,-6,"string") -hasLocation(#20342,#20214) -expr_containers(#20342,#20336) +typeexprs(#20342,2,#20337,-3,"string") +hasLocation(#20342,#20220) +expr_containers(#20342,#20337) literals("string","string",#20342) -is_method(#20333) #20343=* -properties(#20343,#20312,5,0,"g(x: any) {}") -#20344=@"loc,{#10000},25,3,25,14" -locations_default(#20344,#10000,25,3,25,14) -hasLocation(#20343,#20344) -#20345=* -exprs(#20345,0,#20343,0,"g") -hasLocation(#20345,#20224) -enclosing_stmt(#20345,#20312) -expr_containers(#20345,#20001) -literals("g","g",#20345) +typeexprs(#20343,2,#20337,-6,"string") +hasLocation(#20343,#20214) +expr_containers(#20343,#20337) +literals("string","string",#20343) +is_method(#20334) +#20344=* +properties(#20344,#20314,5,0,"g(x: any) {}") +#20345=@"loc,{#10000},25,3,25,14" +locations_default(#20345,#10000,25,3,25,14) +hasLocation(#20344,#20345) #20346=* -exprs(#20346,9,#20343,1,"g(x: any) {}") -hasLocation(#20346,#20344) -enclosing_stmt(#20346,#20312) +exprs(#20346,0,#20344,0,"g") +hasLocation(#20346,#20224) +enclosing_stmt(#20346,#20314) expr_containers(#20346,#20001) +literals("g","g",#20346) #20347=* -scopes(#20347,1) -scopenodes(#20346,#20347) -scopenesting(#20347,#20316) -#20348=@"var;{x};{#20347}" -variables(#20348,"x",#20347) -#20349=* -exprs(#20349,78,#20346,0,"x") -hasLocation(#20349,#20228) -expr_containers(#20349,#20346) -literals("x","x",#20349) -decl(#20349,#20348) -#20350=@"var;{arguments};{#20347}" -variables(#20350,"arguments",#20347) -is_arguments_object(#20350) -#20351=* -typeexprs(#20351,2,#20346,-6,"any") -hasLocation(#20351,#20232) -expr_containers(#20351,#20346) -literals("any","any",#20351) +exprs(#20347,9,#20344,1,"g(x: any) {}") +hasLocation(#20347,#20345) +enclosing_stmt(#20347,#20314) +expr_containers(#20347,#20001) +#20348=* +scopes(#20348,1) +scopenodes(#20347,#20348) +scopenesting(#20348,#20317) +#20349=@"var;{x};{#20348}" +variables(#20349,"x",#20348) +#20350=* +exprs(#20350,78,#20347,0,"x") +hasLocation(#20350,#20228) +expr_containers(#20350,#20347) +literals("x","x",#20350) +decl(#20350,#20349) +#20351=@"var;{arguments};{#20348}" +variables(#20351,"arguments",#20348) +is_arguments_object(#20351) #20352=* -stmts(#20352,1,#20346,-2,"{}") -#20353=@"loc,{#10000},25,13,25,14" -locations_default(#20353,#10000,25,13,25,14) -hasLocation(#20352,#20353) -stmt_containers(#20352,#20346) -is_method(#20343) -#20354=* -properties(#20354,#20312,6,8,"abstract x: number;") -#20355=@"loc,{#10000},28,3,28,21" -locations_default(#20355,#10000,28,3,28,21) -hasLocation(#20354,#20355) -#20356=* +typeexprs(#20352,2,#20347,-6,"any") +hasLocation(#20352,#20232) +expr_containers(#20352,#20347) +literals("any","any",#20352) +#20353=* +stmts(#20353,1,#20347,-2,"{}") +#20354=@"loc,{#10000},25,13,25,14" +locations_default(#20354,#10000,25,13,25,14) +hasLocation(#20353,#20354) +stmt_containers(#20353,#20347) +is_method(#20344) +#20355=* +properties(#20355,#20314,6,8,"abstract x: number;") +#20356=@"loc,{#10000},28,3,28,21" +locations_default(#20356,#10000,28,3,28,21) +hasLocation(#20355,#20356) #20357=* -exprs(#20357,0,#20354,0,"x") -hasLocation(#20357,#20242) -expr_containers(#20357,#20356) -literals("x","x",#20357) -is_abstract_member(#20354) #20358=* -typeexprs(#20358,2,#20354,2,"number") -hasLocation(#20358,#20246) -enclosing_stmt(#20358,#20312) -expr_containers(#20358,#20001) -literals("number","number",#20358) +exprs(#20358,0,#20355,0,"x") +hasLocation(#20358,#20242) +expr_containers(#20358,#20357) +literals("x","x",#20358) +is_abstract_member(#20355) #20359=* -properties(#20359,#20312,7,0,"constructor() {}") -#20360=@"loc,{#10000},18,26,18,25" -locations_default(#20360,#10000,18,26,18,25) -hasLocation(#20359,#20360) -#20361=* -exprs(#20361,0,#20359,0,"constructor") -hasLocation(#20361,#20360) -enclosing_stmt(#20361,#20312) -expr_containers(#20361,#20001) -literals("constructor","constructor",#20361) -exprs(#20356,9,#20359,1,"() {}") -hasLocation(#20356,#20360) -enclosing_stmt(#20356,#20312) -expr_containers(#20356,#20001) +typeexprs(#20359,2,#20355,2,"number") +hasLocation(#20359,#20246) +enclosing_stmt(#20359,#20314) +expr_containers(#20359,#20001) +literals("number","number",#20359) +#20360=* +properties(#20360,#20314,7,0,"constructor() {}") +#20361=@"loc,{#10000},18,26,18,25" +locations_default(#20361,#10000,18,26,18,25) +hasLocation(#20360,#20361) #20362=* -scopes(#20362,1) -scopenodes(#20356,#20362) -scopenesting(#20362,#20316) -#20363=@"var;{arguments};{#20362}" -variables(#20363,"arguments",#20362) -is_arguments_object(#20363) -#20364=* -stmts(#20364,1,#20356,-2,"{}") -hasLocation(#20364,#20360) -stmt_containers(#20364,#20356) -is_method(#20359) +exprs(#20362,0,#20360,0,"constructor") +hasLocation(#20362,#20361) +enclosing_stmt(#20362,#20314) +expr_containers(#20362,#20001) +literals("constructor","constructor",#20362) +exprs(#20357,9,#20360,1,"() {}") +hasLocation(#20357,#20361) +enclosing_stmt(#20357,#20314) +expr_containers(#20357,#20001) +#20363=* +scopes(#20363,1) +scopenodes(#20357,#20363) +scopenesting(#20363,#20317) +#20364=@"var;{arguments};{#20363}" +variables(#20364,"arguments",#20363) +is_arguments_object(#20364) #20365=* -entry_cfg_node(#20365,#20001) -#20366=@"loc,{#10000},2,1,2,0" -locations_default(#20366,#10000,2,1,2,0) -hasLocation(#20365,#20366) -#20367=* -exit_cfg_node(#20367,#20001) -hasLocation(#20367,#20251) -successor(#20312,#20367) -successor(#20303,#20306) +stmts(#20365,1,#20357,-2,"{}") +hasLocation(#20365,#20361) +stmt_containers(#20365,#20357) +is_method(#20360) +#20366=* +entry_cfg_node(#20366,#20001) +#20367=@"loc,{#10000},2,1,2,0" +locations_default(#20367,#10000,2,1,2,0) +hasLocation(#20366,#20367) #20368=* -entry_cfg_node(#20368,#20303) -hasLocation(#20368,#20307) +exit_cfg_node(#20368,#20001) +hasLocation(#20368,#20251) +successor(#20314,#20368) +successor(#20305,#20308) #20369=* -exit_cfg_node(#20369,#20303) -hasLocation(#20369,#20307) -successor(#20311,#20369) -successor(#20368,#20311) -successor(#20308,#20303) -successor(#20306,#20260) -successor(#20293,#20290) +entry_cfg_node(#20369,#20305) +hasLocation(#20369,#20309) #20370=* -entry_cfg_node(#20370,#20293) -#20371=@"loc,{#10000},11,3,11,2" -locations_default(#20371,#10000,11,3,11,2) -hasLocation(#20370,#20371) -#20372=* -exit_cfg_node(#20372,#20293) -#20373=@"loc,{#10000},11,15,11,14" -locations_default(#20373,#10000,11,15,11,14) -hasLocation(#20372,#20373) -successor(#20299,#20372) -successor(#20296,#20299) -successor(#20370,#20296) -successor(#20292,#20293) -successor(#20290,#20308) -successor(#20280,#20292) -successor(#20270,#20280) -successor(#20264,#20270) -successor(#20262,#20264) -successor(#20260,#20312) -successor(#20255,#20262) -successor(#20365,#20255) +exit_cfg_node(#20370,#20305) +hasLocation(#20370,#20309) +successor(#20313,#20370) +successor(#20369,#20313) +successor(#20310,#20305) +successor(#20308,#20262) +successor(#20295,#20292) +#20371=* +entry_cfg_node(#20371,#20295) +#20372=@"loc,{#10000},11,3,11,2" +locations_default(#20372,#10000,11,3,11,2) +hasLocation(#20371,#20372) +#20373=* +exit_cfg_node(#20373,#20295) +#20374=@"loc,{#10000},11,15,11,14" +locations_default(#20374,#10000,11,15,11,14) +hasLocation(#20373,#20374) +successor(#20301,#20373) +successor(#20298,#20301) +successor(#20371,#20298) +successor(#20294,#20295) +successor(#20292,#20310) +successor(#20282,#20294) +successor(#20272,#20282) +successor(#20266,#20272) +successor(#20264,#20266) +successor(#20262,#20314) +successor(#20258,#20264) +successor(#20366,#20258) numlines(#10000,29,15,8) filetype(#10000,"typescript") diff --git a/javascript/extractor/tests/ts/output/trap/thisparameter.ts.trap b/javascript/extractor/tests/ts/output/trap/thisparameter.ts.trap index 0e766cf77b99..27b075f05c76 100644 --- a/javascript/extractor/tests/ts/output/trap/thisparameter.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/thisparameter.ts.trap @@ -425,146 +425,146 @@ hasLocation(#20001,#20158) variables(#20159,"declaration",#20000) #20160=@"var;{f};{#20000}" variables(#20160,"f",#20000) -#20161=@"var;{C};{#20000}" -variables(#20161,"C",#20000) -#20162=@"local_type_name;{C};{#20000}" -local_type_names(#20162,"C",#20000) -#20163=@"local_type_name;{I};{#20000}" -local_type_names(#20163,"I",#20000) -#20164=* -stmts(#20164,17,#20001,0,"functio ... ber) {}") -hasLocation(#20164,#20003) -stmt_containers(#20164,#20001) +#20161=@"var;{ambient};{#20000}" +variables(#20161,"ambient",#20000) +#20162=@"var;{C};{#20000}" +variables(#20162,"C",#20000) +#20163=@"local_type_name;{C};{#20000}" +local_type_names(#20163,"C",#20000) +#20164=@"local_type_name;{I};{#20000}" +local_type_names(#20164,"I",#20000) #20165=* -exprs(#20165,78,#20164,-1,"declaration") -hasLocation(#20165,#20033) -expr_containers(#20165,#20164) -literals("declaration","declaration",#20165) -decl(#20165,#20159) +stmts(#20165,17,#20001,0,"functio ... ber) {}") +hasLocation(#20165,#20003) +stmt_containers(#20165,#20001) #20166=* -scopes(#20166,1) -scopenodes(#20164,#20166) -scopenesting(#20166,#20000) -#20167=@"var;{x};{#20166}" -variables(#20167,"x",#20166) -#20168=* -exprs(#20168,78,#20164,0,"x") -hasLocation(#20168,#20045) -expr_containers(#20168,#20164) -literals("x","x",#20168) -decl(#20168,#20167) -#20169=@"var;{arguments};{#20166}" -variables(#20169,"arguments",#20166) -is_arguments_object(#20169) -#20170=* -typeexprs(#20170,2,#20164,-4,"void") -hasLocation(#20170,#20041) -expr_containers(#20170,#20164) -literals("void","void",#20170) +exprs(#20166,78,#20165,-1,"declaration") +hasLocation(#20166,#20033) +expr_containers(#20166,#20165) +literals("declaration","declaration",#20166) +decl(#20166,#20159) +#20167=* +scopes(#20167,1) +scopenodes(#20165,#20167) +scopenesting(#20167,#20000) +#20168=@"var;{x};{#20167}" +variables(#20168,"x",#20167) +#20169=* +exprs(#20169,78,#20165,0,"x") +hasLocation(#20169,#20045) +expr_containers(#20169,#20165) +literals("x","x",#20169) +decl(#20169,#20168) +#20170=@"var;{arguments};{#20167}" +variables(#20170,"arguments",#20167) +is_arguments_object(#20170) #20171=* -typeexprs(#20171,2,#20164,-6,"number") -hasLocation(#20171,#20049) -expr_containers(#20171,#20164) -literals("number","number",#20171) +typeexprs(#20171,2,#20165,-4,"void") +hasLocation(#20171,#20041) +expr_containers(#20171,#20165) +literals("void","void",#20171) #20172=* -stmts(#20172,1,#20164,-2,"{}") -#20173=@"loc,{#10000},1,45,1,46" -locations_default(#20173,#10000,1,45,1,46) -hasLocation(#20172,#20173) -stmt_containers(#20172,#20164) -#20174=* -stmts(#20174,18,#20001,1,"var f = ... ber) {}") -hasLocation(#20174,#20007) -stmt_containers(#20174,#20001) +typeexprs(#20172,2,#20165,-6,"number") +hasLocation(#20172,#20049) +expr_containers(#20172,#20165) +literals("number","number",#20172) +#20173=* +stmts(#20173,1,#20165,-2,"{}") +#20174=@"loc,{#10000},1,45,1,46" +locations_default(#20174,#10000,1,45,1,46) +hasLocation(#20173,#20174) +stmt_containers(#20173,#20165) #20175=* -exprs(#20175,64,#20174,0,"f = fun ... ber) {}") -#20176=@"loc,{#10000},3,5,3,44" -locations_default(#20176,#10000,3,5,3,44) -hasLocation(#20175,#20176) -enclosing_stmt(#20175,#20174) -expr_containers(#20175,#20001) -#20177=* -exprs(#20177,78,#20175,0,"f") -hasLocation(#20177,#20059) -enclosing_stmt(#20177,#20174) -expr_containers(#20177,#20001) -literals("f","f",#20177) -decl(#20177,#20160) +stmts(#20175,18,#20001,1,"var f = ... ber) {}") +hasLocation(#20175,#20007) +stmt_containers(#20175,#20001) +#20176=* +exprs(#20176,64,#20175,0,"f = fun ... ber) {}") +#20177=@"loc,{#10000},3,5,3,44" +locations_default(#20177,#10000,3,5,3,44) +hasLocation(#20176,#20177) +enclosing_stmt(#20176,#20175) +expr_containers(#20176,#20001) #20178=* -exprs(#20178,9,#20175,1,"functio ... ber) {}") -#20179=@"loc,{#10000},3,9,3,44" -locations_default(#20179,#10000,3,9,3,44) -hasLocation(#20178,#20179) -enclosing_stmt(#20178,#20174) +exprs(#20178,78,#20176,0,"f") +hasLocation(#20178,#20059) +enclosing_stmt(#20178,#20175) expr_containers(#20178,#20001) -#20180=* -scopes(#20180,1) -scopenodes(#20178,#20180) -scopenesting(#20180,#20000) -#20181=@"var;{x};{#20180}" -variables(#20181,"x",#20180) -#20182=* -exprs(#20182,78,#20178,0,"x") -hasLocation(#20182,#20075) -expr_containers(#20182,#20178) -literals("x","x",#20182) -decl(#20182,#20181) -#20183=@"var;{arguments};{#20180}" -variables(#20183,"arguments",#20180) -is_arguments_object(#20183) -#20184=* -typeexprs(#20184,2,#20178,-4,"string") -hasLocation(#20184,#20071) -expr_containers(#20184,#20178) -literals("string","string",#20184) +literals("f","f",#20178) +decl(#20178,#20160) +#20179=* +exprs(#20179,9,#20176,1,"functio ... ber) {}") +#20180=@"loc,{#10000},3,9,3,44" +locations_default(#20180,#10000,3,9,3,44) +hasLocation(#20179,#20180) +enclosing_stmt(#20179,#20175) +expr_containers(#20179,#20001) +#20181=* +scopes(#20181,1) +scopenodes(#20179,#20181) +scopenesting(#20181,#20000) +#20182=@"var;{x};{#20181}" +variables(#20182,"x",#20181) +#20183=* +exprs(#20183,78,#20179,0,"x") +hasLocation(#20183,#20075) +expr_containers(#20183,#20179) +literals("x","x",#20183) +decl(#20183,#20182) +#20184=@"var;{arguments};{#20181}" +variables(#20184,"arguments",#20181) +is_arguments_object(#20184) #20185=* -typeexprs(#20185,2,#20178,-6,"number") -hasLocation(#20185,#20079) -expr_containers(#20185,#20178) -literals("number","number",#20185) +typeexprs(#20185,2,#20179,-4,"string") +hasLocation(#20185,#20071) +expr_containers(#20185,#20179) +literals("string","string",#20185) #20186=* -stmts(#20186,1,#20178,-2,"{}") -#20187=@"loc,{#10000},3,43,3,44" -locations_default(#20187,#10000,3,43,3,44) -hasLocation(#20186,#20187) -stmt_containers(#20186,#20178) -#20188=* -stmts(#20188,17,#20001,2,"declare ... umber);") -hasLocation(#20188,#20011) -stmt_containers(#20188,#20001) -has_declare_keyword(#20188) +typeexprs(#20186,2,#20179,-6,"number") +hasLocation(#20186,#20079) +expr_containers(#20186,#20179) +literals("number","number",#20186) +#20187=* +stmts(#20187,1,#20179,-2,"{}") +#20188=@"loc,{#10000},3,43,3,44" +locations_default(#20188,#10000,3,43,3,44) +hasLocation(#20187,#20188) +stmt_containers(#20187,#20179) #20189=* -exprs(#20189,78,#20188,-1,"ambient") -hasLocation(#20189,#20091) -expr_containers(#20189,#20188) -literals("ambient","ambient",#20189) -#20190=@"var;{ambient};{#20000}" -variables(#20190,"ambient",#20000) -decl(#20189,#20190) +stmts(#20189,17,#20001,2,"declare ... umber);") +hasLocation(#20189,#20011) +stmt_containers(#20189,#20001) +has_declare_keyword(#20189) +#20190=* +exprs(#20190,78,#20189,-1,"ambient") +hasLocation(#20190,#20091) +expr_containers(#20190,#20189) +literals("ambient","ambient",#20190) +decl(#20190,#20161) #20191=* scopes(#20191,1) -scopenodes(#20188,#20191) +scopenodes(#20189,#20191) scopenesting(#20191,#20000) #20192=@"var;{x};{#20191}" variables(#20192,"x",#20191) #20193=* -exprs(#20193,78,#20188,0,"x") +exprs(#20193,78,#20189,0,"x") hasLocation(#20193,#20103) -expr_containers(#20193,#20188) +expr_containers(#20193,#20189) literals("x","x",#20193) decl(#20193,#20192) #20194=@"var;{arguments};{#20191}" variables(#20194,"arguments",#20191) is_arguments_object(#20194) #20195=* -typeexprs(#20195,2,#20188,-4,"string") +typeexprs(#20195,2,#20189,-4,"string") hasLocation(#20195,#20099) -expr_containers(#20195,#20188) +expr_containers(#20195,#20189) literals("string","string",#20195) #20196=* -typeexprs(#20196,2,#20188,-6,"number") +typeexprs(#20196,2,#20189,-6,"number") hasLocation(#20196,#20107) -expr_containers(#20196,#20188) +expr_containers(#20196,#20189) literals("number","number",#20196) #20197=* stmts(#20197,26,#20001,3,"class C ... C) {}\n}") @@ -578,8 +578,8 @@ hasLocation(#20199,#20115) enclosing_stmt(#20199,#20197) expr_containers(#20199,#20001) literals("C","C",#20199) -decl(#20199,#20161) -typedecl(#20199,#20162) +decl(#20199,#20162) +typedecl(#20199,#20163) #20200=* scopes(#20200,10) scopenodes(#20197,#20200) @@ -612,7 +612,7 @@ typeexprs(#20207,0,#20204,-4,"C") hasLocation(#20207,#20127) expr_containers(#20207,#20204) literals("C","C",#20207) -typebind(#20207,#20162) +typebind(#20207,#20163) #20208=* stmts(#20208,1,#20204,-2,"{}") #20209=@"loc,{#10000},8,19,8,20" @@ -660,7 +660,7 @@ hasLocation(#20219,#20138) enclosing_stmt(#20219,#20217) expr_containers(#20219,#20001) literals("I","I",#20219) -typedecl(#20219,#20163) +typedecl(#20219,#20164) #20220=* properties(#20220,#20217,2,0,"method(this: I);") #20221=@"loc,{#10000},12,3,12,18" @@ -689,7 +689,7 @@ typeexprs(#20226,0,#20223,-4,"I") hasLocation(#20226,#20150) expr_containers(#20226,#20223) literals("I","I",#20226) -typebind(#20226,#20163) +typebind(#20226,#20164) is_method(#20220) is_abstract_member(#20220) #20227=* @@ -729,37 +729,37 @@ successor(#20203,#20204) successor(#20201,#20212) successor(#20199,#20203) successor(#20197,#20217) -successor(#20188,#20199) -successor(#20174,#20177) -successor(#20178,#20175) +successor(#20189,#20199) +successor(#20175,#20178) +successor(#20179,#20176) #20236=* -entry_cfg_node(#20236,#20178) +entry_cfg_node(#20236,#20179) #20237=@"loc,{#10000},3,9,3,8" locations_default(#20237,#10000,3,9,3,8) hasLocation(#20236,#20237) #20238=* -exit_cfg_node(#20238,#20178) +exit_cfg_node(#20238,#20179) #20239=@"loc,{#10000},3,45,3,44" locations_default(#20239,#10000,3,45,3,44) hasLocation(#20238,#20239) -successor(#20186,#20238) -successor(#20182,#20186) -successor(#20236,#20182) -successor(#20177,#20178) -successor(#20175,#20188) -successor(#20164,#20174) +successor(#20187,#20238) +successor(#20183,#20187) +successor(#20236,#20183) +successor(#20178,#20179) +successor(#20176,#20189) +successor(#20165,#20175) #20240=* -entry_cfg_node(#20240,#20164) +entry_cfg_node(#20240,#20165) hasLocation(#20240,#20228) #20241=* -exit_cfg_node(#20241,#20164) +exit_cfg_node(#20241,#20165) #20242=@"loc,{#10000},1,47,1,46" locations_default(#20242,#10000,1,47,1,46) hasLocation(#20241,#20242) -successor(#20172,#20241) -successor(#20168,#20172) -successor(#20240,#20168) -successor(#20165,#20164) -successor(#20227,#20165) +successor(#20173,#20241) +successor(#20169,#20173) +successor(#20240,#20169) +successor(#20166,#20165) +successor(#20227,#20166) numlines(#10000,14,9,0) filetype(#10000,"typescript") From b698b4e5e26b65341033df48aef947c8fb1e802b Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 20 May 2025 12:55:41 +0200 Subject: [PATCH 36/47] JS: Add test for missing type flow through generics --- .../library-tests/UnderlyingTypes/generics.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/generics.ts diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/generics.ts b/javascript/ql/test/library-tests/UnderlyingTypes/generics.ts new file mode 100644 index 000000000000..26e4499f4da2 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/generics.ts @@ -0,0 +1,46 @@ +import * as express from 'express'; + +type Box1 = { + value: T; + other: string; +}; +function t1(b: Box1) { + b.value; // $ MISSING: hasUnderlyingType='express'.Request + b.other; +} + +interface Box2 { + value: T; + other: string; +} +function t2(b: Box2) { + b.value; // $ MISSING: hasUnderlyingType='express'.Request + b.other; +} + +class Box3 { + value: T; + other: string; +} +function t3(b: Box3) { + b.value; // $ MISSING: hasUnderlyingType='express'.Request + b.other; +} + +abstract class Box4 { + abstract getValue(): T; + abstract getOther(): string; +} +function t4(b: Box4) { + b.getValue(); // $ MISSING: hasUnderlyingType='express'.Request + b.getOther(); +} + +type Box5 = { + value: T & { blah: string }; + other: string; +}; +function t5(b: Box5) { + b.value; // $ MISSING: hasUnderlyingType='express'.Request + b.other; +} From d644f8092190973a4c68279f3229c7d52808e156 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 20 May 2025 15:56:50 +0200 Subject: [PATCH 37/47] JS: Remove obsolete meta query --- .../query-suite/not_included_in_qls.expected | 1 - .../ql/src/meta/types/TypesWithQualifiedName.ql | 14 -------------- 2 files changed, 15 deletions(-) delete mode 100644 javascript/ql/src/meta/types/TypesWithQualifiedName.ql diff --git a/javascript/ql/integration-tests/query-suite/not_included_in_qls.expected b/javascript/ql/integration-tests/query-suite/not_included_in_qls.expected index a6c808c6cbfb..aa1d8b2c81de 100644 --- a/javascript/ql/integration-tests/query-suite/not_included_in_qls.expected +++ b/javascript/ql/integration-tests/query-suite/not_included_in_qls.expected @@ -145,4 +145,3 @@ 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 -ql/javascript/ql/src/meta/types/TypesWithQualifiedName.ql diff --git a/javascript/ql/src/meta/types/TypesWithQualifiedName.ql b/javascript/ql/src/meta/types/TypesWithQualifiedName.ql deleted file mode 100644 index db23d2a807ef..000000000000 --- a/javascript/ql/src/meta/types/TypesWithQualifiedName.ql +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @name Types with qualified name - * @description The number of type annotations with a qualified name - * @kind metric - * @metricType project - * @metricAggregate sum - * @tags meta - * @id js/meta/types-with-qualified-name - */ - -import javascript -import meta.MetaMetrics - -select projectRoot(), count(TypeAnnotation t | t.hasQualifiedName(_) or t.hasQualifiedName(_, _)) From 853ba492124daa36300a054f45b7769a6ac03d16 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 4 Jun 2025 10:17:25 +0200 Subject: [PATCH 38/47] Update javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll Co-authored-by: Napalys Klicius --- .../ql/lib/semmle/javascript/internal/TypeResolution.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll index cb575f20ab14..a1d0ab43a6fb 100644 --- a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll @@ -351,6 +351,10 @@ module TypeResolution { or node.(TypeExpr).isNever() or + node.(TypeExpr).isBigInt() + or + node.(TypeExpr).isSymbol() + or node instanceof LiteralTypeExpr or node = any(EnumMember m).getIdentifier() // enum members are constant From 79101fd1210aae83ce0ed97d39feeb7f0c5432a7 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 4 Jun 2025 22:07:03 +0200 Subject: [PATCH 39/47] JS: Add test with type casts --- .../ql/test/library-tests/UnderlyingTypes/test.expected | 2 ++ .../ql/test/library-tests/UnderlyingTypes/typeCast.ts | 7 +++++++ 2 files changed, 9 insertions(+) create mode 100644 javascript/ql/test/library-tests/UnderlyingTypes/typeCast.ts diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/test.expected b/javascript/ql/test/library-tests/UnderlyingTypes/test.expected index 5ed9f42a7a53..be3c188b7088 100644 --- a/javascript/ql/test/library-tests/UnderlyingTypes/test.expected +++ b/javascript/ql/test/library-tests/UnderlyingTypes/test.expected @@ -48,4 +48,6 @@ | subtype.ts:7:13:7:15 | req | 'express'.Request | | subtype.ts:13:13:13:15 | req | 'express'.Request | | subtype.ts:19:13:19:15 | req | 'express'.Request | +| typeCast.ts:4:16:4:35 | e as express.Request | 'express'.Request | +| typeCast.ts:5:16:5:33 | e | 'express'.Request | | varAssignment.ts:4:9:4:11 | req | 'express'.Request | diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/typeCast.ts b/javascript/ql/test/library-tests/UnderlyingTypes/typeCast.ts new file mode 100644 index 000000000000..733292db3334 --- /dev/null +++ b/javascript/ql/test/library-tests/UnderlyingTypes/typeCast.ts @@ -0,0 +1,7 @@ +import * as express from 'express'; + +function t1(e) { + var req1 = e as express.Request; // $ hasUnderlyingType='express'.Request + var req2 = e; // $ hasUnderlyingType='express'.Request + var req3 = e satisfies express.Request; +} From 57fad7e6c99097349f3b60ef2ad52efad6ebb339 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 4 Jun 2025 22:12:36 +0200 Subject: [PATCH 40/47] JS: Add SatisfiesExpr --- javascript/ql/lib/semmle/javascript/dataflow/Sources.qll | 3 ++- .../ql/lib/semmle/javascript/internal/TypeResolution.qll | 2 ++ javascript/ql/test/library-tests/UnderlyingTypes/test.expected | 1 + javascript/ql/test/library-tests/UnderlyingTypes/typeCast.ts | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll b/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll index ad0be2765115..f861488a046c 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll @@ -334,7 +334,8 @@ module SourceNode { astNode instanceof Templating::PipeRefExpr or astNode instanceof Templating::TemplateVarRefExpr or astNode instanceof StringLiteral or - astNode instanceof TypeAssertion + astNode instanceof TypeAssertion or + astNode instanceof SatisfiesExpr ) or exists(VariableDeclarator decl | diff --git a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll index a1d0ab43a6fb..6a99d8917393 100644 --- a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll @@ -235,6 +235,8 @@ module TypeResolution { or value.(TypeAssertion).getTypeAnnotation() = type or + value.(SatisfiesExpr).getTypeAnnotation() = type + or exists(VarDecl decl | // ValueFlow::step is restricted to variables with at most one assignment. Allow the type annotation // of a variable to propagate to its uses, even if the variable has multiple assignments. diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/test.expected b/javascript/ql/test/library-tests/UnderlyingTypes/test.expected index be3c188b7088..9525a32706b4 100644 --- a/javascript/ql/test/library-tests/UnderlyingTypes/test.expected +++ b/javascript/ql/test/library-tests/UnderlyingTypes/test.expected @@ -50,4 +50,5 @@ | subtype.ts:19:13:19:15 | req | 'express'.Request | | typeCast.ts:4:16:4:35 | e as express.Request | 'express'.Request | | typeCast.ts:5:16:5:33 | e | 'express'.Request | +| typeCast.ts:6:16:6:42 | e satis ... Request | 'express'.Request | | varAssignment.ts:4:9:4:11 | req | 'express'.Request | diff --git a/javascript/ql/test/library-tests/UnderlyingTypes/typeCast.ts b/javascript/ql/test/library-tests/UnderlyingTypes/typeCast.ts index 733292db3334..09b6105d0126 100644 --- a/javascript/ql/test/library-tests/UnderlyingTypes/typeCast.ts +++ b/javascript/ql/test/library-tests/UnderlyingTypes/typeCast.ts @@ -3,5 +3,5 @@ import * as express from 'express'; function t1(e) { var req1 = e as express.Request; // $ hasUnderlyingType='express'.Request var req2 = e; // $ hasUnderlyingType='express'.Request - var req3 = e satisfies express.Request; + var req3 = e satisfies express.Request; // $ hasUnderlyingType='express'.Request } From 691fdb106ecf7d7d52296ec50dd7ed4dc08aaf35 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 4 Jun 2025 22:16:25 +0200 Subject: [PATCH 41/47] JS: Nicer jump-to-def for function declarations --- javascript/ql/lib/definitions.qll | 5 +++++ .../ql/test/query-tests/definitions/definitions.expected | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/javascript/ql/lib/definitions.qll b/javascript/ql/lib/definitions.qll index 6c9986288212..2f1c99b7c606 100644 --- a/javascript/ql/lib/definitions.qll +++ b/javascript/ql/lib/definitions.qll @@ -183,6 +183,11 @@ private AstNode redirectOnce(AstNode node) { result = cls.getIdentifier() ) or + exists(FunctionDeclStmt decl | + node = decl and + result = decl.getIdentifier() + ) + or exists(MethodDeclaration member | not member instanceof ConstructorDeclaration and node = member.getBody() and diff --git a/javascript/ql/test/query-tests/definitions/definitions.expected b/javascript/ql/test/query-tests/definitions/definitions.expected index cdf3136eb386..cb91ac6e37ce 100644 --- a/javascript/ql/test/query-tests/definitions/definitions.expected +++ b/javascript/ql/test/query-tests/definitions/definitions.expected @@ -1,6 +1,6 @@ | b.js:3:3:3:3 | x | b.js:2:7:2:7 | x | V | -| b.js:7:1:7:1 | f | b.js:1:1:5:1 | functio ... ar x;\\n} | M | -| b.js:8:1:8:1 | g | a.js:2:1:2:15 | function g() {} | M | +| b.js:7:1:7:1 | f | b.js:1:10:1:10 | f | M | +| b.js:8:1:8:1 | g | a.js:2:10:2:10 | g | M | | client.ts:1:22:1:30 | "./tslib" | tslib.ts:1:1:10:0 | | I | | client.ts:7:19:7:19 | C | tslib.ts:1:14:1:14 | C | T | | client.ts:8:10:8:10 | C | client.ts:3:7:3:7 | C | T | @@ -16,12 +16,12 @@ | client.ts:16:3:16:3 | z | client.ts:13:38:13:38 | z | V | | client.ts:16:5:16:5 | m | tslib.ts:7:5:7:5 | m | M | | d.js:1:17:1:21 | './c' | c.js:1:1:1:20 | | I | -| d.js:10:1:10:1 | A | d.js:7:1:9:1 | functio ... = 42;\\n} | V | +| d.js:10:1:10:1 | A | d.js:7:10:7:10 | A | V | | d.js:16:19:16:23 | Super | d.js:12:7:12:11 | Super | V | | d.js:16:25:16:24 | args | d.js:16:25:16:24 | args | V | | d.js:20:1:20:1 | o | d.js:3:9:5:1 | {\\n f: ... () {}\\n} | V | | d.js:20:3:20:3 | f | d.js:4:3:4:18 | f: function() {} | M | -| d.js:22:13:22:13 | A | d.js:7:1:9:1 | functio ... = 42;\\n} | M | +| d.js:22:13:22:13 | A | d.js:7:10:7:10 | A | M | | d.js:23:1:23:1 | a | d.js:22:5:22:5 | a | V | | d.js:23:3:23:3 | x | d.js:8:3:8:8 | this.x | M | | d.js:24:1:24:1 | a | d.js:22:5:22:5 | a | V | From 42f762a140a871f4261ba4829b9d18858c833549 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 9 Jun 2025 16:22:30 +0200 Subject: [PATCH 42/47] JS: Update test output now that 'satisfies' is a SourceNode --- javascript/ql/test/library-tests/DataFlow/tests.expected | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/ql/test/library-tests/DataFlow/tests.expected b/javascript/ql/test/library-tests/DataFlow/tests.expected index e3f6073461a0..26ba8c46a993 100644 --- a/javascript/ql/test/library-tests/DataFlow/tests.expected +++ b/javascript/ql/test/library-tests/DataFlow/tests.expected @@ -1523,6 +1523,7 @@ sources | tst2.ts:13:39:13:38 | super(...args) | | tst2.ts:13:39:13:38 | this | | tst2.ts:15:11:15:13 | A.x | +| tst2.ts:15:11:15:30 | A.x satisfies number | | tst.js:1:1:1:0 | this | | tst.js:1:1:1:24 | import ... m 'fs'; | | tst.js:1:10:1:11 | fs | From a6488cbad99a9a590c981fe5541a36ca29f21ddb Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 10 Jun 2025 16:06:42 +0200 Subject: [PATCH 43/47] Update javascript/ql/lib/semmle/javascript/internal/NameResolution.qll Co-authored-by: Erik Krogh Kristensen --- .../ql/lib/semmle/javascript/internal/NameResolution.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll index 8a9a1c71edce..394eb34e8d3e 100644 --- a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll @@ -312,12 +312,12 @@ module NameResolution { */ private predicate storeToVariable(Expr value, string prop, LocalVariableLike target) { exists(AssignExpr assign | - // exports.name = value + // target.name = value assign.getLhs().(PropAccess).accesses(target.getAnAccess(), prop) and value = assign.getRhs() ) or - // exports = { name: value } + // target = { name: value } value = target.getAnAssignedExpr().(ObjectExpr).getPropertyByName(prop).getInit() } From 18f9133715a59f4c92e1fad974d3610257e9de68 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 10 Jun 2025 16:14:05 +0200 Subject: [PATCH 44/47] JS: Rename and clarify comment for trackFunctionType --- .../javascript/internal/TypeResolution.qll | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll index 6a99d8917393..ddf5757a38cc 100644 --- a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll @@ -8,10 +8,16 @@ module TypeResolution { predicate trackType = TypeFlow::TrackNode::track/1; - Node trackFunctionType(Function fun) { + /** + * Gets a node that has `fun` as an underlying type. + * + * We track through underlying types as an approximate way to handle calls to a type + * that is a union/intersection involving functions. + */ + Node trackUnderlyingFunctionType(Function fun) { result = fun or - exists(Node mid | mid = trackFunctionType(fun) | + exists(Node mid | mid = trackUnderlyingFunctionType(fun) | TypeFlow::step(mid, result) or UnderlyingTypes::underlyingTypeStep(mid, result) @@ -138,7 +144,7 @@ module TypeResolution { or valueHasType(call.getCallee(), trackFunctionValue(target)) or - valueHasType(call.getCallee(), trackFunctionType(target)) and + valueHasType(call.getCallee(), trackUnderlyingFunctionType(target)) and ( call instanceof NewExpr and target = any(ConstructorTypeExpr t).getFunction() @@ -165,7 +171,7 @@ module TypeResolution { or not exists(func.getReturnTypeAnnotation()) and exists(Function functionType | - contextualType(func, trackFunctionType(functionType)) and + contextualType(func, trackUnderlyingFunctionType(functionType)) and returnType = functionType.getReturnTypeAnnotation() ) } @@ -270,7 +276,7 @@ module TypeResolution { or // Contextual typing for parameters exists(Function lambda, Function functionType, int i | - contextualType(lambda, trackFunctionType(functionType)) + contextualType(lambda, trackUnderlyingFunctionType(functionType)) or exists(InterfaceDefinition interface | contextualType(lambda, trackType(interface)) and From 72cc439125a18992715818346743225ccc8035b6 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 10 Jun 2025 17:32:42 +0200 Subject: [PATCH 45/47] JS: Normalize a few more extensions --- javascript/ql/lib/semmle/javascript/internal/NameResolution.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll index 394eb34e8d3e..0aa76b38e7ce 100644 --- a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll @@ -392,7 +392,7 @@ module NameResolution { private string normalizeModuleName(string name) { result = name.regexpReplaceAll("^node:", "") - .regexpReplaceAll("\\.[jt]sx?$", "") + .regexpReplaceAll("\\.[cm]?[jt]sx?$", "") .regexpReplaceAll("/(index)?$", "") } From 2aa5fa17f7cb5216d520999f998e3279d1079637 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 11 Jun 2025 10:21:24 +0200 Subject: [PATCH 46/47] JS: Add comment and examples in FlowImpl doc --- .../javascript/internal/NameResolution.qll | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll index 0aa76b38e7ce..3d0d8d6ef2b0 100644 --- a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll @@ -271,6 +271,32 @@ module NameResolution { */ module TypeFlow = FlowImpl; + /** + * Generates a directed graph for tracking type names or value names back toward their definition. + * The ultimate definition might not be in the database, but the graph lets us track as far as we can. + * + * The module parameter determines whether types or values should be tracked. + * + * The example below illustrates the need for two separate instantiations of this module. + * When tracking through the nodes corresponding to `X`, we need to remember whether a type or value was tracked. + * + * ```ts + * // lib.ts + * class C1 {} + * class C2 {} + * + * const X = C1; + * type X = C2; + * + * export { X } + * + * // use.ts + * import { X } from "./lib" + * + * var x1 = X // should refer to C1 + * var x2: X; // should refer to C2 + * ``` + */ private module FlowImpl { /** * Gets the exported member of `mod` named `name`. From e848aa747b181e5f87dc93343fc72b37329aefec Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 11 Jun 2025 10:24:21 +0200 Subject: [PATCH 47/47] JS: Clarifying comment on commonStep --- javascript/ql/lib/semmle/javascript/internal/NameResolution.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll index 3d0d8d6ef2b0..96e72108e2ec 100644 --- a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll @@ -65,6 +65,8 @@ module NameResolution { /** * Holds if values/namespaces/types in `node1` can flow to values/namespaces/types in `node2`. + * + * May also include some type-specific steps in cases where this is harmless when tracking values. */ private predicate commonStep(Node node1, Node node2) { // Import paths are part of the graph and has an incoming edge from the imported module, if found.