mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
JS: Use in TypeAnnotation.getClass and hasUnderlyingType predicates
This commit is contained in:
@@ -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>`, `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, _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import javascript
|
||||
|
||||
query string test_hasQualifiedName(JSDocNamedTypeExpr expr) { expr.hasQualifiedName(result) }
|
||||
query string test_hasUnderlyingType(JSDocNamedTypeExpr expr) { expr.hasUnderlyingType(result) }
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<number> |
|
||||
| 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 |
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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() |
|
||||
|
||||
Reference in New Issue
Block a user