JavaScript: Fix exported name of default re-exports.

A default re-export (not part of the standard yet) looks like this:

```
export f from 'mod';
```

What this means is that the default export of `mod` is re-exported under the name `f`.

Default re-export specifiers (like `f` in this example) are modelled as a kind of default export specifier in our library, but unlike normal default export specifiers they do not export the name `default`.

This was previously not modelled correctly, leading to surprising errors down the line, for example in type inference where we suddenly would no longer be able to resolve an import that otherwise looked resolvable.
This commit is contained in:
Max Schaefer
2018-08-17 12:14:41 +01:00
parent 44e4b25f42
commit a9f1e21363
19 changed files with 79 additions and 8 deletions

View File

@@ -8,6 +8,8 @@
* Modelling of global variables has been improved. This may give more true-positive results and fewer false-positive results for a variety of queries.
* Modelling of re-export declarations has been improved. This may result in fewer false-positive results for a variety of queries.
* Modelling of taint flow through the array operations `map` and `join` has been improved. This may give additional results for the security queries.
* Support for popular libraries has been improved. Consequently, queries may produce more results on code bases that use the following libraries:

View File

@@ -428,22 +428,39 @@ class ExportSpecifier extends Expr, @exportspecifier {
string getExportedName() { result = getExported().getName() }
}
/** A named export specifier. */
/**
* A named export specifier, for example `v` in `export { v }`.
*/
class NamedExportSpecifier extends ExportSpecifier, @namedexportspecifier {
}
/** A default export specifier. */
/**
* A default export specifier, for example `default` in `export default 42`,
* or `v` in `export v from "mod"`.
*/
class ExportDefaultSpecifier extends ExportSpecifier, @exportdefaultspecifier {
override string getLocalName() {
getExportDeclaration() instanceof ReExportDeclaration and result = "default"
}
override string getExportedName() {
result = "default"
}
}
/** A namespace export specifier. */
/**
* A default export specifier in a re-export declaration, for example `v` in
* `export v from "mod"`.
*/
class ReExportDefaultSpecifier extends ExportDefaultSpecifier {
ReExportDefaultSpecifier() {
getExportDeclaration() instanceof ReExportDeclaration
}
override string getLocalName() { result = "default" }
override string getExportedName() { result = getExported().getName() }
}
/**
* A namespace export specifier, for example `*` in `export * from "mod"`.
*/
class ExportNamespaceSpecifier extends ExportSpecifier, @exportnamespacespecifier {
}

View File

@@ -217,6 +217,12 @@
| reexport-mixins.js:1:1:4:0 | module object of module reexport-mixins |
| reexport-unknown.js:1:1:2:0 | exports object of module reexport-unknown |
| reexport-unknown.js:1:1:2:0 | module object of module reexport-unknown |
| reexport/client/src/index.js:1:1:3:0 | exports object of module index |
| reexport/client/src/index.js:1:1:3:0 | module object of module index |
| reexport/lib/index.js:1:1:4:0 | exports object of module index |
| reexport/lib/index.js:1:1:4:0 | module object of module index |
| reexport/lib/src/utils/util.js:1:1:3:0 | exports object of module util |
| reexport/lib/src/utils/util.js:1:1:3:0 | module object of module util |
| refinements.js:1:1:8:1 | function f1 |
| refinements.js:1:1:8:1 | instance of function f1 |
| refinements.js:10:1:24:1 | function f2 |

View File

@@ -221,6 +221,7 @@
| objlit.js:38:11:38:12 | x6 | objlit.js:38:16:38:21 | this.h | file://:0:0:0:0 | indefinite value (heap) |
| objlit.js:38:11:38:12 | x6 | objlit.js:38:16:38:21 | this.h | objlit.js:41:10:41:22 | anonymous function |
| objlit.js:43:7:43:8 | o3 | objlit.js:43:12:45:3 | {\\n _ ... o2\\n } | objlit.js:43:12:45:3 | object literal |
| reexport/client/src/index.js:2:5:2:8 | test | reexport/client/src/index.js:2:12:2:15 | data | file://:0:0:0:0 | non-empty, non-numeric string |
| refinements.js:3:7:3:8 | x1 | refinements.js:3:12:3:12 | g | file://:0:0:0:0 | undefined |
| refinements.js:7:7:7:8 | x3 | refinements.js:7:12:7:12 | g | file://:0:0:0:0 | undefined |
| refinements.js:11:7:11:7 | a | refinements.js:11:11:11:72 | Math.ra ... ' : 42) | file://:0:0:0:0 | non-empty, non-numeric string |

View File

@@ -0,0 +1,2 @@
import { data } from '../../lib';
var test = data;

View File

@@ -0,0 +1,3 @@
export data from './src/utils/util'
// semmle-extractor-options: --experimental

View File

@@ -0,0 +1,2 @@
export default "data";

View File

@@ -121,6 +121,7 @@
| objlit.js:37:11:37:12 | x5 | objlit.js:37:16:37:21 | this.f | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| objlit.js:38:11:38:12 | x6 | objlit.js:38:16:38:21 | this.h | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| objlit.js:43:7:43:8 | o3 | objlit.js:43:12:45:3 | {\\n _ ... o2\\n } | object |
| reexport/client/src/index.js:2:5:2:8 | test | reexport/client/src/index.js:2:12:2:15 | data | string |
| refinements.js:3:7:3:8 | x1 | refinements.js:3:12:3:12 | g | undefined |
| refinements.js:5:9:5:10 | x2 | refinements.js:5:14:5:14 | g | |
| refinements.js:7:7:7:8 | x3 | refinements.js:7:12:7:12 | g | undefined |

View File

@@ -1,10 +1,12 @@
| a.js:1:1:3:1 | export ... n 23;\\n} |
| a.js:5:1:5:32 | export ... } = o; |
| b.js:5:1:5:18 | export { f as g }; |
| b.js:7:1:7:21 | export ... './a'; |
| d.js:4:1:4:20 | export * from 'm/c'; |
| e.js:2:1:2:16 | export { x, y }; |
| e.js:3:1:3:35 | export ... './a'; |
| es2015_require.js:3:1:3:25 | export ... ss C {} |
| export-in-mjs.mjs:1:1:1:34 | export ... s = 42; |
| f.ts:5:1:5:24 | export ... oo() {} |
| m/c.js:5:1:5:30 | export ... '../b'; |
| tst.html:7:3:7:22 | export const y = 42; |

View File

@@ -2,3 +2,4 @@
| e.js:2:10:2:10 | x | e.js:2:10:2:10 | x | e.js:2:10:2:10 | x |
| e.js:2:13:2:13 | y | e.js:2:13:2:13 | y | e.js:2:13:2:13 | y |
| e.js:3:10:3:21 | default as g | e.js:3:10:3:16 | default | e.js:3:21:3:21 | g |
| m/c.js:5:10:5:15 | g as h | m/c.js:5:10:5:10 | g | m/c.js:5:15:5:15 | h |

View File

@@ -1,11 +1,13 @@
| a.js:1:1:5:32 | <toplevel> | default | a.js:1:1:3:1 | export ... n 23;\\n} |
| a.js:1:1:5:32 | <toplevel> | x | a.js:5:1:5:32 | export ... } = o; |
| a.js:1:1:5:32 | <toplevel> | y | a.js:5:1:5:32 | export ... } = o; |
| b.js:1:1:6:0 | <toplevel> | g | b.js:5:1:5:18 | export { f as g }; |
| b.js:1:1:10:0 | <toplevel> | f2 | b.js:7:1:7:21 | export ... './a'; |
| b.js:1:1:10:0 | <toplevel> | g | b.js:5:1:5:18 | export { f as g }; |
| e.js:1:1:4:0 | <toplevel> | g | e.js:3:1:3:35 | export ... './a'; |
| e.js:1:1:4:0 | <toplevel> | x | e.js:2:1:2:16 | export { x, y }; |
| e.js:1:1:4:0 | <toplevel> | y | e.js:2:1:2:16 | export { x, y }; |
| es2015_require.js:1:1:3:25 | <toplevel> | default | es2015_require.js:3:1:3:25 | export ... ss C {} |
| export-in-mjs.mjs:1:1:1:34 | <toplevel> | exported_from_mjs | export-in-mjs.mjs:1:1:1:34 | export ... s = 42; |
| f.ts:1:1:6:0 | <toplevel> | foo | f.ts:5:1:5:24 | export ... oo() {} |
| m/c.js:1:1:8:0 | <toplevel> | h | m/c.js:5:1:5:30 | export ... '../b'; |
| tst.html:4:23:8:0 | <toplevel> | y | tst.html:7:3:7:22 | export const y = 42; |

View File

@@ -1,2 +1,4 @@
| b.js:7:1:7:21 | export ... './a'; | b.js:7:16:7:20 | './a' |
| d.js:4:1:4:20 | export * from 'm/c'; | d.js:4:15:4:19 | 'm/c' |
| e.js:3:1:3:35 | export ... './a'; | e.js:3:30:3:34 | './a' |
| m/c.js:5:1:5:30 | export ... '../b'; | m/c.js:5:24:5:29 | '../b' |

View File

@@ -3,3 +3,7 @@ import f from './a';
f();
export { f as g };
export f2 from './a';
// semmle-extractor-options: --experimental

View File

@@ -0,0 +1,6 @@
| b.js:5:10:5:15 | f as g | f |
| b.js:7:8:7:9 | f2 | default |
| e.js:2:10:2:10 | x | x |
| e.js:2:13:2:13 | y | y |
| e.js:3:10:3:21 | default as g | default |
| m/c.js:5:10:5:15 | g as h | g |

View File

@@ -0,0 +1,4 @@
import javascript
from ExportSpecifier es
select es, es.getLocalName()

View File

@@ -0,0 +1,6 @@
| b.js:5:10:5:15 | f as g | f |
| b.js:7:8:7:9 | f2 | default |
| e.js:2:10:2:10 | x | x |
| e.js:2:13:2:13 | y | y |
| e.js:3:10:3:21 | default as g | default |
| m/c.js:5:10:5:15 | g as h | g |

View File

@@ -0,0 +1,4 @@
import javascript
from ExportSpecifier es
select es, es.getLocalName()

View File

@@ -1,10 +1,12 @@
| a.js:1:1:3:1 | export ... n 23;\\n} | default | a.js:1:16:3:1 | functio ... n 23;\\n} |
| a.js:5:1:5:32 | export ... } = o; | x | a.js:5:18:5:20 | f() |
| b.js:5:1:5:18 | export { f as g }; | g | b.js:5:10:5:10 | f |
| b.js:7:1:7:21 | export ... './a'; | f2 | a.js:1:16:3:1 | functio ... n 23;\\n} |
| e.js:2:1:2:16 | export { x, y }; | x | e.js:2:10:2:10 | x |
| e.js:2:1:2:16 | export { x, y }; | y | e.js:2:13:2:13 | y |
| e.js:3:1:3:35 | export ... './a'; | g | a.js:1:16:3:1 | functio ... n 23;\\n} |
| es2015_require.js:3:1:3:25 | export ... ss C {} | default | es2015_require.js:3:16:3:25 | class C {} |
| export-in-mjs.mjs:1:1:1:34 | export ... s = 42; | exported_from_mjs | export-in-mjs.mjs:1:32:1:33 | 42 |
| f.ts:5:1:5:24 | export ... oo() {} | foo | f.ts:5:8:5:24 | function foo() {} |
| m/c.js:5:1:5:30 | export ... '../b'; | h | b.js:5:10:5:10 | f |
| tst.html:7:3:7:22 | export const y = 42; | y | tst.html:7:20:7:21 | 42 |

View File

@@ -1,3 +1,7 @@
import * as b from '../b';
b.g();
export { g as h } from '../b';
// semmle-extractor-options: --experimental