diff --git a/javascript/ql/src/semmle/javascript/AST.qll b/javascript/ql/src/semmle/javascript/AST.qll index 81bb64f272c..364c7ce9af9 100644 --- a/javascript/ql/src/semmle/javascript/AST.qll +++ b/javascript/ql/src/semmle/javascript/AST.qll @@ -125,6 +125,42 @@ class ASTNode extends @ast_node, Locatable { /** Holds if this syntactic entity belongs to an externs file. */ predicate inExternsFile() { getTopLevel().isExterns() } + /** + * Holds if this is an ambient node that is not a `TypeExpr` and is not inside a `.d.ts` file + * + * Since the overwhelming majority of ambient nodes are `TypeExpr` or inside `.d.ts` files, + * we avoid caching them. + */ + cached + private predicate isAmbientInternal() { + getParent().isAmbientInternal() + or + not isAmbientTopLevel(getTopLevel()) and + ( + this instanceof ExternalModuleDeclaration + or + this instanceof GlobalAugmentationDeclaration + or + this instanceof ExportAsNamespaceDeclaration + or + this instanceof TypeAliasDeclaration + or + this instanceof InterfaceDeclaration + or + hasDeclareKeyword(this) + or + hasTypeKeyword(this) + or + // An export such as `export declare function f()` should be seen as ambient. + hasDeclareKeyword(this.(ExportNamedDeclaration).getOperand()) + or + exists(Function f | + this = f and + not f.hasBody() + ) + ) + } + /** * Holds if this is part of an ambient declaration or type annotation in a TypeScript file. * @@ -134,9 +170,22 @@ class ASTNode extends @ast_node, Locatable { * The TypeScript compiler emits no code for ambient declarations, but they * can affect name resolution and type checking at compile-time. */ - predicate isAmbient() { getParent().isAmbient() } + pragma[inline] + predicate isAmbient() { + isAmbientInternal() + or + isAmbientTopLevel(getTopLevel()) + or + this instanceof TypeExpr + } } +/** + * Holds if the given file is a `.d.ts` file. + */ +cached +private predicate isAmbientTopLevel(TopLevel tl) { tl.getFile().getBaseName().matches("%.d.ts") } + /** * A toplevel syntactic unit; that is, a stand-alone script, an inline script * embedded in an HTML `