From 29a85e02ab4d43cf67d013aaac158c6a32108d20 Mon Sep 17 00:00:00 2001 From: Slawomir Sadziak Date: Sun, 9 Oct 2016 00:49:51 +0200 Subject: [PATCH 1/3] Fix #10758 Add compiler option to parse in strict mode * add compiler option alwaysStrict * compile in strict mode when option is set * emit "use strict" --- src/compiler/binder.ts | 15 +++++++++++++-- src/compiler/commandLineParser.ts | 5 +++++ src/compiler/diagnosticMessages.json | 4 ++++ src/compiler/transformers/ts.ts | 12 ++++++++++++ src/compiler/types.ts | 1 + src/harness/unittests/transpile.ts | 4 ++++ .../baselines/reference/alwaysStrict.errors.txt | 10 ++++++++++ tests/baselines/reference/alwaysStrict.js | 11 +++++++++++ .../reference/alwaysStrictES6.errors.txt | 10 ++++++++++ tests/baselines/reference/alwaysStrictES6.js | 11 +++++++++++ .../reference/alwaysStrictModule.errors.txt | 12 ++++++++++++ tests/baselines/reference/alwaysStrictModule.js | 17 +++++++++++++++++ .../alwaysStrictNoImplicitUseStrict.errors.txt | 12 ++++++++++++ .../alwaysStrictNoImplicitUseStrict.js | 17 +++++++++++++++++ .../transpile/Supports setting alwaysStrict.js | 3 +++ tests/cases/compiler/alwaysStrict.ts | 5 +++++ tests/cases/compiler/alwaysStrictES6.ts | 6 ++++++ tests/cases/compiler/alwaysStrictModule.ts | 8 ++++++++ .../compiler/alwaysStrictNoImplicitUseStrict.ts | 9 +++++++++ 19 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/alwaysStrict.errors.txt create mode 100644 tests/baselines/reference/alwaysStrict.js create mode 100644 tests/baselines/reference/alwaysStrictES6.errors.txt create mode 100644 tests/baselines/reference/alwaysStrictES6.js create mode 100644 tests/baselines/reference/alwaysStrictModule.errors.txt create mode 100644 tests/baselines/reference/alwaysStrictModule.js create mode 100644 tests/baselines/reference/alwaysStrictNoImplicitUseStrict.errors.txt create mode 100644 tests/baselines/reference/alwaysStrictNoImplicitUseStrict.js create mode 100644 tests/baselines/reference/transpile/Supports setting alwaysStrict.js create mode 100644 tests/cases/compiler/alwaysStrict.ts create mode 100644 tests/cases/compiler/alwaysStrictES6.ts create mode 100644 tests/cases/compiler/alwaysStrictModule.ts create mode 100644 tests/cases/compiler/alwaysStrictNoImplicitUseStrict.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 27803b43526..13da6520408 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -121,7 +121,8 @@ namespace ts { // If this file is an external module, then it is automatically in strict-mode according to // ES6. If it is not an external module, then we'll determine if it is in strict mode or - // not depending on if we see "use strict" in certain places (or if we hit a class/namespace). + // not depending on if we see "use strict" in certain places or if we hit a class/namespace + // or if compiler options contain alwaysStrict. let inStrictMode: boolean; let symbolCount = 0; @@ -139,7 +140,7 @@ namespace ts { file = f; options = opts; languageVersion = getEmitScriptTarget(options); - inStrictMode = !!file.externalModuleIndicator; + inStrictMode = bindInStrictMode(file, opts); classifiableNames = createMap(); symbolCount = 0; skipTransformFlagAggregation = isDeclarationFile(file); @@ -174,6 +175,16 @@ namespace ts { return bindSourceFile; + function bindInStrictMode(file: SourceFile, opts: CompilerOptions): boolean { + if (opts.alwaysStrict && !isDeclarationFile(file)) { + // bind in strict mode source files with alwaysStrict option + return true; + } + else { + return !!file.externalModuleIndicator; + } + } + function createSymbol(flags: SymbolFlags, name: string): Symbol { symbolCount++; return new Symbol(flags, name); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 648447ac26a..2d657307ec7 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -444,6 +444,11 @@ namespace ts { name: "importHelpers", type: "boolean", description: Diagnostics.Import_emit_helpers_from_tslib + }, + { + name: "alwaysStrict", + type: "boolean", + description: Diagnostics.Parse_in_strict_mode_and_emit_use_strict_for_each_source_file } ]; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 58c0e6ee3f2..70fc7c43d8d 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2861,6 +2861,10 @@ "category": "Error", "code": 6140 }, + "Parse in strict mode and emit \"use strict\" for each source file": { + "category": "Message", + "code": 6141 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 41f03d20f2e..7b6c28a8f41 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -436,6 +436,11 @@ namespace ts { function visitSourceFile(node: SourceFile) { currentSourceFile = node; + // ensure "use strict"" is emitted in all scenarios in alwaysStrict mode + if (compilerOptions.alwaysStrict) { + node = emitUseStrict(node); + } + // If the source file requires any helpers and is an external module, and // the importHelpers compiler option is enabled, emit a synthesized import // statement for the helpers library. @@ -472,6 +477,13 @@ namespace ts { return node; } + function emitUseStrict(node: SourceFile): SourceFile { + const statements: Statement[] = []; + statements.push(startOnNewLine(createStatement(createLiteral("use strict")))); + // add "use strict" as the first statement + return updateSourceFileNode(node, statements.concat(node.statements)); + } + /** * Tests whether we should emit a __decorate call for a class declaration. */ diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 638504e613f..171d7c40e7e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2929,6 +2929,7 @@ namespace ts { allowSyntheticDefaultImports?: boolean; allowUnreachableCode?: boolean; allowUnusedLabels?: boolean; + alwaysStrict?: boolean; baseUrl?: string; charset?: string; /* @internal */ configFilePath?: string; diff --git a/src/harness/unittests/transpile.ts b/src/harness/unittests/transpile.ts index 808a5df37c1..35b4f808350 100644 --- a/src/harness/unittests/transpile.ts +++ b/src/harness/unittests/transpile.ts @@ -253,6 +253,10 @@ var x = 0;`, { options: { compilerOptions: { allowUnusedLabels: true }, fileName: "input.js", reportDiagnostics: true } }); + transpilesCorrectly("Supports setting 'alwaysStrict'", "x;", { + options: { compilerOptions: { alwaysStrict: true }, fileName: "input.js", reportDiagnostics: true } + }); + transpilesCorrectly("Supports setting 'baseUrl'", "x;", { options: { compilerOptions: { baseUrl: "./folder/baseUrl" }, fileName: "input.js", reportDiagnostics: true } }); diff --git a/tests/baselines/reference/alwaysStrict.errors.txt b/tests/baselines/reference/alwaysStrict.errors.txt new file mode 100644 index 00000000000..d7fcfb45801 --- /dev/null +++ b/tests/baselines/reference/alwaysStrict.errors.txt @@ -0,0 +1,10 @@ +tests/cases/compiler/alwaysStrict.ts(3,9): error TS1100: Invalid use of 'arguments' in strict mode. + + +==== tests/cases/compiler/alwaysStrict.ts (1 errors) ==== + + function f() { + var arguments = []; + ~~~~~~~~~ +!!! error TS1100: Invalid use of 'arguments' in strict mode. + } \ No newline at end of file diff --git a/tests/baselines/reference/alwaysStrict.js b/tests/baselines/reference/alwaysStrict.js new file mode 100644 index 00000000000..bc5f9a29912 --- /dev/null +++ b/tests/baselines/reference/alwaysStrict.js @@ -0,0 +1,11 @@ +//// [alwaysStrict.ts] + +function f() { + var arguments = []; +} + +//// [alwaysStrict.js] +"use strict"; +function f() { + var arguments = []; +} diff --git a/tests/baselines/reference/alwaysStrictES6.errors.txt b/tests/baselines/reference/alwaysStrictES6.errors.txt new file mode 100644 index 00000000000..b775a6e4755 --- /dev/null +++ b/tests/baselines/reference/alwaysStrictES6.errors.txt @@ -0,0 +1,10 @@ +tests/cases/compiler/alwaysStrictES6.ts(3,9): error TS1100: Invalid use of 'arguments' in strict mode. + + +==== tests/cases/compiler/alwaysStrictES6.ts (1 errors) ==== + + function f() { + var arguments = []; + ~~~~~~~~~ +!!! error TS1100: Invalid use of 'arguments' in strict mode. + } \ No newline at end of file diff --git a/tests/baselines/reference/alwaysStrictES6.js b/tests/baselines/reference/alwaysStrictES6.js new file mode 100644 index 00000000000..2c7ad36bce2 --- /dev/null +++ b/tests/baselines/reference/alwaysStrictES6.js @@ -0,0 +1,11 @@ +//// [alwaysStrictES6.ts] + +function f() { + var arguments = []; +} + +//// [alwaysStrictES6.js] +"use strict"; +function f() { + var arguments = []; +} diff --git a/tests/baselines/reference/alwaysStrictModule.errors.txt b/tests/baselines/reference/alwaysStrictModule.errors.txt new file mode 100644 index 00000000000..90c1a0b930a --- /dev/null +++ b/tests/baselines/reference/alwaysStrictModule.errors.txt @@ -0,0 +1,12 @@ +tests/cases/compiler/alwaysStrictModule.ts(4,13): error TS1100: Invalid use of 'arguments' in strict mode. + + +==== tests/cases/compiler/alwaysStrictModule.ts (1 errors) ==== + + module M { + export function f() { + var arguments = []; + ~~~~~~~~~ +!!! error TS1100: Invalid use of 'arguments' in strict mode. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/alwaysStrictModule.js b/tests/baselines/reference/alwaysStrictModule.js new file mode 100644 index 00000000000..f39c87ed96c --- /dev/null +++ b/tests/baselines/reference/alwaysStrictModule.js @@ -0,0 +1,17 @@ +//// [alwaysStrictModule.ts] + +module M { + export function f() { + var arguments = []; + } +} + +//// [alwaysStrictModule.js] +"use strict"; +var M; +(function (M) { + function f() { + var arguments = []; + } + M.f = f; +})(M || (M = {})); diff --git a/tests/baselines/reference/alwaysStrictNoImplicitUseStrict.errors.txt b/tests/baselines/reference/alwaysStrictNoImplicitUseStrict.errors.txt new file mode 100644 index 00000000000..d677ec9b4cb --- /dev/null +++ b/tests/baselines/reference/alwaysStrictNoImplicitUseStrict.errors.txt @@ -0,0 +1,12 @@ +tests/cases/compiler/alwaysStrictNoImplicitUseStrict.ts(4,13): error TS1100: Invalid use of 'arguments' in strict mode. + + +==== tests/cases/compiler/alwaysStrictNoImplicitUseStrict.ts (1 errors) ==== + + module M { + export function f() { + var arguments = []; + ~~~~~~~~~ +!!! error TS1100: Invalid use of 'arguments' in strict mode. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/alwaysStrictNoImplicitUseStrict.js b/tests/baselines/reference/alwaysStrictNoImplicitUseStrict.js new file mode 100644 index 00000000000..cfa7f69629f --- /dev/null +++ b/tests/baselines/reference/alwaysStrictNoImplicitUseStrict.js @@ -0,0 +1,17 @@ +//// [alwaysStrictNoImplicitUseStrict.ts] + +module M { + export function f() { + var arguments = []; + } +} + +//// [alwaysStrictNoImplicitUseStrict.js] +"use strict"; +var M; +(function (M) { + function f() { + var arguments = []; + } + M.f = f; +})(M || (M = {})); diff --git a/tests/baselines/reference/transpile/Supports setting alwaysStrict.js b/tests/baselines/reference/transpile/Supports setting alwaysStrict.js new file mode 100644 index 00000000000..8d91090453b --- /dev/null +++ b/tests/baselines/reference/transpile/Supports setting alwaysStrict.js @@ -0,0 +1,3 @@ +"use strict"; +x; +//# sourceMappingURL=input.js.map \ No newline at end of file diff --git a/tests/cases/compiler/alwaysStrict.ts b/tests/cases/compiler/alwaysStrict.ts new file mode 100644 index 00000000000..22ec09c6e38 --- /dev/null +++ b/tests/cases/compiler/alwaysStrict.ts @@ -0,0 +1,5 @@ +// @alwaysStrict: true + +function f() { + var arguments = []; +} \ No newline at end of file diff --git a/tests/cases/compiler/alwaysStrictES6.ts b/tests/cases/compiler/alwaysStrictES6.ts new file mode 100644 index 00000000000..edb542fa86a --- /dev/null +++ b/tests/cases/compiler/alwaysStrictES6.ts @@ -0,0 +1,6 @@ +// @target: ES6 +// @alwaysStrict: true + +function f() { + var arguments = []; +} \ No newline at end of file diff --git a/tests/cases/compiler/alwaysStrictModule.ts b/tests/cases/compiler/alwaysStrictModule.ts new file mode 100644 index 00000000000..b706f5869a6 --- /dev/null +++ b/tests/cases/compiler/alwaysStrictModule.ts @@ -0,0 +1,8 @@ +// @module: commonjs +// @alwaysStrict: true + +module M { + export function f() { + var arguments = []; + } +} \ No newline at end of file diff --git a/tests/cases/compiler/alwaysStrictNoImplicitUseStrict.ts b/tests/cases/compiler/alwaysStrictNoImplicitUseStrict.ts new file mode 100644 index 00000000000..d3173df8ab5 --- /dev/null +++ b/tests/cases/compiler/alwaysStrictNoImplicitUseStrict.ts @@ -0,0 +1,9 @@ +// @module: commonjs +// @alwaysStrict: true +// @noImplicitUseStrict: true + +module M { + export function f() { + var arguments = []; + } +} \ No newline at end of file From ea808f52fe3dd70dedc6294ebda26a4100767ad1 Mon Sep 17 00:00:00 2001 From: Slawomir Sadziak Date: Mon, 10 Oct 2016 12:15:34 +0200 Subject: [PATCH 2/3] Fix #10758 Add compiler option to parse in strict mode * add unit test to ensure "use strict" is not added twice * fix code --- src/compiler/factory.ts | 30 +++++++++++++++++++ src/compiler/transformers/ts.ts | 9 +----- .../reference/alwaysStrictAlreadyUseStrict.js | 11 +++++++ .../alwaysStrictAlreadyUseStrict.symbols | 8 +++++ .../alwaysStrictAlreadyUseStrict.types | 11 +++++++ .../compiler/alwaysStrictAlreadyUseStrict.ts | 5 ++++ 6 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/alwaysStrictAlreadyUseStrict.js create mode 100644 tests/baselines/reference/alwaysStrictAlreadyUseStrict.symbols create mode 100644 tests/baselines/reference/alwaysStrictAlreadyUseStrict.types create mode 100644 tests/cases/compiler/alwaysStrictAlreadyUseStrict.ts diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 97a36f46b13..6bf6259c364 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2235,6 +2235,36 @@ namespace ts { return statementOffset; } + /** + * Ensures "use strict" directive is added + * + * @param node source file + */ + export function ensureUseStrict(node: SourceFile): SourceFile { + let foundUseStrict = false; + let statementOffset = 0; + const numStatements = node.statements.length; + while (statementOffset < numStatements) { + const statement = node.statements[statementOffset]; + if (isPrologueDirective(statement)) { + if (isUseStrictPrologue(statement as ExpressionStatement)) { + foundUseStrict = true; + } + } + else { + break; + } + statementOffset++; + } + if (!foundUseStrict) { + const statements: Statement[] = []; + statements.push(startOnNewLine(createStatement(createLiteral("use strict")))); + // add "use strict" as the first statement + return updateSourceFileNode(node, statements.concat(node.statements)); + } + return node; + } + /** * Wraps the operand to a BinaryExpression in parentheses if they are needed to preserve the intended * order of operations. diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 7b6c28a8f41..112f838a370 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -438,7 +438,7 @@ namespace ts { // ensure "use strict"" is emitted in all scenarios in alwaysStrict mode if (compilerOptions.alwaysStrict) { - node = emitUseStrict(node); + node = ensureUseStrict(node); } // If the source file requires any helpers and is an external module, and @@ -477,13 +477,6 @@ namespace ts { return node; } - function emitUseStrict(node: SourceFile): SourceFile { - const statements: Statement[] = []; - statements.push(startOnNewLine(createStatement(createLiteral("use strict")))); - // add "use strict" as the first statement - return updateSourceFileNode(node, statements.concat(node.statements)); - } - /** * Tests whether we should emit a __decorate call for a class declaration. */ diff --git a/tests/baselines/reference/alwaysStrictAlreadyUseStrict.js b/tests/baselines/reference/alwaysStrictAlreadyUseStrict.js new file mode 100644 index 00000000000..cc3be45dedc --- /dev/null +++ b/tests/baselines/reference/alwaysStrictAlreadyUseStrict.js @@ -0,0 +1,11 @@ +//// [alwaysStrictAlreadyUseStrict.ts] +"use strict" +function f() { + var a = []; +} + +//// [alwaysStrictAlreadyUseStrict.js] +"use strict"; +function f() { + var a = []; +} diff --git a/tests/baselines/reference/alwaysStrictAlreadyUseStrict.symbols b/tests/baselines/reference/alwaysStrictAlreadyUseStrict.symbols new file mode 100644 index 00000000000..143f579b94d --- /dev/null +++ b/tests/baselines/reference/alwaysStrictAlreadyUseStrict.symbols @@ -0,0 +1,8 @@ +=== tests/cases/compiler/alwaysStrictAlreadyUseStrict.ts === +"use strict" +function f() { +>f : Symbol(f, Decl(alwaysStrictAlreadyUseStrict.ts, 0, 12)) + + var a = []; +>a : Symbol(a, Decl(alwaysStrictAlreadyUseStrict.ts, 2, 7)) +} diff --git a/tests/baselines/reference/alwaysStrictAlreadyUseStrict.types b/tests/baselines/reference/alwaysStrictAlreadyUseStrict.types new file mode 100644 index 00000000000..0181b40de27 --- /dev/null +++ b/tests/baselines/reference/alwaysStrictAlreadyUseStrict.types @@ -0,0 +1,11 @@ +=== tests/cases/compiler/alwaysStrictAlreadyUseStrict.ts === +"use strict" +>"use strict" : "use strict" + +function f() { +>f : () => void + + var a = []; +>a : any[] +>[] : undefined[] +} diff --git a/tests/cases/compiler/alwaysStrictAlreadyUseStrict.ts b/tests/cases/compiler/alwaysStrictAlreadyUseStrict.ts new file mode 100644 index 00000000000..1d804a6cf90 --- /dev/null +++ b/tests/cases/compiler/alwaysStrictAlreadyUseStrict.ts @@ -0,0 +1,5 @@ +// @alwaysStrict: true +"use strict" +function f() { + var a = []; +} \ No newline at end of file From 8210634e8dbc9a1bc41c108b21c19b4cfac4d91c Mon Sep 17 00:00:00 2001 From: Slawomir Sadziak Date: Mon, 10 Oct 2016 23:03:07 +0200 Subject: [PATCH 3/3] Fix #10758 Add compiler option to parse in strict mode * fix comment * optimize loop --- src/compiler/factory.ts | 7 ++----- src/compiler/transformers/ts.ts | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 6bf6259c364..5a95600d089 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2242,19 +2242,16 @@ namespace ts { */ export function ensureUseStrict(node: SourceFile): SourceFile { let foundUseStrict = false; - let statementOffset = 0; - const numStatements = node.statements.length; - while (statementOffset < numStatements) { - const statement = node.statements[statementOffset]; + for (const statement of node.statements) { if (isPrologueDirective(statement)) { if (isUseStrictPrologue(statement as ExpressionStatement)) { foundUseStrict = true; + break; } } else { break; } - statementOffset++; } if (!foundUseStrict) { const statements: Statement[] = []; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 112f838a370..2c146c91bc3 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -436,7 +436,7 @@ namespace ts { function visitSourceFile(node: SourceFile) { currentSourceFile = node; - // ensure "use strict"" is emitted in all scenarios in alwaysStrict mode + // ensure "use strict" is emitted in all scenarios in alwaysStrict mode if (compilerOptions.alwaysStrict) { node = ensureUseStrict(node); }