mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #3873 from asger-semmle/js/type-qualified-name-fallback
Approved by erik-krogh
This commit is contained in:
@@ -33,3 +33,4 @@
|
||||
|
||||
|
||||
## Changes to libraries
|
||||
* The predicate `TypeAnnotation.hasQualifiedName` now works in more cases when the imported library was not present during extraction.
|
||||
|
||||
14
javascript/ql/src/meta/types/TypesWithQualifiedName.ql
Normal file
14
javascript/ql/src/meta/types/TypesWithQualifiedName.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @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(_, _))
|
||||
@@ -509,6 +509,15 @@ class LocalNamespaceName extends @local_namespace_name, LexicalName {
|
||||
/** Gets a use of this namespace in an export. */
|
||||
ExportVarAccess getAnExportAccess() { namespacebind(result, this) }
|
||||
|
||||
/**
|
||||
* Gets an access to a type member of this namespace alias,
|
||||
* such as `http.ServerRequest` where `http` is a reference to this namespace.
|
||||
*/
|
||||
QualifiedTypeAccess getAMemberAccess(string member) {
|
||||
result.getIdentifier().getName() = member and
|
||||
result.getQualifier() = this.getAnAccess()
|
||||
}
|
||||
|
||||
/** Gets an identifier that refers to this namespace name. */
|
||||
Identifier getAnAccess() { namespacebind(result, this) }
|
||||
|
||||
@@ -663,13 +672,54 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef {
|
||||
|
||||
override predicate hasQualifiedName(string globalName) {
|
||||
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) {
|
||||
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 and
|
||||
this =
|
||||
spec.getLocal().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName)
|
||||
)
|
||||
or
|
||||
exists(ImportEqualsDeclaration imprt |
|
||||
moduleName = getImportName(imprt.getImportedEntity()) and
|
||||
this =
|
||||
imprt
|
||||
.getIdentifier()
|
||||
.(LocalNamespaceDecl)
|
||||
.getLocalNamespaceName()
|
||||
.getAMemberAccess(exportedName)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a suitable name for the library imported by `import`.
|
||||
*
|
||||
* 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.getImportedPath().getValue() |
|
||||
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 @localtypeaccess, TypeAccess, Identifier, LexicalAccess {
|
||||
override predicate isStringy() { getName() = "String" }
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
| tst.ts:4:8:4:21 | UnresolvedType | unresolved | UnresolvedType |
|
||||
| tst.ts:5:8:5:19 | ResolvedType | resolved | ResolvedType |
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
hasQualifiedNameModule
|
||||
| default-import | default | tst.ts:11:9:11:21 | DefaultImport |
|
||||
| import-assign | Foo | tst.ts:10:9:10:15 | asn.Foo |
|
||||
| library-tests/TypeScript/HasQualifiedNameFallback/tst.ts | ExportedClass | relative.ts:4:8:4:20 | ExportedClass |
|
||||
| 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 | Foo | tst.ts:9:9:9:21 | namespace.Foo |
|
||||
hasQualifiedNameGlobal
|
||||
| UnresolvedName | tst.ts:12:9:12:22 | UnresolvedName |
|
||||
paramExample
|
||||
| tst.ts:7:5:7:6 | x1 |
|
||||
| tst.ts:13:5:13:6 | x8 |
|
||||
@@ -0,0 +1,13 @@
|
||||
import javascript
|
||||
|
||||
query TypeAnnotation hasQualifiedNameModule(string moduleName, string member) {
|
||||
result.hasQualifiedName(moduleName, member)
|
||||
}
|
||||
|
||||
query TypeAnnotation hasQualifiedNameGlobal(string globalName) {
|
||||
result.hasQualifiedName(globalName)
|
||||
}
|
||||
|
||||
query Parameter paramExample() {
|
||||
result.getTypeAnnotation().hasQualifiedName("named-import", "Name1")
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import * as foo from "./tst";
|
||||
import { ExportedClass } from "./tst";
|
||||
|
||||
var x: ExportedClass;
|
||||
@@ -0,0 +1,16 @@
|
||||
import * as namespace from "namespace-import";
|
||||
import { Name1, Name2 } from "named-import";
|
||||
import DefaultImport from "default-import";
|
||||
import asn = require("import-assign");
|
||||
|
||||
function foo(
|
||||
x1: Name1,
|
||||
x2: Name2,
|
||||
x3: namespace.Foo,
|
||||
x5: asn.Foo,
|
||||
x6: DefaultImport,
|
||||
x7: UnresolvedName,
|
||||
x8: Name1<number>
|
||||
) {}
|
||||
|
||||
export class ExportedClass {};
|
||||
Reference in New Issue
Block a user