Skip to content

Commit

Permalink
Added check for \u and \N escapes within bytes literals, which ar…
Browse files Browse the repository at this point in the history
…e illegal. (#9521)
  • Loading branch information
erictraut authored Nov 30, 2024
1 parent 1a66d4c commit 6a6c9eb
Show file tree
Hide file tree
Showing 6 changed files with 25 additions and 8 deletions.
4 changes: 3 additions & 1 deletion packages/pyright-internal/src/analyzer/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1351,7 +1351,9 @@ export class Checker extends ParseTreeWalker {
this._evaluator.addDiagnosticForTextRange(
this._fileInfo,
DiagnosticRule.reportInvalidStringEscapeSequence,
LocMessage.stringUnsupportedEscape(),
node.d.strings.some((string) => (string.d.token.flags & StringTokenFlags.Bytes) !== 0)
? LocMessage.bytesUnsupportedEscape()
: LocMessage.stringUnsupportedEscape(),
{ start: start + error.offset, length: error.length }
);
}
Expand Down
1 change: 1 addition & 0 deletions packages/pyright-internal/src/localization/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ export namespace Localizer {
);
export const breakInExceptionGroup = () => getRawString('Diagnostic.breakInExceptionGroup');
export const breakOutsideLoop = () => getRawString('Diagnostic.breakOutsideLoop');
export const bytesUnsupportedEscape = () => getRawString('Diagnostic.bytesUnsupportedEscape');
export const callableExtraArgs = () => getRawString('Diagnostic.callableExtraArgs');
export const callableFirstArg = () => getRawString('Diagnostic.callableFirstArg');
export const callableNotInstantiable = () =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@
"message": "\"break\" can be used only within a loop",
"comment": "{Locked='break'}"
},
"bytesUnsupportedEscape": {
"message": "Unsupported escape sequence in bytes literal",
"comment": "{Locked='bytes'}"
},
"callableExtraArgs": {
"message": "Expected only two type arguments to \"Callable\"",
"comment": "{Locked='Callable'}"
Expand Down
20 changes: 14 additions & 6 deletions packages/pyright-internal/src/parser/stringTokenUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ interface IncompleteUnescapedString {

function completeUnescapedString(incomplete: IncompleteUnescapedString, originalString: string): UnescapedString {
const newValue = incomplete.valueParts.join('');
// Use the original string if it's identical. This prevents us from allocating memory to hold
// a copy (a copy is made because the original string is a 'slice' of another, so it doesn't exist in the cache yet).
// Use the original string if it's identical. This prevents us from allocating
// memory to hold a copy. A copy is made because the original string is a
// 'slice' of another, so it doesn't exist in the cache yet.
const value = originalString !== newValue ? newValue : originalString;
return {
...incomplete,
Expand Down Expand Up @@ -224,6 +225,12 @@ export function getUnescapedString(stringToken: StringToken | FStringMiddleToken
case Char.N: {
let foundIllegalChar = false;
let charCount = 1;

// This type of escape isn't allowed for bytes.
if (isBytes) {
foundIllegalChar = true;
}

if (getEscapedCharacter(charCount) !== Char.OpenBrace) {
foundIllegalChar = true;
} else {
Expand Down Expand Up @@ -260,11 +267,12 @@ export function getUnescapedString(stringToken: StringToken | FStringMiddleToken
}

case Char.u:
localValue = scanHexEscape(4);
break;

case Char.U:
localValue = scanHexEscape(8);
// This type of escape isn't allowed for bytes.
if (isBytes) {
addInvalidEscapeOffset();
}
localValue = scanHexEscape(curChar === Char.u ? 4 : 8);
break;

default:
Expand Down
2 changes: 2 additions & 0 deletions packages/pyright-internal/src/tests/samples/strings2.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@
# This should generate an error.
v4 = b"a" f""

# This should generate a warning.
v5 = b"\u00FF"
2 changes: 1 addition & 1 deletion packages/pyright-internal/src/tests/typeEvaluator8.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,7 @@ test('PseudoGeneric3', () => {
test('Strings2', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['strings2.py']);

TestUtils.validateResults(analysisResults, 2);
TestUtils.validateResults(analysisResults, 2, 1);
});

test('LiteralString1', () => {
Expand Down

0 comments on commit 6a6c9eb

Please sign in to comment.