diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e4696363ebb..c8b0100a02e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14793,7 +14793,7 @@ namespace ts { return; } - if (findAncestor(node, node => node.kind === SyntaxKind.PropertyDeclaration ? true : isExpression(node) ? false : "quit") && + if (isInPropertyInitializer(node) && !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right) && !isPropertyDeclaredInAncestorClass(prop)) { error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, unescapeLeadingUnderscores(right.escapedText)); @@ -14806,6 +14806,20 @@ namespace ts { } } + function isInPropertyInitializer(node: Node): boolean { + return !!findAncestor(node, node => { + switch (node.kind) { + case SyntaxKind.PropertyDeclaration: + return true; + case SyntaxKind.PropertyAssignment: + // We might be in `a = { b: this.b }`, so keep looking. See `tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts`. + return false; + default: + return isPartOfExpression(node) ? false : "quit"; + } + }); + } + /** * It's possible that "prop.valueDeclaration" is a local declaration, but the property was also declared in a superclass. * In that case we won't consider it used before its declaration, because it gets its value from the superclass' declaration. diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index d89a4677ef9..fb5e9e0354e 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -22,10 +22,6 @@ namespace FourSlash { ts.disableIncrementalParsing = false; - function normalizeNewLines(s: string) { - return s.replace(/\r\n/g, "\n"); - } - // Represents a parsed source file with metadata export interface FourSlashFile { // The contents of the file (with markers, etc stripped out) @@ -364,7 +360,7 @@ namespace FourSlash { baseIndentSize: 0, indentSize: 4, tabSize: 4, - newLineCharacter: Harness.IO.newLine(), + newLineCharacter: "\n", convertTabsToSpaces: true, indentStyle: ts.IndentStyle.Smart, insertSpaceAfterCommaDelimiter: true, @@ -1603,7 +1599,7 @@ namespace FourSlash { } } - public printCurrentFileState(makeWhitespaceVisible: boolean, makeCaretVisible: boolean) { + public printCurrentFileState(showWhitespace: boolean, makeCaretVisible: boolean) { for (const file of this.testData.files) { const active = (this.activeFile === file); Harness.IO.log(`=== Script (${file.fileName}) ${(active ? "(active, cursor at |)" : "")} ===`); @@ -1611,8 +1607,8 @@ namespace FourSlash { if (active) { content = content.substr(0, this.currentCaretPosition) + (makeCaretVisible ? "|" : "") + content.substr(this.currentCaretPosition); } - if (makeWhitespaceVisible) { - content = TestState.makeWhitespaceVisible(content); + if (showWhitespace) { + content = makeWhitespaceVisible(content); } Harness.IO.log(content); } @@ -2128,10 +2124,8 @@ namespace FourSlash { public verifyCurrentFileContent(text: string) { const actual = this.getFileContent(this.activeFile.fileName); - if (normalizeNewLines(actual) !== normalizeNewLines(text)) { - throw new Error("verifyCurrentFileContent\n" + - "\tExpected: \"" + TestState.makeWhitespaceVisible(text) + "\"\n" + - "\t Actual: \"" + TestState.makeWhitespaceVisible(actual) + "\""); + if (actual !== text) { + throw new Error(`verifyCurrentFileContent failed:\n${showTextDiff(text, actual)}`); } } @@ -2305,11 +2299,11 @@ namespace FourSlash { const actualText = this.rangeText(ranges[0]); const result = includeWhiteSpace - ? normalizeNewLines(actualText) === normalizeNewLines(expectedText) + ? actualText === expectedText : this.removeWhitespace(actualText) === this.removeWhitespace(expectedText); if (!result) { - this.raiseError(`Actual text doesn't match expected text. Actual:\n'${actualText}'\nExpected:\n'${expectedText}'`); + this.raiseError(`Actual range text doesn't match expected text.\n${showTextDiff(expectedText, actualText)}`); } } @@ -2403,15 +2397,19 @@ namespace FourSlash { const originalContent = scriptInfo.content; for (const codeFix of codeFixes) { this.applyEdits(codeFix.changes[0].fileName, codeFix.changes[0].textChanges, /*isFormattingEdit*/ false); - actualTextArray.push(this.normalizeNewlines(this.rangeText(ranges[0]))); + let text = this.rangeText(ranges[0]); + // TODO:GH#18445 (remove this line to see errors in many `importNameCodeFix` tests) + text = text.replace(/\r\n/g, "\n"); + actualTextArray.push(text); scriptInfo.updateContent(originalContent); } - const sortedExpectedArray = ts.map(expectedTextArray, str => this.normalizeNewlines(str)).sort(); + const sortedExpectedArray = expectedTextArray.sort(); const sortedActualArray = actualTextArray.sort(); - if (!ts.arrayIsEqualTo(sortedExpectedArray, sortedActualArray)) { - this.raiseError( - `Actual text array doesn't match expected text array. \nActual: \n'${sortedActualArray.join("\n\n")}'\n---\nExpected: \n'${sortedExpectedArray.join("\n\n")}'`); - } + ts.zipWith(sortedExpectedArray, sortedActualArray, (expected, actual, index) => { + if (expected !== actual) { + this.raiseError(`Import fix at index ${index} doesn't match.\n${showTextDiff(expected, actual)}`); + } + }); } public verifyDocCommentTemplate(expected: ts.TextInsertion | undefined) { @@ -2431,7 +2429,7 @@ namespace FourSlash { } if (actual.newText !== expected.newText) { - this.raiseError(`${name} failed - expected insertion:\n"${this.clarifyNewlines(expected.newText)}"\nactual insertion:\n"${this.clarifyNewlines(actual.newText)}"`); + this.raiseError(`${name} failed for expected insertion.\n${showTextDiff(expected.newText, actual.newText)}`); } if (actual.caretOffset !== expected.caretOffset) { @@ -2440,17 +2438,6 @@ namespace FourSlash { } } - private clarifyNewlines(str: string) { - return str.replace(/\r?\n/g, lineEnding => { - const representation = lineEnding === "\r\n" ? "CRLF" : "LF"; - return "# - " + representation + lineEnding; - }); - } - - private normalizeNewlines(str: string) { - return str.replace(/\r?\n/g, "\n"); - } - public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) { const openBraceMap = ts.createMapFromTemplate({ @@ -2878,8 +2865,8 @@ namespace FourSlash { } const actualContent = this.getFileContent(this.activeFile.fileName); - if (this.normalizeNewlines(actualContent) !== this.normalizeNewlines(expectedContent)) { - this.raiseError(`verifyFileAfterApplyingRefactors failed: expected:\n${expectedContent}\nactual:\n${actualContent}`); + if (actualContent !== expectedContent) { + this.raiseError(`verifyFileAfterApplyingRefactors failed:\n${showTextDiff(expectedContent, actualContent)}`); } } @@ -3014,10 +3001,6 @@ namespace FourSlash { } } - private static makeWhitespaceVisible(text: string) { - return text.replace(/ /g, "\u00B7").replace(/\r/g, "\u00B6").replace(/\n/g, "\u2193\n").replace(/\t/g, "\u2192\ "); - } - public setCancelled(numberOfCalls: number): void { this.cancellationToken.setCancelled(numberOfCalls); } @@ -3319,12 +3302,7 @@ ${code} let column = 1; const flush = (lastSafeCharIndex: number) => { - if (lastSafeCharIndex === undefined) { - output = output + content.substr(lastNormalCharPosition); - } - else { - output = output + content.substr(lastNormalCharPosition, lastSafeCharIndex - lastNormalCharPosition); - } + output = output + content.substr(lastNormalCharPosition, lastSafeCharIndex === undefined ? undefined : lastSafeCharIndex - lastNormalCharPosition); }; if (content.length > 0) { @@ -3511,6 +3489,27 @@ ${code} function toArray(x: T | T[]): T[] { return ts.isArray(x) ? x : [x]; } + + function makeWhitespaceVisible(text: string) { + return text.replace(/ /g, "\u00B7").replace(/\r/g, "\u00B6").replace(/\n/g, "\u2193\n").replace(/\t/g, "\u2192\ "); + } + + function showTextDiff(expected: string, actual: string): string { + // Only show whitespace if the difference is whitespace-only. + if (differOnlyByWhitespace(expected, actual)) { + expected = makeWhitespaceVisible(expected); + actual = makeWhitespaceVisible(actual); + } + return `Expected:\n${expected}\nActual:${actual}`; + } + + function differOnlyByWhitespace(a: string, b: string) { + return stripWhitespace(a) === stripWhitespace(b); + } + + function stripWhitespace(s: string): string { + return s.replace(/\s/g, ""); + } } namespace FourSlashInterface { @@ -4143,15 +4142,15 @@ namespace FourSlashInterface { } public printCurrentFileState() { - this.state.printCurrentFileState(/*makeWhitespaceVisible*/ false, /*makeCaretVisible*/ true); + this.state.printCurrentFileState(/*showWhitespace*/ false, /*makeCaretVisible*/ true); } public printCurrentFileStateWithWhitespace() { - this.state.printCurrentFileState(/*makeWhitespaceVisible*/ true, /*makeCaretVisible*/ true); + this.state.printCurrentFileState(/*showWhitespace*/ true, /*makeCaretVisible*/ true); } public printCurrentFileStateWithoutCaret() { - this.state.printCurrentFileState(/*makeWhitespaceVisible*/ false, /*makeCaretVisible*/ false); + this.state.printCurrentFileState(/*showWhitespace*/ false, /*makeCaretVisible*/ false); } public printCurrentQuickInfo() { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 2fc1aac2d83..8344be36753 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -500,7 +500,8 @@ namespace Harness { export let IO: IO; // harness always uses one kind of new line - const harnessNewLine = "\r\n"; + // But note that `parseTestData` in `fourslash.ts` uses "\n" + export const harnessNewLine = "\r\n"; // Root for file paths that are stored in a virtual file system export const virtualFileSystemRoot = "/"; diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 78da05570d8..23bc08108c7 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -130,7 +130,7 @@ namespace Harness.LanguageService { } public getNewLine(): string { - return "\r\n"; + return harnessNewLine; } public getFilenames(): string[] { diff --git a/src/harness/runner.ts b/src/harness/runner.ts index 4653e440f11..6e1f91b21af 100644 --- a/src/harness/runner.ts +++ b/src/harness/runner.ts @@ -216,8 +216,8 @@ if (taskConfigsFolder) { for (let i = 0; i < workerCount; i++) { const config = workerConfigs[i]; - // use last worker to run unit tests - config.runUnitTests = i === workerCount - 1; + // use last worker to run unit tests if we're not just running a single specific runner + config.runUnitTests = runners.length !== 1 && i === workerCount - 1; Harness.IO.writeFile(ts.combinePaths(taskConfigsFolder, `task-config${i}.json`), JSON.stringify(workerConfigs[i])); } } diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index cddb9b19d4a..b3ef80b2a31 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -263,7 +263,7 @@ class RWCRunner extends RunnerBase { */ public initializeTests(): void { // Read in and evaluate the test list - const testList = this.enumerateTestFiles(); + const testList = this.tests && this.tests.length ? this.tests : this.enumerateTestFiles(); for (let i = 0; i < testList.length; i++) { this.runTest(testList[i]); } diff --git a/src/harness/unittests/extractMethods.ts b/src/harness/unittests/extractMethods.ts index c836698aeb4..5cab0289d67 100644 --- a/src/harness/unittests/extractMethods.ts +++ b/src/harness/unittests/extractMethods.ts @@ -404,6 +404,12 @@ function test(x: number) { "Cannot extract range containing conditional break or continue statements." ]); + testExtractRangeFailed("extractRangeFailed9", + `var x = ([#||]1 + 2);`, + [ + "Statement or expression expected." + ]); + testExtractMethod("extractMethod1", `namespace A { let x = 1; @@ -709,6 +715,57 @@ function M3() { }`); } M3() { } constructor() { } +}`); + // Shorthand property names + testExtractMethod("extractMethod29", + `interface UnaryExpression { + kind: "Unary"; + operator: string; + operand: any; +} + +function parseUnaryExpression(operator: string): UnaryExpression { + [#|return { + kind: "Unary", + operator, + operand: parsePrimaryExpression(), + };|] +} + +function parsePrimaryExpression(): any { + throw "Not implemented"; +}`); + // Type parameter as declared type + testExtractMethod("extractMethod30", + `function F() { + [#|let t: T;|] +}`); + // Return in nested function + testExtractMethod("extractMethod31", + `namespace N { + + export const value = 1; + + () => { + var f: () => number; + [#|f = function (): number { + return value; + }|] + } +}`); + // Return in nested class + testExtractMethod("extractMethod32", + `namespace N { + + export const value = 1; + + () => { + [#|var c = class { + M() { + return value; + } + }|] + } }`); }); diff --git a/src/server/client.ts b/src/server/client.ts index 0ffac42dae4..9e472a83e2b 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -573,7 +573,7 @@ namespace ts.server { getEditsForRefactor( fileName: string, - _formatOptions: FormatCodeSettings, + formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo { @@ -581,6 +581,7 @@ namespace ts.server { const args = this.createFileLocationOrRangeRequestArgs(positionOrRange, fileName) as protocol.GetEditsForRefactorRequestArgs; args.refactor = refactorName; args.action = actionName; + args.formatOptions = formatOptions; const request = this.processRequest(CommandNames.GetEditsForRefactor, args); const response = this.processResponse(request); diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 3fdbd8fd7f7..1740f8ae0ba 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -494,6 +494,7 @@ namespace ts.server.protocol { refactor: string; /* The 'name' property from the refactoring action */ action: string; + formatOptions: FormatCodeSettings, }; diff --git a/src/server/session.ts b/src/server/session.ts index 0b3038a408d..2d773d6f467 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1488,7 +1488,7 @@ namespace ts.server { const result = project.getLanguageService().getEditsForRefactor( file, - this.projectService.getFormatCodeOptions(), + convertFormatOptions(args.formatOptions), position || textRange, args.refactor, args.action diff --git a/src/services/refactors/extractMethod.ts b/src/services/refactors/extractMethod.ts index 7b5e1e40c7e..4c24d22a2f3 100644 --- a/src/services/refactors/extractMethod.ts +++ b/src/services/refactors/extractMethod.ts @@ -149,6 +149,11 @@ namespace ts.refactor.extractMethod { // exported only for tests export function getRangeToExtract(sourceFile: SourceFile, span: TextSpan): RangeToExtract { const length = span.length || 0; + + if (length === 0) { + return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.StatementOrExpressionExpected)] }; + } + // Walk up starting from the the start position until we find a non-SourceFile node that subsumes the selected span. // This may fail (e.g. you select two statements in the root of a source file) let start = getParentNodeInSpan(getTokenAtPosition(sourceFile, span.start, /*includeJsDocComment*/ false), sourceFile, span); @@ -855,6 +860,7 @@ namespace ts.refactor.extractMethod { return { body: createBlock(body.statements, /*multLine*/ true), returnValueProperty: undefined }; } let returnValueProperty: string; + let ignoreReturns = false; const statements = createNodeArray(isBlock(body) ? body.statements.slice(0) : [isStatement(body) ? body : createReturn(body)]); // rewrite body if either there are writes that should be propagated back via return statements or there are substitutions if (writes || substitutions.size) { @@ -877,7 +883,7 @@ namespace ts.refactor.extractMethod { } function visitor(node: Node): VisitResult { - if (node.kind === SyntaxKind.ReturnStatement && writes) { + if (!ignoreReturns && node.kind === SyntaxKind.ReturnStatement && writes) { const assignments: ObjectLiteralElementLike[] = getPropertyAssignmentsForWrites(writes); if ((node).expression) { if (!returnValueProperty) { @@ -893,8 +899,12 @@ namespace ts.refactor.extractMethod { } } else { + const oldIgnoreReturns = ignoreReturns; + ignoreReturns = ignoreReturns || isFunctionLike(node) || isClassLike(node); const substitution = substitutions.get(getNodeId(node).toString()); - return substitution || visitEachChild(node, visitor, nullTransformationContext); + const result = substitution || visitEachChild(node, visitor, nullTransformationContext); + ignoreReturns = oldIgnoreReturns; + return result; } } } @@ -1161,7 +1171,11 @@ namespace ts.refactor.extractMethod { } function recordUsagebySymbol(identifier: Identifier, usage: Usage, isTypeName: boolean) { - const symbol = checker.getSymbolAtLocation(identifier); + // If the identifier is both a property name and its value, we're only interested in its value + // (since the name is a declaration and will be included in the extracted range). + const symbol = identifier.parent && isShorthandPropertyAssignment(identifier.parent) && identifier.parent.name === identifier + ? checker.getShorthandAssignmentValueSymbol(identifier.parent) + : checker.getSymbolAtLocation(identifier); if (!symbol) { // cannot find symbol - do nothing return undefined; @@ -1218,7 +1232,11 @@ namespace ts.refactor.extractMethod { substitutionsPerScope[i].set(symbolId, substitution); } else if (isTypeName) { - errorsPerScope[i].push(createDiagnosticForNode(identifier, Messages.TypeWillNotBeVisibleInTheNewScope)); + // If the symbol is a type parameter that won't be in scope, we'll pass it as a type argument + // so there's no problem. + if (!(symbol.flags & SymbolFlags.TypeParameter)) { + errorsPerScope[i].push(createDiagnosticForNode(identifier, Messages.TypeWillNotBeVisibleInTheNewScope)); + } } else { usagesPerScope[i].usages.set(identifier.text as string, { usage, symbol, node: identifier }); diff --git a/src/services/services.ts b/src/services/services.ts index 197f76b607f..bdd0eb41e4f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2019,7 +2019,7 @@ namespace ts { startPosition, endPosition, program: getProgram(), - newLineCharacter: host.getNewLine(), + newLineCharacter: formatOptions ? formatOptions.newLineCharacter : host.getNewLine(), rulesProvider: getRuleProvider(formatOptions), cancellationToken }; diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 42c1d1e9a4f..f3ad7df1607 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -184,16 +184,12 @@ namespace ts.textChanges { return s; } - function getNewlineKind(context: { newLineCharacter: string }) { - return context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed; - } - export class ChangeTracker { private changes: Change[] = []; private readonly newLineCharacter: string; public static fromContext(context: RefactorContext | CodeFixContext) { - return new ChangeTracker(getNewlineKind(context), context.rulesProvider); + return new ChangeTracker(context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.rulesProvider); } constructor( diff --git a/tests/baselines/reference/extractMethod/extractMethod29.ts b/tests/baselines/reference/extractMethod/extractMethod29.ts new file mode 100644 index 00000000000..aa7004d254e --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod29.ts @@ -0,0 +1,62 @@ +// ==ORIGINAL== +interface UnaryExpression { + kind: "Unary"; + operator: string; + operand: any; +} + +function parseUnaryExpression(operator: string): UnaryExpression { + return { + kind: "Unary", + operator, + operand: parsePrimaryExpression(), + }; +} + +function parsePrimaryExpression(): any { + throw "Not implemented"; +} +// ==SCOPE::inner function in function 'parseUnaryExpression'== +interface UnaryExpression { + kind: "Unary"; + operator: string; + operand: any; +} + +function parseUnaryExpression(operator: string): UnaryExpression { + return /*RENAME*/newFunction(); + + function newFunction() { + return { + kind: "Unary", + operator, + operand: parsePrimaryExpression(), + }; + } +} + +function parsePrimaryExpression(): any { + throw "Not implemented"; +} +// ==SCOPE::function in global scope== +interface UnaryExpression { + kind: "Unary"; + operator: string; + operand: any; +} + +function parseUnaryExpression(operator: string): UnaryExpression { + return /*RENAME*/newFunction(operator); +} + +function newFunction(operator: string) { + return { + kind: "Unary", + operator, + operand: parsePrimaryExpression(), + }; +} + +function parsePrimaryExpression(): any { + throw "Not implemented"; +} \ No newline at end of file diff --git a/tests/baselines/reference/extractMethod/extractMethod30.ts b/tests/baselines/reference/extractMethod/extractMethod30.ts new file mode 100644 index 00000000000..67dc1208abc --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod30.ts @@ -0,0 +1,19 @@ +// ==ORIGINAL== +function F() { + let t: T; +} +// ==SCOPE::inner function in function 'F'== +function F() { + /*RENAME*/newFunction(); + + function newFunction() { + let t: T; + } +} +// ==SCOPE::function in global scope== +function F() { + /*RENAME*/newFunction(); +} +function newFunction() { + let t: T; +} diff --git a/tests/baselines/reference/extractMethod/extractMethod31.ts b/tests/baselines/reference/extractMethod/extractMethod31.ts new file mode 100644 index 00000000000..754814dcc26 --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod31.ts @@ -0,0 +1,45 @@ +// ==ORIGINAL== +namespace N { + + export const value = 1; + + () => { + var f: () => number; + f = function (): number { + return value; + } + } +} +// ==SCOPE::function in namespace 'N'== +namespace N { + + export const value = 1; + + () => { + var f: () => number; + f = /*RENAME*/newFunction(f); + } + + function newFunction(f: () => number) { + f = function(): number { + return value; + }; + return f; + } +} +// ==SCOPE::function in global scope== +namespace N { + + export const value = 1; + + () => { + var f: () => number; + f = /*RENAME*/newFunction(f); + } +} +function newFunction(f: () => number) { + f = function(): number { + return N.value; + }; + return f; +} diff --git a/tests/baselines/reference/extractMethod/extractMethod32.ts b/tests/baselines/reference/extractMethod/extractMethod32.ts new file mode 100644 index 00000000000..b9b870a08fa --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod32.ts @@ -0,0 +1,46 @@ +// ==ORIGINAL== +namespace N { + + export const value = 1; + + () => { + var c = class { + M() { + return value; + } + } + } +} +// ==SCOPE::function in namespace 'N'== +namespace N { + + export const value = 1; + + () => { + /*RENAME*/newFunction(); + } + + function newFunction() { + var c = class { + M() { + return value; + } + }; + } +} +// ==SCOPE::function in global scope== +namespace N { + + export const value = 1; + + () => { + /*RENAME*/newFunction(); + } +} +function newFunction() { + var c = class { + M() { + return N.value; + } + }; +} diff --git a/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.errors.txt b/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.errors.txt new file mode 100644 index 00000000000..f237fd07c80 --- /dev/null +++ b/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.errors.txt @@ -0,0 +1,11 @@ +tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts(2,27): error TS2448: Block-scoped variable 'b' used before its declaration. + + +==== tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts (1 errors) ==== + export class C { + public a = { b: this.b }; + ~ +!!! error TS2448: Block-scoped variable 'b' used before its declaration. + private b = 0; + } + \ No newline at end of file diff --git a/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.js b/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.js new file mode 100644 index 00000000000..e34604e0a4e --- /dev/null +++ b/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.js @@ -0,0 +1,18 @@ +//// [useBeforeDeclaration_propertyAssignment.ts] +export class C { + public a = { b: this.b }; + private b = 0; +} + + +//// [useBeforeDeclaration_propertyAssignment.js] +"use strict"; +exports.__esModule = true; +var C = /** @class */ (function () { + function C() { + this.a = { b: this.b }; + this.b = 0; + } + return C; +}()); +exports.C = C; diff --git a/tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts b/tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts new file mode 100644 index 00000000000..bdd84473c57 --- /dev/null +++ b/tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts @@ -0,0 +1,4 @@ +export class C { + public a = { b: this.b }; + private b = 0; +} diff --git a/tests/cases/fourslash/codeFixAddMissingMember5.ts b/tests/cases/fourslash/codeFixAddMissingMember5.ts index db893cb61d3..44b11c5141b 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember5.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember5.ts @@ -17,5 +17,4 @@ verify.currentFileContentIs(`class C { ()=>{ this.foo === 10 }; } } -C.foo = undefined; -`); \ No newline at end of file +C.foo = undefined;` + "\r\n"); // TODO: GH#18445 diff --git a/tests/cases/fourslash/codeFixAddMissingMember7.ts b/tests/cases/fourslash/codeFixAddMissingMember7.ts index 8ac7f2b5aff..7690023815c 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember7.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember7.ts @@ -13,5 +13,4 @@ verify.getAndApplyCodeFix(/*errorCode*/ undefined, /*index*/ 2) verify.currentFileContentIs(`class C { static p = ()=>{ this.foo === 10 }; } -C.foo = undefined; -`); +C.foo = undefined;` + "\r\n"); // TODO: GH#18445 diff --git a/tests/cases/fourslash/codeFixSuperAfterThis.ts b/tests/cases/fourslash/codeFixSuperAfterThis.ts index 55b44b07881..a4db2c909fd 100644 --- a/tests/cases/fourslash/codeFixSuperAfterThis.ts +++ b/tests/cases/fourslash/codeFixSuperAfterThis.ts @@ -9,7 +9,8 @@ //// super(); //// |]} ////} +// TODO: GH#18445 verify.rangeAfterCodeFix(` - super(); + super();\r this.a = 12; - `, /*includeWhiteSpace*/ true); \ No newline at end of file + `, /*includeWhiteSpace*/ true); diff --git a/tests/cases/fourslash/codeFixSuperCall.ts b/tests/cases/fourslash/codeFixSuperCall.ts index f7584050a15..28f7d34a2bd 100644 --- a/tests/cases/fourslash/codeFixSuperCall.ts +++ b/tests/cases/fourslash/codeFixSuperCall.ts @@ -6,6 +6,7 @@ //// constructor() {[| //// |]} ////} +// TODO: GH#18445 verify.rangeAfterCodeFix(` - super(); + super();\r `, /*includeWhitespace*/ true); diff --git a/tests/cases/fourslash/extract-method14.ts b/tests/cases/fourslash/extract-method14.ts index 27a561743c6..e2b58a36450 100644 --- a/tests/cases/fourslash/extract-method14.ts +++ b/tests/cases/fourslash/extract-method14.ts @@ -19,7 +19,7 @@ edit.applyRefactor({ `function foo() { var i = 10; var __return: any; - ({ __return, i } = n/*RENAME*/ewFunction(i)); + ({ __return, i } = /*RENAME*/newFunction(i)); return __return; } function newFunction(i) { diff --git a/tests/cases/fourslash/formatConflictDiff3Marker1.ts b/tests/cases/fourslash/formatConflictDiff3Marker1.ts index f6492a6f60c..188fe5b0dd2 100644 --- a/tests/cases/fourslash/formatConflictDiff3Marker1.ts +++ b/tests/cases/fourslash/formatConflictDiff3Marker1.ts @@ -11,12 +11,12 @@ ////} format.document(); -verify.currentFileContentIs("class C {\r\n\ -<<<<<<< HEAD\r\n\ - v = 1;\r\n\ -||||||| merged common ancestors\r\n\ -v = 3;\r\n\ -=======\r\n\ -v = 2;\r\n\ ->>>>>>> Branch - a\r\n\ -}"); \ No newline at end of file +verify.currentFileContentIs(`class C { +<<<<<<< HEAD + v = 1; +||||||| merged common ancestors +v = 3; +======= +v = 2; +>>>>>>> Branch - a +}`); diff --git a/tests/cases/fourslash/formatConflictMarker1.ts b/tests/cases/fourslash/formatConflictMarker1.ts index c0e2dbc4f69..413206dc893 100644 --- a/tests/cases/fourslash/formatConflictMarker1.ts +++ b/tests/cases/fourslash/formatConflictMarker1.ts @@ -9,10 +9,10 @@ ////} format.document(); -verify.currentFileContentIs("class C {\r\n\ -<<<<<<< HEAD\r\n\ - v = 1;\r\n\ -=======\r\n\ -v = 2;\r\n\ ->>>>>>> Branch - a\r\n\ -}"); \ No newline at end of file +verify.currentFileContentIs(`class C { +<<<<<<< HEAD + v = 1; +======= +v = 2; +>>>>>>> Branch - a +}`); diff --git a/tests/cases/fourslash/importNameCodeFixReExport.ts b/tests/cases/fourslash/importNameCodeFixReExport.ts index c77d32cc458..eb1c1a91343 100644 --- a/tests/cases/fourslash/importNameCodeFixReExport.ts +++ b/tests/cases/fourslash/importNameCodeFixReExport.ts @@ -10,7 +10,8 @@ ////x;|] goTo.file("/b.ts"); -verify.rangeAfterCodeFix(`import { x } from "./a"; - +// TODO:GH#18445 +verify.rangeAfterCodeFix(`import { x } from "./a";\r +\r export { x } from "./a"; x;`, /*includeWhiteSpace*/ true);