Skip to content

Commit bfcc434

Browse files
committed
JS: Use both local and global names in hasQualifiedName
1 parent f7552a7 commit bfcc434

File tree

5 files changed

+93
-0
lines changed

5 files changed

+93
-0
lines changed

javascript/ql/src/semmle/javascript/TypeScript.qll

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,15 @@ class LocalNamespaceName extends @local_namespace_name, LexicalName {
509509
/** Gets a use of this namespace in an export. */
510510
ExportVarAccess getAnExportAccess() { namespacebind(result, this) }
511511

512+
/**
513+
* Gets an access to a type member of this namespace alias,
514+
* such as `http.ServerRequest` where `http` is a reference to this namespace.
515+
*/
516+
QualifiedTypeAccess getAMemberAccess(string member) {
517+
result.getIdentifier().getName() = member and
518+
result.getQualifier() = this.getAnAccess()
519+
}
520+
512521
/** Gets an identifier that refers to this namespace name. */
513522
Identifier getAnAccess() { namespacebind(result, this) }
514523

@@ -663,13 +672,50 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef {
663672

664673
override predicate hasQualifiedName(string globalName) {
665674
getTypeName().hasQualifiedName(globalName)
675+
or
676+
exists(LocalTypeAccess local | local = this |
677+
not exists(local.getLocalTypeName()) and // Without a local type name, the type is looked up in the global scope.
678+
globalName = local.getName()
679+
)
666680
}
667681

668682
override predicate hasQualifiedName(string moduleName, string exportedName) {
669683
getTypeName().hasQualifiedName(moduleName, exportedName)
684+
or
685+
exists(ImportDeclaration imprt, ImportSpecifier spec |
686+
moduleName = getImportName(imprt) and
687+
spec = imprt.getASpecifier()
688+
|
689+
spec.getImportedName() = exportedName and
690+
this = spec.getLocal().(TypeDecl).getLocalTypeName().getAnAccess()
691+
or
692+
spec instanceof ImportNamespaceSpecifier and
693+
this =
694+
spec.getLocal().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName)
695+
)
696+
or
697+
exists(ImportEqualsDeclaration imprt |
698+
moduleName = getImportName(imprt.getImportedEntity()) and
699+
this =
700+
imprt.getId().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName)
701+
)
670702
}
671703
}
672704

705+
/**
706+
* Gets a suitable name for the library imported by `import`.
707+
*
708+
* For relative imports, this is the snapshot-relative path to the imported module.
709+
* For non-relative imports, it is the import path itself.
710+
*/
711+
private string getImportName(Import imprt) {
712+
exists(string path | path = imprt.getImportedPath().getValue() |
713+
if path.regexpMatch("[./].*")
714+
then result = imprt.getImportedModule().getFile().getRelativePath()
715+
else result = path
716+
)
717+
}
718+
673719
/** An identifier that is used as part of a type, such as `Date`. */
674720
class LocalTypeAccess extends @localtypeaccess, TypeAccess, Identifier, LexicalAccess {
675721
override predicate isStringy() { getName() = "String" }
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
hasQualifiedNameModule
2+
| default-import | default | tst.ts:11:9:11:21 | DefaultImport |
3+
| import-assign | Foo | tst.ts:10:9:10:15 | asn.Foo |
4+
| library-tests/TypeScript/HasQualifiedNameFallback/tst.ts | ExportedClass | relative.ts:4:8:4:20 | ExportedClass |
5+
| named-import | Name1 | tst.ts:7:9:7:13 | Name1 |
6+
| named-import | Name1 | tst.ts:13:9:13:13 | Name1 |
7+
| named-import | Name1 | tst.ts:13:9:13:21 | Name1<number> |
8+
| named-import | Name2 | tst.ts:8:9:8:13 | Name2 |
9+
| namespace-import | Foo | tst.ts:9:9:9:21 | namespace.Foo |
10+
hasQualifiedNameGlobal
11+
| UnresolvedName | tst.ts:12:9:12:22 | UnresolvedName |
12+
paramExample
13+
| tst.ts:7:5:7:6 | x1 |
14+
| tst.ts:13:5:13:6 | x8 |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import javascript
2+
3+
query TypeAnnotation hasQualifiedNameModule(string moduleName, string member) {
4+
result.hasQualifiedName(moduleName, member)
5+
}
6+
7+
query TypeAnnotation hasQualifiedNameGlobal(string globalName) {
8+
result.hasQualifiedName(globalName)
9+
}
10+
11+
query Parameter paramExample() {
12+
result.getTypeAnnotation().hasQualifiedName("named-import", "Name1")
13+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import * as foo from "./tst";
2+
import { ExportedClass } from "./tst";
3+
4+
var x: ExportedClass;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as namespace from "namespace-import";
2+
import { Name1, Name2 } from "named-import";
3+
import DefaultImport from "default-import";
4+
import asn = require("import-assign");
5+
6+
function foo(
7+
x1: Name1,
8+
x2: Name2,
9+
x3: namespace.Foo,
10+
x5: asn.Foo,
11+
x6: DefaultImport,
12+
x7: UnresolvedName,
13+
x8: Name1<number>
14+
) {}
15+
16+
export class ExportedClass {};

0 commit comments

Comments
 (0)