Skip to content

Commit 549abcf

Browse files
sandersnmprobst
authored andcommitted
Plain JS binder errors (microsoft#46816)
* Plain JS binder errors Issue select errors from the binder in JS files that do not have checkJS explicitly turned on or off. These errors mirror runtime checks done by Javascript. * Rest of plain JS binder errors * address PR comments * Only issue binder errors in plain JS. Checker errors require requesting diagnostics, which stll needs to be peformance tested. This commit removes one cross-file duplicate declaration error in the tests. * fix const lint
1 parent b21afdc commit 549abcf

32 files changed

+893
-9
lines changed

src/compiler/program.ts

+35-7
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,25 @@ namespace ts {
818818
}
819819
}
820820

821+
/** @internal */
822+
export const plainJSErrors: Set<number> = new Set([
823+
Diagnostics.Cannot_redeclare_block_scoped_variable_0.code,
824+
Diagnostics.A_module_cannot_have_multiple_default_exports.code,
825+
Diagnostics.Another_export_default_is_here.code,
826+
Diagnostics.The_first_export_default_is_here.code,
827+
Diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module.code,
828+
Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode.code,
829+
Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here.code,
830+
Diagnostics.constructor_is_a_reserved_word.code,
831+
Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode.code,
832+
Diagnostics.Code_contained_in_a_class_is_evaluated_in_JavaScript_s_strict_mode_which_does_not_allow_this_use_of_0_For_more_information_see_https_Colon_Slash_Slashdeveloper_mozilla_org_Slashen_US_Slashdocs_SlashWeb_SlashJavaScript_SlashReference_SlashStrict_mode.code,
833+
Diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode.code,
834+
Diagnostics.Invalid_use_of_0_in_strict_mode.code,
835+
Diagnostics.A_label_is_not_allowed_here.code,
836+
Diagnostics.Octal_literals_are_not_allowed_in_strict_mode.code,
837+
Diagnostics.with_statements_are_not_allowed_in_strict_mode.code,
838+
]);
839+
821840
/**
822841
* Determine if source file needs to be re-created even if its text hasn't changed
823842
*/
@@ -2005,15 +2024,24 @@ namespace ts {
20052024

20062025
Debug.assert(!!sourceFile.bindDiagnostics);
20072026

2008-
const isCheckJs = isCheckJsEnabledForFile(sourceFile, options);
2027+
const isJs = sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX;
2028+
const isCheckJs = isJs && isCheckJsEnabledForFile(sourceFile, options);
2029+
const isPlainJs = isJs && !sourceFile.checkJsDirective && options.checkJs === undefined;
20092030
const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false;
2010-
// By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins)
2011-
const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX
2012-
|| sourceFile.scriptKind === ScriptKind.External || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred);
2013-
const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
2014-
const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
20152031

2016-
return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined);
2032+
// By default, only type-check .ts, .tsx, Deferred, plain JS, checked JS and External
2033+
// - plain JS: .js files with no // ts-check and checkJs: undefined
2034+
// - check JS: .js files with either // ts-check or checkJs: true
2035+
// - external: files that are added by plugins
2036+
const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX
2037+
|| sourceFile.scriptKind === ScriptKind.External || isPlainJs || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred);
2038+
let bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
2039+
const checkDiagnostics = includeBindAndCheckDiagnostics && !isPlainJs ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
2040+
if (isPlainJs) {
2041+
bindDiagnostics = filter(bindDiagnostics, d => plainJSErrors.has(d.code));
2042+
}
2043+
// skip ts-expect-error errors in plain JS files, and skip JSDoc errors except in checked JS
2044+
return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics && !isPlainJs, bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined);
20172045
});
20182046
}
20192047

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/a.js(18,9): error TS1210: Code contained in a class is evaluated in JavaScript's strict mode which does not allow this use of 'arguments'. For more information, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode.
2+
3+
4+
==== /a.js (1 errors) ====
5+
class A {
6+
/**
7+
* Constructor
8+
*
9+
* @param {object} [foo={}]
10+
*/
11+
constructor(foo = {}) {
12+
const key = "bar";
13+
14+
/**
15+
* @type object
16+
*/
17+
this.foo = foo;
18+
19+
/**
20+
* @type object
21+
*/
22+
const arguments = this.arguments;
23+
~~~~~~~~~
24+
!!! error TS1210: Code contained in a class is evaluated in JavaScript's strict mode which does not allow this use of 'arguments'. For more information, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode.
25+
26+
/**
27+
* @type object
28+
*/
29+
this.bar = arguments.bar;
30+
31+
/**
32+
* @type object
33+
*/
34+
this.baz = arguments[key];
35+
36+
/**
37+
* @type object
38+
*/
39+
this.options = arguments;
40+
}
41+
42+
get arguments() {
43+
return { bar: {} };
44+
}
45+
}
46+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/a.js(16,9): error TS1210: Code contained in a class is evaluated in JavaScript's strict mode which does not allow this use of 'arguments'. For more information, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode.
2+
3+
4+
==== /a.js (1 errors) ====
5+
class A {
6+
/**
7+
* @param {object} [foo={}]
8+
*/
9+
m(foo = {}) {
10+
const key = "bar";
11+
12+
/**
13+
* @type object
14+
*/
15+
this.foo = foo;
16+
17+
/**
18+
* @type object
19+
*/
20+
const arguments = this.arguments;
21+
~~~~~~~~~
22+
!!! error TS1210: Code contained in a class is evaluated in JavaScript's strict mode which does not allow this use of 'arguments'. For more information, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode.
23+
24+
/**
25+
* @type object
26+
*/
27+
this.bar = arguments.bar;
28+
29+
/**
30+
* @type object
31+
*/
32+
this.baz = arguments[key];
33+
34+
/**
35+
* @type object
36+
*/
37+
this.options = arguments;
38+
}
39+
40+
get arguments() {
41+
return { bar: {} };
42+
}
43+
}
44+
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
tests/cases/compiler/export.js(1,13): error TS2451: Cannot redeclare block-scoped variable 'foo'.
12
tests/cases/compiler/export.js(1,13): error TS8008: Type aliases can only be used in TypeScript files.
3+
tests/cases/compiler/export.js(6,14): error TS2451: Cannot redeclare block-scoped variable 'foo'.
24

35

4-
==== tests/cases/compiler/export.js (1 errors) ====
6+
==== tests/cases/compiler/export.js (3 errors) ====
57
export type foo = 5;
68
~~~
9+
!!! error TS2451: Cannot redeclare block-scoped variable 'foo'.
10+
~~~
711
!!! error TS8008: Type aliases can only be used in TypeScript files.
812
/**
913
* @typedef {{
1014
* }}
1115
*/
12-
export const foo = 5;
16+
export const foo = 5;
17+
~~~
18+
!!! error TS2451: Cannot redeclare block-scoped variable 'foo'.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
tests/cases/conformance/salsa/plainJSBinderErrors.js(1,1): error TS2528: A module cannot have multiple default exports.
2+
tests/cases/conformance/salsa/plainJSBinderErrors.js(2,1): error TS2528: A module cannot have multiple default exports.
3+
tests/cases/conformance/salsa/plainJSBinderErrors.js(3,7): error TS1262: Identifier expected. 'await' is a reserved word at the top-level of a module.
4+
tests/cases/conformance/salsa/plainJSBinderErrors.js(4,7): error TS1214: Identifier expected. 'yield' is a reserved word in strict mode. Modules are automatically in strict mode.
5+
tests/cases/conformance/salsa/plainJSBinderErrors.js(6,11): error TS1359: Identifier expected. 'await' is a reserved word that cannot be used here.
6+
tests/cases/conformance/salsa/plainJSBinderErrors.js(9,11): error TS1214: Identifier expected. 'yield' is a reserved word in strict mode. Modules are automatically in strict mode.
7+
tests/cases/conformance/salsa/plainJSBinderErrors.js(12,5): error TS18012: '#constructor' is a reserved word.
8+
tests/cases/conformance/salsa/plainJSBinderErrors.js(15,20): error TS1102: 'delete' cannot be called on an identifier in strict mode.
9+
tests/cases/conformance/salsa/plainJSBinderErrors.js(18,16): error TS1102: 'delete' cannot be called on an identifier in strict mode.
10+
tests/cases/conformance/salsa/plainJSBinderErrors.js(19,16): error TS1102: 'delete' cannot be called on an identifier in strict mode.
11+
tests/cases/conformance/salsa/plainJSBinderErrors.js(22,15): error TS1210: Code contained in a class is evaluated in JavaScript's strict mode which does not allow this use of 'eval'. For more information, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode.
12+
tests/cases/conformance/salsa/plainJSBinderErrors.js(23,15): error TS1210: Code contained in a class is evaluated in JavaScript's strict mode which does not allow this use of 'arguments'. For more information, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode.
13+
tests/cases/conformance/salsa/plainJSBinderErrors.js(26,27): error TS1121: Octal literals are not allowed in strict mode.
14+
tests/cases/conformance/salsa/plainJSBinderErrors.js(27,9): error TS1101: 'with' statements are not allowed in strict mode.
15+
tests/cases/conformance/salsa/plainJSBinderErrors.js(33,13): error TS1344: 'A label is not allowed here.
16+
tests/cases/conformance/salsa/plainJSBinderErrors.js(39,7): error TS1215: Invalid use of 'eval'. Modules are automatically in strict mode.
17+
tests/cases/conformance/salsa/plainJSBinderErrors.js(40,7): error TS1215: Invalid use of 'arguments'. Modules are automatically in strict mode.
18+
19+
20+
==== tests/cases/conformance/salsa/plainJSBinderErrors.js (17 errors) ====
21+
export default 12
22+
~~~~~~~~~~~~~~~~~
23+
!!! error TS2528: A module cannot have multiple default exports.
24+
!!! related TS2753 tests/cases/conformance/salsa/plainJSBinderErrors.js:2:1: Another export default is here.
25+
export default 13
26+
~~~~~~~~~~~~~~~~~
27+
!!! error TS2528: A module cannot have multiple default exports.
28+
!!! related TS2752 tests/cases/conformance/salsa/plainJSBinderErrors.js:1:1: The first export default is here.
29+
const await = 1
30+
~~~~~
31+
!!! error TS1262: Identifier expected. 'await' is a reserved word at the top-level of a module.
32+
const yield = 2
33+
~~~~~
34+
!!! error TS1214: Identifier expected. 'yield' is a reserved word in strict mode. Modules are automatically in strict mode.
35+
async function f() {
36+
const await = 3
37+
~~~~~
38+
!!! error TS1359: Identifier expected. 'await' is a reserved word that cannot be used here.
39+
}
40+
function* g() {
41+
const yield = 4
42+
~~~~~
43+
!!! error TS1214: Identifier expected. 'yield' is a reserved word in strict mode. Modules are automatically in strict mode.
44+
}
45+
class C {
46+
#constructor = 5
47+
~~~~~~~~~~~~
48+
!!! error TS18012: '#constructor' is a reserved word.
49+
deleted() {
50+
function container(f) {
51+
delete f
52+
~
53+
!!! error TS1102: 'delete' cannot be called on an identifier in strict mode.
54+
}
55+
var g = 6
56+
delete g
57+
~
58+
!!! error TS1102: 'delete' cannot be called on an identifier in strict mode.
59+
delete container
60+
~~~~~~~~~
61+
!!! error TS1102: 'delete' cannot be called on an identifier in strict mode.
62+
}
63+
evalArguments() {
64+
const eval = 7
65+
~~~~
66+
!!! error TS1210: Code contained in a class is evaluated in JavaScript's strict mode which does not allow this use of 'eval'. For more information, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode.
67+
const arguments = 8
68+
~~~~~~~~~
69+
!!! error TS1210: Code contained in a class is evaluated in JavaScript's strict mode which does not allow this use of 'arguments'. For more information, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode.
70+
}
71+
withOctal() {
72+
const redundant = 010
73+
~~~
74+
!!! error TS1121: Octal literals are not allowed in strict mode.
75+
with (redundant) {
76+
~~~~
77+
!!! error TS1101: 'with' statements are not allowed in strict mode.
78+
return toFixed()
79+
}
80+
}
81+
label() {
82+
for(;;) {
83+
label: var x = 1
84+
~~~~~
85+
!!! error TS1344: 'A label is not allowed here.
86+
break label
87+
}
88+
return x
89+
}
90+
}
91+
const eval = 9
92+
~~~~
93+
!!! error TS1215: Invalid use of 'eval'. Modules are automatically in strict mode.
94+
const arguments = 10
95+
~~~~~~~~~
96+
!!! error TS1215: Invalid use of 'arguments'. Modules are automatically in strict mode.
97+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//// [plainJSBinderErrors.js]
2+
export default 12
3+
export default 13
4+
const await = 1
5+
const yield = 2
6+
async function f() {
7+
const await = 3
8+
}
9+
function* g() {
10+
const yield = 4
11+
}
12+
class C {
13+
#constructor = 5
14+
deleted() {
15+
function container(f) {
16+
delete f
17+
}
18+
var g = 6
19+
delete g
20+
delete container
21+
}
22+
evalArguments() {
23+
const eval = 7
24+
const arguments = 8
25+
}
26+
withOctal() {
27+
const redundant = 010
28+
with (redundant) {
29+
return toFixed()
30+
}
31+
}
32+
label() {
33+
for(;;) {
34+
label: var x = 1
35+
break label
36+
}
37+
return x
38+
}
39+
}
40+
const eval = 9
41+
const arguments = 10
42+
43+
44+
//// [plainJSBinderErrors.js]
45+
export default 12;
46+
export default 13;
47+
const await = 1;
48+
const yield = 2;
49+
async function f() {
50+
const await = 3;
51+
}
52+
function* g() {
53+
const yield = 4;
54+
}
55+
class C {
56+
#constructor = 5;
57+
deleted() {
58+
function container(f) {
59+
delete f;
60+
}
61+
var g = 6;
62+
delete g;
63+
delete container;
64+
}
65+
evalArguments() {
66+
const eval = 7;
67+
const arguments = 8;
68+
}
69+
withOctal() {
70+
const redundant = 010;
71+
with (redundant) {
72+
return toFixed();
73+
}
74+
}
75+
label() {
76+
for (;;) {
77+
label: var x = 1;
78+
break label;
79+
}
80+
return x;
81+
}
82+
}
83+
const eval = 9;
84+
const arguments = 10;

0 commit comments

Comments
 (0)