Added an error message for a Literal type annotation that includes a string with a named unicode escape sequence. These are not supported.

This commit is contained in:
Eric Traut 2024-04-09 17:26:27 -07:00
parent bd08098c5d
commit ad1c378268
6 changed files with 18 additions and 4 deletions

View File

@ -14775,6 +14775,16 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
} else {
type = cloneBuiltinClassWithLiteral(node, classType, 'str', value);
}
itemExpr.strings.forEach((stringNode) => {
if ((stringNode.token.flags & StringTokenFlags.NamedUnicodeEscape) !== 0) {
addDiagnostic(
DiagnosticRule.reportInvalidTypeForm,
LocMessage.literalNamedUnicodeEscape(),
stringNode
);
}
});
} else if (itemExpr.nodeType === ParseNodeType.Number) {
if (!itemExpr.isImaginary && itemExpr.isInteger) {
type = cloneBuiltinClassWithLiteral(node, classType, 'int', itemExpr.value);

View File

@ -601,6 +601,7 @@ export namespace Localizer {
export const listAssignmentMismatch = () =>
new ParameterizedString<{ type: string }>(getRawString('Diagnostic.listAssignmentMismatch'));
export const listInAnnotation = () => getRawString('Diagnostic.listInAnnotation');
export const literalNamedUnicodeEscape = () => getRawString('Diagnostic.literalNamedUnicodeEscape');
export const literalUnsupportedType = () => getRawString('Diagnostic.literalUnsupportedType');
export const literalEmptyArgs = () => getRawString('Diagnostic.literalEmptyArgs');
export const literalNotAllowed = () => getRawString('Diagnostic.literalNotAllowed');

View File

@ -263,6 +263,7 @@
"listAssignmentMismatch": "Expression with type \"{type}\" cannot be assigned to target list",
"listInAnnotation": "List expression not allowed in type annotation",
"literalUnsupportedType": "Type arguments for \"Literal\" must be None, a literal value (int, bool, str, or bytes), or an enum value",
"literalNamedUnicodeEscape": "Named unicode escape sequences are not supported in \"Literal\" string annotations",
"literalEmptyArgs": "Expected one or more type arguments after \"Literal\"",
"literalNotAllowed": "\"Literal\" cannot be used in this context without a type argument",
"literalNotCallable": "Literal type cannot be instantiated",

View File

@ -1593,6 +1593,7 @@ export class Tokenizer {
this._cs.getCurrentChar() === Char.N &&
this._cs.nextChar === Char.OpenBrace
) {
flags |= StringTokenFlags.NamedUnicodeEscape;
isInNamedUnicodeEscape = true;
} else {
// If this is an f-string, the only escapes that are allowed is for

View File

@ -170,6 +170,7 @@ export const enum StringTokenFlags {
// Other conditions
ReplacementFieldStart = 1 << 7,
ReplacementFieldEnd = 1 << 8,
NamedUnicodeEscape = 1 << 9,
// Error conditions
Unterminated = 1 << 16,

View File

@ -1166,7 +1166,7 @@ test('Strings: good name escapes', () => {
const stringToken0 = results.tokens.getItemAt(0) as StringToken;
const unescapedValue0 = StringTokenUtils.getUnescapedString(stringToken0);
assert.equal(stringToken0.type, TokenType.String);
assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote);
assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.NamedUnicodeEscape);
assert.equal(stringToken0.length, 23);
assert.equal(stringToken0.escapedValue, '\\N{caret escape blah}');
assert.equal(unescapedValue0.value, '-');
@ -1174,7 +1174,7 @@ test('Strings: good name escapes', () => {
const stringToken1 = results.tokens.getItemAt(1) as StringToken;
const unescapedValue1 = StringTokenUtils.getUnescapedString(stringToken1);
assert.equal(stringToken1.type, TokenType.String);
assert.equal(stringToken1.flags, StringTokenFlags.DoubleQuote);
assert.equal(stringToken1.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.NamedUnicodeEscape);
assert.equal(stringToken1.length, 10);
assert.equal(stringToken1.escapedValue, 'a\\N{A9}a');
assert.equal(unescapedValue1.value, 'a-a');
@ -1188,7 +1188,7 @@ test('Strings: bad name escapes', () => {
const stringToken0 = results.tokens.getItemAt(0) as StringToken;
const unescapedValue0 = StringTokenUtils.getUnescapedString(stringToken0);
assert.equal(stringToken0.type, TokenType.String);
assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote);
assert.equal(stringToken0.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.NamedUnicodeEscape);
assert.equal(unescapedValue0.unescapeErrors.length, 1);
assert.equal(stringToken0.length, 10);
assert.equal(stringToken0.escapedValue, '\\N{caret');
@ -1197,7 +1197,7 @@ test('Strings: bad name escapes', () => {
const stringToken1 = results.tokens.getItemAt(1) as StringToken;
const unescapedValue1 = StringTokenUtils.getUnescapedString(stringToken1);
assert.equal(stringToken1.type, TokenType.String);
assert.equal(stringToken1.flags, StringTokenFlags.DoubleQuote);
assert.equal(stringToken1.flags, StringTokenFlags.DoubleQuote | StringTokenFlags.NamedUnicodeEscape);
assert.equal(unescapedValue1.unescapeErrors.length, 1);
assert.equal(stringToken1.length, 9);
assert.equal(stringToken1.escapedValue, '\\N{.A9}');