Merge pull request #3873 from asger-semmle/js/type-qualified-name-fallback

Approved by erik-krogh
This commit is contained in:
CodeQL CI
2020-09-07 09:48:05 +01:00
committed by GitHub
8 changed files with 113 additions and 0 deletions

View File

@@ -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.

View 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(_, _))

View File

@@ -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" }

View File

@@ -1 +1,2 @@
| tst.ts:4:8:4:21 | UnresolvedType | unresolved | UnresolvedType |
| tst.ts:5:8:5:19 | ResolvedType | resolved | ResolvedType |

View File

@@ -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 |

View File

@@ -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")
}

View File

@@ -0,0 +1,4 @@
import * as foo from "./tst";
import { ExportedClass } from "./tst";
var x: ExportedClass;

View File

@@ -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 {};