From 2589f04902ff873c0740272a0c49962ab7042be9 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 10 Jan 2025 10:28:54 -0700 Subject: [PATCH] Changed code that computes unescaped literal string type to convert CRLF to LF within multi-line triple-quoted strings so the behavior is consistent with runtime. This addresses #9681. --- .../pyright-internal/src/parser/parser.ts | 5 +- .../src/parser/stringTokenUtils.ts | 6 ++- .../pyright-internal/src/tests/testUtils.ts | 54 +++++++++++-------- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/packages/pyright-internal/src/parser/parser.ts b/packages/pyright-internal/src/parser/parser.ts index 218fb66cd594..03bf4a9bba66 100644 --- a/packages/pyright-internal/src/parser/parser.ts +++ b/packages/pyright-internal/src/parser/parser.ts @@ -5034,7 +5034,10 @@ export class Parser { } } else { const stringToken = stringNode.d.strings[0].d.token; - const stringValue = StringTokenUtils.getUnescapedString(stringNode.d.strings[0].d.token); + const stringValue = StringTokenUtils.getUnescapedString( + stringNode.d.strings[0].d.token, + /* elideCrlf */ false + ); const unescapedString = stringValue.value; const tokenOffset = stringToken.start; const prefixLength = stringToken.prefixLength + stringToken.quoteMarkLength; diff --git a/packages/pyright-internal/src/parser/stringTokenUtils.ts b/packages/pyright-internal/src/parser/stringTokenUtils.ts index c53dda0dd96b..975d5c5bcfbf 100644 --- a/packages/pyright-internal/src/parser/stringTokenUtils.ts +++ b/packages/pyright-internal/src/parser/stringTokenUtils.ts @@ -51,7 +51,7 @@ function completeUnescapedString(incomplete: IncompleteUnescapedString, original }; } -export function getUnescapedString(stringToken: StringToken | FStringMiddleToken): UnescapedString { +export function getUnescapedString(stringToken: StringToken | FStringMiddleToken, elideCrlf = true): UnescapedString { const escapedString = stringToken.escapedValue; const isRaw = (stringToken.flags & StringTokenFlags.Raw) !== 0; @@ -305,7 +305,9 @@ export function getUnescapedString(stringToken: StringToken | FStringMiddleToken } else if (curChar === Char.LineFeed || curChar === Char.CarriageReturn) { // Skip over the escaped new line (either one or two characters). if (curChar === Char.CarriageReturn && getEscapedCharacter(1) === Char.LineFeed) { - appendOutputChar(curChar); + if (!elideCrlf) { + appendOutputChar(curChar); + } strOffset++; curChar = getEscapedCharacter(); } diff --git a/packages/pyright-internal/src/tests/testUtils.ts b/packages/pyright-internal/src/tests/testUtils.ts index e68630c00976..e816f4470dc1 100644 --- a/packages/pyright-internal/src/tests/testUtils.ts +++ b/packages/pyright-internal/src/tests/testUtils.ts @@ -166,22 +166,6 @@ export function getAnalysisResults( }); } -export function printDiagnostics(fileResults: FileAnalysisResult) { - if (fileResults.errors.length > 0) { - console.error(`Errors in ${fileResults.fileUri}:`); - for (const diag of fileResults.errors) { - console.error(` ${diag.message}`); - } - } - - if (fileResults.warnings.length > 0) { - console.error(`Warnings in ${fileResults.fileUri}:`); - for (const diag of fileResults.warnings) { - console.error(` ${diag.message}`); - } - } -} - export function validateResults( results: FileAnalysisResult[], errorCount: number, @@ -192,22 +176,48 @@ export function validateResults( deprecated?: number ) { assert.strictEqual(results.length, 1); - assert.strictEqual(results[0].errors.length, errorCount); - assert.strictEqual(results[0].warnings.length, warningCount); + + if (results[0].errors.length !== errorCount) { + logDiagnostics(results[0].errors); + assert.fail(`Expected ${errorCount} errors, got ${results[0].errors.length}`); + } + + if (results[0].warnings.length !== warningCount) { + logDiagnostics(results[0].warnings); + assert.fail(`Expected ${warningCount} warnings, got ${results[0].warnings.length}`); + } if (infoCount !== undefined) { - assert.strictEqual(results[0].infos.length, infoCount); + if (results[0].infos.length !== infoCount) { + logDiagnostics(results[0].infos); + assert.fail(`Expected ${infoCount} infos, got ${results[0].infos.length}`); + } } if (unusedCode !== undefined) { - assert.strictEqual(results[0].unusedCodes.length, unusedCode); + if (results[0].unusedCodes.length !== unusedCode) { + logDiagnostics(results[0].unusedCodes); + assert.fail(`Expected ${unusedCode} unused, got ${results[0].unusedCodes.length}`); + } } if (unreachableCode !== undefined) { - assert.strictEqual(results[0].unreachableCodes.length, unreachableCode); + if (results[0].unreachableCodes.length !== unreachableCode) { + logDiagnostics(results[0].unreachableCodes); + assert.fail(`Expected ${unreachableCode} unreachable, got ${results[0].unreachableCodes.length}`); + } } if (deprecated !== undefined) { - assert.strictEqual(results[0].deprecateds.length, deprecated); + if (results[0].deprecateds.length !== deprecated) { + logDiagnostics(results[0].deprecateds); + assert.fail(`Expected ${deprecated} deprecated, got ${results[0].deprecateds.length}`); + } + } +} + +function logDiagnostics(diags: Diagnostic[]) { + for (const diag of diags) { + console.error(` [${diag.range.start.line + 1}:${diag.range.start.character + 1}] ${diag.message}`); } }