Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 13 additions & 19 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,6 @@
# string literal as a type expression.
_MULTIPLE_WORDS_NONTYPE_RE = re.compile(r'\s*[^\s.\'"|\[]+\s+[^\s.\'"|\[]')

# Matches any valid Python identifier, including identifiers with Unicode characters.
#
# [^\d\W] = word character that is not a digit
# \w = word character
# \Z = match end of string; does not allow a trailing \n, unlike $
_IDENTIFIER_RE = re.compile(r"^[^\d\W]\w*\Z", re.UNICODE)


class SemanticAnalyzer(
NodeVisitor[None], SemanticAnalyzerInterface, SemanticAnalyzerPluginInterface, SplittingVisitor
Expand Down Expand Up @@ -8043,16 +8036,9 @@ def try_parse_as_type_expression(self, maybe_type_expr: Expression) -> None:
return
elif isinstance(maybe_type_expr, StrExpr):
str_value = maybe_type_expr.value # cache
# Filter out string literals with common patterns that could not
# possibly be in a type expression
if _MULTIPLE_WORDS_NONTYPE_RE.match(str_value):
# A common pattern in string literals containing a sentence.
# But cannot be a type expression.
maybe_type_expr.as_type = None
return
# Filter out string literals which look like an identifier but
# cannot be a type expression, for a few common reasons
if _IDENTIFIER_RE.fullmatch(str_value):
if str_value.isidentifier():
sym = self.lookup(str_value, UnboundType(str_value), suppress_errors=True)
if sym is None:
# Does not refer to anything in the local symbol table
Expand All @@ -8078,13 +8064,21 @@ def try_parse_as_type_expression(self, maybe_type_expr: Expression) -> None:
return
else: # does not look like an identifier
if '"' in str_value or "'" in str_value:
# Only valid inside a Literal[...] type
# Only valid inside a Literal[...] or Annotated[..., ...] type
if "[" not in str_value:
# Cannot be a Literal[...] type
# Cannot be a Literal[...] or Annotated[..., ...] type
maybe_type_expr.as_type = None
return
elif str_value == "":
# Empty string is not a valid type
elif len(str_value) < 2 or str_value.isspace():
# Whitespace-only strings cannot be valid types. Very short strings can
# only be valid if they are identifiers, but we already checked for those.
maybe_type_expr.as_type = None
return
# Filter out string literals with common patterns that could not
# possibly be in a type expression
if _MULTIPLE_WORDS_NONTYPE_RE.match(str_value):
# A common pattern in string literals containing a sentence.
# But cannot be a type expression.
maybe_type_expr.as_type = None
return
elif isinstance(maybe_type_expr, IndexExpr):
Expand Down
Loading