From 6c59dc34acb84a9cd8b5d28a384a98bbc6dccab6 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 18 Nov 2019 18:03:37 -0800 Subject: [PATCH] More tests for super.method call chain, improve vary-by (#35013) --- src/harness/harness.ts | 85 +++++++++++++++++-- src/testRunner/compilerRunner.ts | 26 +++++- .../callChainWithSuper(target=es2016).js | 18 ++++ .../callChainWithSuper(target=es2017).js | 18 ++++ .../callChainWithSuper(target=es2018).js | 18 ++++ .../callChainWithSuper(target=es2019).js | 18 ++++ .../callChainWithSuper(target=es2020).js | 18 ++++ .../callChainWithSuper(target=es5).js | 39 +++++++++ .../callChainWithSuper(target=es6).js | 18 ++++ .../callChainWithSuper(target=esnext).js | 18 ++++ ...tionalMethodDeclarations(target=es2016).js | 13 +++ ...tionalMethodDeclarations(target=esnext).js | 13 +++ .../optionalMethodDeclarations.ts | 8 ++ .../callChain/callChainWithSuper.ts | 10 +++ 14 files changed, 314 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/callChainWithSuper(target=es2016).js create mode 100644 tests/baselines/reference/callChainWithSuper(target=es2017).js create mode 100644 tests/baselines/reference/callChainWithSuper(target=es2018).js create mode 100644 tests/baselines/reference/callChainWithSuper(target=es2019).js create mode 100644 tests/baselines/reference/callChainWithSuper(target=es2020).js create mode 100644 tests/baselines/reference/callChainWithSuper(target=es5).js create mode 100644 tests/baselines/reference/callChainWithSuper(target=es6).js create mode 100644 tests/baselines/reference/callChainWithSuper(target=esnext).js create mode 100644 tests/baselines/reference/optionalMethodDeclarations(target=es2016).js create mode 100644 tests/baselines/reference/optionalMethodDeclarations(target=esnext).js create mode 100644 tests/cases/conformance/classes/methodDeclarations/optionalMethodDeclarations.ts create mode 100644 tests/cases/conformance/expressions/optionalChaining/callChain/callChainWithSuper.ts diff --git a/src/harness/harness.ts b/src/harness/harness.ts index e93033e126b..198e79dd9c3 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1412,10 +1412,65 @@ namespace Harness { [key: string]: string; } - function splitVaryBySettingValue(text: string): string[] | undefined { + function splitVaryBySettingValue(text: string, varyBy: string): string[] | undefined { if (!text) return undefined; - const entries = text.split(/,/).map(s => s.trim().toLowerCase()).filter(s => s.length > 0); - return entries && entries.length > 1 ? entries : undefined; + + let star = false; + const includes: string[] = []; + const excludes: string[] = []; + for (let s of text.split(/,/g)) { + s = s.trim().toLowerCase(); + if (s.length === 0) continue; + if (s === "*") { + star = true; + } + else if (ts.startsWith(s, "-") || ts.startsWith(s, "!")) { + excludes.push(s.slice(1)); + } + else { + includes.push(s); + } + } + + // do nothing if the setting has no variations + if (includes.length <= 1 && !star && excludes.length === 0) { + return undefined; + } + + const variations: { key: string, value?: string | number }[] = []; + const values = getVaryByStarSettingValues(varyBy); + + // add (and deduplicate) all included entries + for (const include of includes) { + const value = values?.get(include); + if (ts.findIndex(variations, v => v.key === include || value !== undefined && v.value === value) === -1) { + variations.push({ key: include, value }); + } + } + + if (star && values) { + // add all entries + for (const [key, value] of ts.arrayFrom(values.entries())) { + if (ts.findIndex(variations, v => v.key === key || v.value === value) === -1) { + variations.push({ key, value }); + } + } + } + + // remove all excluded entries + for (const exclude of excludes) { + const value = values?.get(exclude); + let index: number; + while ((index = ts.findIndex(variations, v => v.key === exclude || value !== undefined && v.value === value)) >= 0) { + ts.orderedRemoveItemAt(variations, index); + } + } + + if (variations.length === 0) { + throw new Error(`Variations in test option '@${varyBy}' resulted in an empty set.`); + } + + return ts.map(variations, v => v.key); } function computeFileBasedTestConfigurationVariations(configurations: FileBasedTestConfiguration[], variationState: FileBasedTestConfiguration, varyByEntries: [string, string[]][], offset: number) { @@ -1433,17 +1488,37 @@ namespace Harness { } } + let booleanVaryByStarSettingValues: ts.Map | undefined; + + function getVaryByStarSettingValues(varyBy: string): ts.ReadonlyMap | undefined { + const option = ts.forEach(ts.optionDeclarations, decl => ts.equateStringsCaseInsensitive(decl.name, varyBy) ? decl : undefined); + if (option) { + if (typeof option.type === "object") { + return option.type; + } + if (option.type === "boolean") { + return booleanVaryByStarSettingValues || (booleanVaryByStarSettingValues = ts.createMapFromTemplate({ + true: 1, + false: 0 + })); + } + } + } + /** * Compute FileBasedTestConfiguration variations based on a supplied list of variable settings. */ - export function getFileBasedTestConfigurations(settings: TestCaseParser.CompilerSettings, varyBy: string[]): FileBasedTestConfiguration[] | undefined { + export function getFileBasedTestConfigurations(settings: TestCaseParser.CompilerSettings, varyBy: readonly string[]): FileBasedTestConfiguration[] | undefined { let varyByEntries: [string, string[]][] | undefined; + let variationCount = 1; for (const varyByKey of varyBy) { if (ts.hasProperty(settings, varyByKey)) { // we only consider variations when there are 2 or more variable entries. - const entries = splitVaryBySettingValue(settings[varyByKey]); + const entries = splitVaryBySettingValue(settings[varyByKey], varyByKey); if (entries) { if (!varyByEntries) varyByEntries = []; + variationCount *= entries.length; + if (variationCount > 25) throw new Error(`Provided test options exceeded the maximum number of variations: ${varyBy.map(v => `'@${v}'`).join(", ")}`); varyByEntries.push([varyByKey, entries]); } } diff --git a/src/testRunner/compilerRunner.ts b/src/testRunner/compilerRunner.ts index e922fab5bcf..2a99b34d5a0 100644 --- a/src/testRunner/compilerRunner.ts +++ b/src/testRunner/compilerRunner.ts @@ -112,6 +112,30 @@ class CompilerBaselineRunner extends RunnerBase { } class CompilerTest { + private static varyBy: readonly string[] = [ + "module", + "target", + "jsx", + "removeComments", + "importHelpers", + "importHelpers", + "downlevelIteration", + "isolatedModules", + "strict", + "noImplicitAny", + "strictNullChecks", + "strictFunctionTypes", + "strictBindCallApply", + "strictPropertyInitialization", + "noImplicitThis", + "alwaysStrict", + "allowSyntheticDefaultImports", + "esModuleInterop", + "emitDecoratorMetadata", + "skipDefaultLibCheck", + "preserveConstEnums", + "skipLibCheck", + ]; private fileName: string; private justName: string; private configuredName: string; @@ -220,7 +244,7 @@ class CompilerTest { // also see `parseCompilerTestConfigurations` in tests/webTestServer.ts const content = Harness.IO.readFile(file)!; const settings = Harness.TestCaseParser.extractCompilerSettings(content); - const configurations = Harness.getFileBasedTestConfigurations(settings, /*varyBy*/ ["module", "target"]); + const configurations = Harness.getFileBasedTestConfigurations(settings, CompilerTest.varyBy); return { file, configurations, content }; } diff --git a/tests/baselines/reference/callChainWithSuper(target=es2016).js b/tests/baselines/reference/callChainWithSuper(target=es2016).js new file mode 100644 index 00000000000..fcfbb5db553 --- /dev/null +++ b/tests/baselines/reference/callChainWithSuper(target=es2016).js @@ -0,0 +1,18 @@ +//// [callChainWithSuper.ts] +// GH#34952 +class Base { method?() {} } +class Derived extends Base { + method1() { return super.method?.(); } + method2() { return super["method"]?.(); } +} + +//// [callChainWithSuper.js] +"use strict"; +// GH#34952 +class Base { + method() { } +} +class Derived extends Base { + method1() { var _a; return (_a = super.method) === null || _a === void 0 ? void 0 : _a.call(this); } + method2() { var _a; return (_a = super["method"]) === null || _a === void 0 ? void 0 : _a.call(this); } +} diff --git a/tests/baselines/reference/callChainWithSuper(target=es2017).js b/tests/baselines/reference/callChainWithSuper(target=es2017).js new file mode 100644 index 00000000000..fcfbb5db553 --- /dev/null +++ b/tests/baselines/reference/callChainWithSuper(target=es2017).js @@ -0,0 +1,18 @@ +//// [callChainWithSuper.ts] +// GH#34952 +class Base { method?() {} } +class Derived extends Base { + method1() { return super.method?.(); } + method2() { return super["method"]?.(); } +} + +//// [callChainWithSuper.js] +"use strict"; +// GH#34952 +class Base { + method() { } +} +class Derived extends Base { + method1() { var _a; return (_a = super.method) === null || _a === void 0 ? void 0 : _a.call(this); } + method2() { var _a; return (_a = super["method"]) === null || _a === void 0 ? void 0 : _a.call(this); } +} diff --git a/tests/baselines/reference/callChainWithSuper(target=es2018).js b/tests/baselines/reference/callChainWithSuper(target=es2018).js new file mode 100644 index 00000000000..fcfbb5db553 --- /dev/null +++ b/tests/baselines/reference/callChainWithSuper(target=es2018).js @@ -0,0 +1,18 @@ +//// [callChainWithSuper.ts] +// GH#34952 +class Base { method?() {} } +class Derived extends Base { + method1() { return super.method?.(); } + method2() { return super["method"]?.(); } +} + +//// [callChainWithSuper.js] +"use strict"; +// GH#34952 +class Base { + method() { } +} +class Derived extends Base { + method1() { var _a; return (_a = super.method) === null || _a === void 0 ? void 0 : _a.call(this); } + method2() { var _a; return (_a = super["method"]) === null || _a === void 0 ? void 0 : _a.call(this); } +} diff --git a/tests/baselines/reference/callChainWithSuper(target=es2019).js b/tests/baselines/reference/callChainWithSuper(target=es2019).js new file mode 100644 index 00000000000..fcfbb5db553 --- /dev/null +++ b/tests/baselines/reference/callChainWithSuper(target=es2019).js @@ -0,0 +1,18 @@ +//// [callChainWithSuper.ts] +// GH#34952 +class Base { method?() {} } +class Derived extends Base { + method1() { return super.method?.(); } + method2() { return super["method"]?.(); } +} + +//// [callChainWithSuper.js] +"use strict"; +// GH#34952 +class Base { + method() { } +} +class Derived extends Base { + method1() { var _a; return (_a = super.method) === null || _a === void 0 ? void 0 : _a.call(this); } + method2() { var _a; return (_a = super["method"]) === null || _a === void 0 ? void 0 : _a.call(this); } +} diff --git a/tests/baselines/reference/callChainWithSuper(target=es2020).js b/tests/baselines/reference/callChainWithSuper(target=es2020).js new file mode 100644 index 00000000000..fcfbb5db553 --- /dev/null +++ b/tests/baselines/reference/callChainWithSuper(target=es2020).js @@ -0,0 +1,18 @@ +//// [callChainWithSuper.ts] +// GH#34952 +class Base { method?() {} } +class Derived extends Base { + method1() { return super.method?.(); } + method2() { return super["method"]?.(); } +} + +//// [callChainWithSuper.js] +"use strict"; +// GH#34952 +class Base { + method() { } +} +class Derived extends Base { + method1() { var _a; return (_a = super.method) === null || _a === void 0 ? void 0 : _a.call(this); } + method2() { var _a; return (_a = super["method"]) === null || _a === void 0 ? void 0 : _a.call(this); } +} diff --git a/tests/baselines/reference/callChainWithSuper(target=es5).js b/tests/baselines/reference/callChainWithSuper(target=es5).js new file mode 100644 index 00000000000..5487991bc29 --- /dev/null +++ b/tests/baselines/reference/callChainWithSuper(target=es5).js @@ -0,0 +1,39 @@ +//// [callChainWithSuper.ts] +// GH#34952 +class Base { method?() {} } +class Derived extends Base { + method1() { return super.method?.(); } + method2() { return super["method"]?.(); } +} + +//// [callChainWithSuper.js] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +// GH#34952 +var Base = /** @class */ (function () { + function Base() { + } + Base.prototype.method = function () { }; + return Base; +}()); +var Derived = /** @class */ (function (_super) { + __extends(Derived, _super); + function Derived() { + return _super !== null && _super.apply(this, arguments) || this; + } + Derived.prototype.method1 = function () { var _a; return (_a = _super.prototype.method) === null || _a === void 0 ? void 0 : _a.call(this); }; + Derived.prototype.method2 = function () { var _a; return (_a = _super.prototype["method"]) === null || _a === void 0 ? void 0 : _a.call(this); }; + return Derived; +}(Base)); diff --git a/tests/baselines/reference/callChainWithSuper(target=es6).js b/tests/baselines/reference/callChainWithSuper(target=es6).js new file mode 100644 index 00000000000..fcfbb5db553 --- /dev/null +++ b/tests/baselines/reference/callChainWithSuper(target=es6).js @@ -0,0 +1,18 @@ +//// [callChainWithSuper.ts] +// GH#34952 +class Base { method?() {} } +class Derived extends Base { + method1() { return super.method?.(); } + method2() { return super["method"]?.(); } +} + +//// [callChainWithSuper.js] +"use strict"; +// GH#34952 +class Base { + method() { } +} +class Derived extends Base { + method1() { var _a; return (_a = super.method) === null || _a === void 0 ? void 0 : _a.call(this); } + method2() { var _a; return (_a = super["method"]) === null || _a === void 0 ? void 0 : _a.call(this); } +} diff --git a/tests/baselines/reference/callChainWithSuper(target=esnext).js b/tests/baselines/reference/callChainWithSuper(target=esnext).js new file mode 100644 index 00000000000..30e1ad3886d --- /dev/null +++ b/tests/baselines/reference/callChainWithSuper(target=esnext).js @@ -0,0 +1,18 @@ +//// [callChainWithSuper.ts] +// GH#34952 +class Base { method?() {} } +class Derived extends Base { + method1() { return super.method?.(); } + method2() { return super["method"]?.(); } +} + +//// [callChainWithSuper.js] +"use strict"; +// GH#34952 +class Base { + method() { } +} +class Derived extends Base { + method1() { return super.method?.(); } + method2() { return super["method"]?.(); } +} diff --git a/tests/baselines/reference/optionalMethodDeclarations(target=es2016).js b/tests/baselines/reference/optionalMethodDeclarations(target=es2016).js new file mode 100644 index 00000000000..2b37bff7d80 --- /dev/null +++ b/tests/baselines/reference/optionalMethodDeclarations(target=es2016).js @@ -0,0 +1,13 @@ +//// [optionalMethodDeclarations.ts] +// https://github.com/microsoft/TypeScript/issues/34952#issuecomment-552025027 +class C { + // ? should be removed in emit + method?() {} +} + +//// [optionalMethodDeclarations.js] +// https://github.com/microsoft/TypeScript/issues/34952#issuecomment-552025027 +class C { + // ? should be removed in emit + method() { } +} diff --git a/tests/baselines/reference/optionalMethodDeclarations(target=esnext).js b/tests/baselines/reference/optionalMethodDeclarations(target=esnext).js new file mode 100644 index 00000000000..2b37bff7d80 --- /dev/null +++ b/tests/baselines/reference/optionalMethodDeclarations(target=esnext).js @@ -0,0 +1,13 @@ +//// [optionalMethodDeclarations.ts] +// https://github.com/microsoft/TypeScript/issues/34952#issuecomment-552025027 +class C { + // ? should be removed in emit + method?() {} +} + +//// [optionalMethodDeclarations.js] +// https://github.com/microsoft/TypeScript/issues/34952#issuecomment-552025027 +class C { + // ? should be removed in emit + method() { } +} diff --git a/tests/cases/conformance/classes/methodDeclarations/optionalMethodDeclarations.ts b/tests/cases/conformance/classes/methodDeclarations/optionalMethodDeclarations.ts new file mode 100644 index 00000000000..d1bd1a8d2d0 --- /dev/null +++ b/tests/cases/conformance/classes/methodDeclarations/optionalMethodDeclarations.ts @@ -0,0 +1,8 @@ +// @target: esnext,es2016 +// @noTypesAndSymbols: true + +// https://github.com/microsoft/TypeScript/issues/34952#issuecomment-552025027 +class C { + // ? should be removed in emit + method?() {} +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/optionalChaining/callChain/callChainWithSuper.ts b/tests/cases/conformance/expressions/optionalChaining/callChain/callChainWithSuper.ts new file mode 100644 index 00000000000..d66c71b1c3a --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/callChain/callChainWithSuper.ts @@ -0,0 +1,10 @@ +// @target: *,-es3 +// @strict: true +// @noTypesAndSymbols: true + +// GH#34952 +class Base { method?() {} } +class Derived extends Base { + method1() { return super.method?.(); } + method2() { return super["method"]?.(); } +} \ No newline at end of file