| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722 |
- """Pattern checker. This file is conceptually part of TypeChecker."""
- from __future__ import annotations
- from collections import defaultdict
- from typing import NamedTuple
- from typing_extensions import Final
- import mypy.checker
- from mypy import message_registry
- from mypy.checkmember import analyze_member_access
- from mypy.expandtype import expand_type_by_instance
- from mypy.join import join_types
- from mypy.literals import literal_hash
- from mypy.maptype import map_instance_to_supertype
- from mypy.meet import narrow_declared_type
- from mypy.messages import MessageBuilder
- from mypy.nodes import ARG_POS, Context, Expression, NameExpr, TypeAlias, TypeInfo, Var
- from mypy.options import Options
- from mypy.patterns import (
- AsPattern,
- ClassPattern,
- MappingPattern,
- OrPattern,
- Pattern,
- SequencePattern,
- SingletonPattern,
- StarredPattern,
- ValuePattern,
- )
- from mypy.plugin import Plugin
- from mypy.subtypes import is_subtype
- from mypy.typeops import (
- coerce_to_literal,
- make_simplified_union,
- try_getting_str_literals_from_type,
- tuple_fallback,
- )
- from mypy.types import (
- AnyType,
- Instance,
- LiteralType,
- NoneType,
- ProperType,
- TupleType,
- Type,
- TypedDictType,
- TypeOfAny,
- UninhabitedType,
- UnionType,
- get_proper_type,
- )
- from mypy.typevars import fill_typevars
- from mypy.visitor import PatternVisitor
- self_match_type_names: Final = [
- "builtins.bool",
- "builtins.bytearray",
- "builtins.bytes",
- "builtins.dict",
- "builtins.float",
- "builtins.frozenset",
- "builtins.int",
- "builtins.list",
- "builtins.set",
- "builtins.str",
- "builtins.tuple",
- ]
- non_sequence_match_type_names: Final = ["builtins.str", "builtins.bytes", "builtins.bytearray"]
- # For every Pattern a PatternType can be calculated. This requires recursively calculating
- # the PatternTypes of the sub-patterns first.
- # Using the data in the PatternType the match subject and captured names can be narrowed/inferred.
- class PatternType(NamedTuple):
- type: Type # The type the match subject can be narrowed to
- rest_type: Type # The remaining type if the pattern didn't match
- captures: dict[Expression, Type] # The variables captured by the pattern
- class PatternChecker(PatternVisitor[PatternType]):
- """Pattern checker.
- This class checks if a pattern can match a type, what the type can be narrowed to, and what
- type capture patterns should be inferred as.
- """
- # Some services are provided by a TypeChecker instance.
- chk: mypy.checker.TypeChecker
- # This is shared with TypeChecker, but stored also here for convenience.
- msg: MessageBuilder
- # Currently unused
- plugin: Plugin
- # The expression being matched against the pattern
- subject: Expression
- subject_type: Type
- # Type of the subject to check the (sub)pattern against
- type_context: list[Type]
- # Types that match against self instead of their __match_args__ if used as a class pattern
- # Filled in from self_match_type_names
- self_match_types: list[Type]
- # Types that are sequences, but don't match sequence patterns. Filled in from
- # non_sequence_match_type_names
- non_sequence_match_types: list[Type]
- options: Options
- def __init__(
- self, chk: mypy.checker.TypeChecker, msg: MessageBuilder, plugin: Plugin, options: Options
- ) -> None:
- self.chk = chk
- self.msg = msg
- self.plugin = plugin
- self.type_context = []
- self.self_match_types = self.generate_types_from_names(self_match_type_names)
- self.non_sequence_match_types = self.generate_types_from_names(
- non_sequence_match_type_names
- )
- self.options = options
- def accept(self, o: Pattern, type_context: Type) -> PatternType:
- self.type_context.append(type_context)
- result = o.accept(self)
- self.type_context.pop()
- return result
- def visit_as_pattern(self, o: AsPattern) -> PatternType:
- current_type = self.type_context[-1]
- if o.pattern is not None:
- pattern_type = self.accept(o.pattern, current_type)
- typ, rest_type, type_map = pattern_type
- else:
- typ, rest_type, type_map = current_type, UninhabitedType(), {}
- if not is_uninhabited(typ) and o.name is not None:
- typ, _ = self.chk.conditional_types_with_intersection(
- current_type, [get_type_range(typ)], o, default=current_type
- )
- if not is_uninhabited(typ):
- type_map[o.name] = typ
- return PatternType(typ, rest_type, type_map)
- def visit_or_pattern(self, o: OrPattern) -> PatternType:
- current_type = self.type_context[-1]
- #
- # Check all the subpatterns
- #
- pattern_types = []
- for pattern in o.patterns:
- pattern_type = self.accept(pattern, current_type)
- pattern_types.append(pattern_type)
- current_type = pattern_type.rest_type
- #
- # Collect the final type
- #
- types = []
- for pattern_type in pattern_types:
- if not is_uninhabited(pattern_type.type):
- types.append(pattern_type.type)
- #
- # Check the capture types
- #
- capture_types: dict[Var, list[tuple[Expression, Type]]] = defaultdict(list)
- # Collect captures from the first subpattern
- for expr, typ in pattern_types[0].captures.items():
- node = get_var(expr)
- capture_types[node].append((expr, typ))
- # Check if other subpatterns capture the same names
- for i, pattern_type in enumerate(pattern_types[1:]):
- vars = {get_var(expr) for expr, _ in pattern_type.captures.items()}
- if capture_types.keys() != vars:
- self.msg.fail(message_registry.OR_PATTERN_ALTERNATIVE_NAMES, o.patterns[i])
- for expr, typ in pattern_type.captures.items():
- node = get_var(expr)
- capture_types[node].append((expr, typ))
- captures: dict[Expression, Type] = {}
- for var, capture_list in capture_types.items():
- typ = UninhabitedType()
- for _, other in capture_list:
- typ = join_types(typ, other)
- captures[capture_list[0][0]] = typ
- union_type = make_simplified_union(types)
- return PatternType(union_type, current_type, captures)
- def visit_value_pattern(self, o: ValuePattern) -> PatternType:
- current_type = self.type_context[-1]
- typ = self.chk.expr_checker.accept(o.expr)
- typ = coerce_to_literal(typ)
- narrowed_type, rest_type = self.chk.conditional_types_with_intersection(
- current_type, [get_type_range(typ)], o, default=current_type
- )
- if not isinstance(get_proper_type(narrowed_type), (LiteralType, UninhabitedType)):
- return PatternType(narrowed_type, UnionType.make_union([narrowed_type, rest_type]), {})
- return PatternType(narrowed_type, rest_type, {})
- def visit_singleton_pattern(self, o: SingletonPattern) -> PatternType:
- current_type = self.type_context[-1]
- value: bool | None = o.value
- if isinstance(value, bool):
- typ = self.chk.expr_checker.infer_literal_expr_type(value, "builtins.bool")
- elif value is None:
- typ = NoneType()
- else:
- assert False
- narrowed_type, rest_type = self.chk.conditional_types_with_intersection(
- current_type, [get_type_range(typ)], o, default=current_type
- )
- return PatternType(narrowed_type, rest_type, {})
- def visit_sequence_pattern(self, o: SequencePattern) -> PatternType:
- #
- # check for existence of a starred pattern
- #
- current_type = get_proper_type(self.type_context[-1])
- if not self.can_match_sequence(current_type):
- return self.early_non_match()
- star_positions = [i for i, p in enumerate(o.patterns) if isinstance(p, StarredPattern)]
- star_position: int | None = None
- if len(star_positions) == 1:
- star_position = star_positions[0]
- elif len(star_positions) >= 2:
- assert False, "Parser should prevent multiple starred patterns"
- required_patterns = len(o.patterns)
- if star_position is not None:
- required_patterns -= 1
- #
- # get inner types of original type
- #
- if isinstance(current_type, TupleType):
- inner_types = current_type.items
- size_diff = len(inner_types) - required_patterns
- if size_diff < 0:
- return self.early_non_match()
- elif size_diff > 0 and star_position is None:
- return self.early_non_match()
- else:
- inner_type = self.get_sequence_type(current_type, o)
- if inner_type is None:
- inner_type = self.chk.named_type("builtins.object")
- inner_types = [inner_type] * len(o.patterns)
- #
- # match inner patterns
- #
- contracted_new_inner_types: list[Type] = []
- contracted_rest_inner_types: list[Type] = []
- captures: dict[Expression, Type] = {}
- contracted_inner_types = self.contract_starred_pattern_types(
- inner_types, star_position, required_patterns
- )
- for p, t in zip(o.patterns, contracted_inner_types):
- pattern_type = self.accept(p, t)
- typ, rest, type_map = pattern_type
- contracted_new_inner_types.append(typ)
- contracted_rest_inner_types.append(rest)
- self.update_type_map(captures, type_map)
- new_inner_types = self.expand_starred_pattern_types(
- contracted_new_inner_types, star_position, len(inner_types)
- )
- rest_inner_types = self.expand_starred_pattern_types(
- contracted_rest_inner_types, star_position, len(inner_types)
- )
- #
- # Calculate new type
- #
- new_type: Type
- rest_type: Type = current_type
- if isinstance(current_type, TupleType):
- narrowed_inner_types = []
- inner_rest_types = []
- for inner_type, new_inner_type in zip(inner_types, new_inner_types):
- (
- narrowed_inner_type,
- inner_rest_type,
- ) = self.chk.conditional_types_with_intersection(
- new_inner_type, [get_type_range(inner_type)], o, default=new_inner_type
- )
- narrowed_inner_types.append(narrowed_inner_type)
- inner_rest_types.append(inner_rest_type)
- if all(not is_uninhabited(typ) for typ in narrowed_inner_types):
- new_type = TupleType(narrowed_inner_types, current_type.partial_fallback)
- else:
- new_type = UninhabitedType()
- if all(is_uninhabited(typ) for typ in inner_rest_types):
- # All subpatterns always match, so we can apply negative narrowing
- rest_type = TupleType(rest_inner_types, current_type.partial_fallback)
- else:
- new_inner_type = UninhabitedType()
- for typ in new_inner_types:
- new_inner_type = join_types(new_inner_type, typ)
- new_type = self.construct_sequence_child(current_type, new_inner_type)
- if is_subtype(new_type, current_type):
- new_type, _ = self.chk.conditional_types_with_intersection(
- current_type, [get_type_range(new_type)], o, default=current_type
- )
- else:
- new_type = current_type
- return PatternType(new_type, rest_type, captures)
- def get_sequence_type(self, t: Type, context: Context) -> Type | None:
- t = get_proper_type(t)
- if isinstance(t, AnyType):
- return AnyType(TypeOfAny.from_another_any, t)
- if isinstance(t, UnionType):
- items = [self.get_sequence_type(item, context) for item in t.items]
- not_none_items = [item for item in items if item is not None]
- if not_none_items:
- return make_simplified_union(not_none_items)
- else:
- return None
- if self.chk.type_is_iterable(t) and isinstance(t, (Instance, TupleType)):
- if isinstance(t, TupleType):
- t = tuple_fallback(t)
- return self.chk.iterable_item_type(t, context)
- else:
- return None
- def contract_starred_pattern_types(
- self, types: list[Type], star_pos: int | None, num_patterns: int
- ) -> list[Type]:
- """
- Contracts a list of types in a sequence pattern depending on the position of a starred
- capture pattern.
- For example if the sequence pattern [a, *b, c] is matched against types [bool, int, str,
- bytes] the contracted types are [bool, Union[int, str], bytes].
- If star_pos in None the types are returned unchanged.
- """
- if star_pos is None:
- return types
- new_types = types[:star_pos]
- star_length = len(types) - num_patterns
- new_types.append(make_simplified_union(types[star_pos : star_pos + star_length]))
- new_types += types[star_pos + star_length :]
- return new_types
- def expand_starred_pattern_types(
- self, types: list[Type], star_pos: int | None, num_types: int
- ) -> list[Type]:
- """Undoes the contraction done by contract_starred_pattern_types.
- For example if the sequence pattern is [a, *b, c] and types [bool, int, str] are extended
- to length 4 the result is [bool, int, int, str].
- """
- if star_pos is None:
- return types
- new_types = types[:star_pos]
- star_length = num_types - len(types) + 1
- new_types += [types[star_pos]] * star_length
- new_types += types[star_pos + 1 :]
- return new_types
- def visit_starred_pattern(self, o: StarredPattern) -> PatternType:
- captures: dict[Expression, Type] = {}
- if o.capture is not None:
- list_type = self.chk.named_generic_type("builtins.list", [self.type_context[-1]])
- captures[o.capture] = list_type
- return PatternType(self.type_context[-1], UninhabitedType(), captures)
- def visit_mapping_pattern(self, o: MappingPattern) -> PatternType:
- current_type = get_proper_type(self.type_context[-1])
- can_match = True
- captures: dict[Expression, Type] = {}
- for key, value in zip(o.keys, o.values):
- inner_type = self.get_mapping_item_type(o, current_type, key)
- if inner_type is None:
- can_match = False
- inner_type = self.chk.named_type("builtins.object")
- pattern_type = self.accept(value, inner_type)
- if is_uninhabited(pattern_type.type):
- can_match = False
- else:
- self.update_type_map(captures, pattern_type.captures)
- if o.rest is not None:
- mapping = self.chk.named_type("typing.Mapping")
- if is_subtype(current_type, mapping) and isinstance(current_type, Instance):
- mapping_inst = map_instance_to_supertype(current_type, mapping.type)
- dict_typeinfo = self.chk.lookup_typeinfo("builtins.dict")
- rest_type = Instance(dict_typeinfo, mapping_inst.args)
- else:
- object_type = self.chk.named_type("builtins.object")
- rest_type = self.chk.named_generic_type(
- "builtins.dict", [object_type, object_type]
- )
- captures[o.rest] = rest_type
- if can_match:
- # We can't narrow the type here, as Mapping key is invariant.
- new_type = self.type_context[-1]
- else:
- new_type = UninhabitedType()
- return PatternType(new_type, current_type, captures)
- def get_mapping_item_type(
- self, pattern: MappingPattern, mapping_type: Type, key: Expression
- ) -> Type | None:
- mapping_type = get_proper_type(mapping_type)
- if isinstance(mapping_type, TypedDictType):
- with self.msg.filter_errors() as local_errors:
- result: Type | None = self.chk.expr_checker.visit_typeddict_index_expr(
- mapping_type, key
- )
- has_local_errors = local_errors.has_new_errors()
- # If we can't determine the type statically fall back to treating it as a normal
- # mapping
- if has_local_errors:
- with self.msg.filter_errors() as local_errors:
- result = self.get_simple_mapping_item_type(pattern, mapping_type, key)
- if local_errors.has_new_errors():
- result = None
- else:
- with self.msg.filter_errors():
- result = self.get_simple_mapping_item_type(pattern, mapping_type, key)
- return result
- def get_simple_mapping_item_type(
- self, pattern: MappingPattern, mapping_type: Type, key: Expression
- ) -> Type:
- result, _ = self.chk.expr_checker.check_method_call_by_name(
- "__getitem__", mapping_type, [key], [ARG_POS], pattern
- )
- return result
- def visit_class_pattern(self, o: ClassPattern) -> PatternType:
- current_type = get_proper_type(self.type_context[-1])
- #
- # Check class type
- #
- type_info = o.class_ref.node
- if type_info is None:
- return PatternType(AnyType(TypeOfAny.from_error), AnyType(TypeOfAny.from_error), {})
- if isinstance(type_info, TypeAlias) and not type_info.no_args:
- self.msg.fail(message_registry.CLASS_PATTERN_GENERIC_TYPE_ALIAS, o)
- return self.early_non_match()
- if isinstance(type_info, TypeInfo):
- any_type = AnyType(TypeOfAny.implementation_artifact)
- typ: Type = Instance(type_info, [any_type] * len(type_info.defn.type_vars))
- elif isinstance(type_info, TypeAlias):
- typ = type_info.target
- else:
- if isinstance(type_info, Var) and type_info.type is not None:
- name = type_info.type.str_with_options(self.options)
- else:
- name = type_info.name
- self.msg.fail(message_registry.CLASS_PATTERN_TYPE_REQUIRED.format(name), o.class_ref)
- return self.early_non_match()
- new_type, rest_type = self.chk.conditional_types_with_intersection(
- current_type, [get_type_range(typ)], o, default=current_type
- )
- if is_uninhabited(new_type):
- return self.early_non_match()
- # TODO: Do I need this?
- narrowed_type = narrow_declared_type(current_type, new_type)
- #
- # Convert positional to keyword patterns
- #
- keyword_pairs: list[tuple[str | None, Pattern]] = []
- match_arg_set: set[str] = set()
- captures: dict[Expression, Type] = {}
- if len(o.positionals) != 0:
- if self.should_self_match(typ):
- if len(o.positionals) > 1:
- self.msg.fail(message_registry.CLASS_PATTERN_TOO_MANY_POSITIONAL_ARGS, o)
- pattern_type = self.accept(o.positionals[0], narrowed_type)
- if not is_uninhabited(pattern_type.type):
- return PatternType(
- pattern_type.type,
- join_types(rest_type, pattern_type.rest_type),
- pattern_type.captures,
- )
- captures = pattern_type.captures
- else:
- with self.msg.filter_errors() as local_errors:
- match_args_type = analyze_member_access(
- "__match_args__",
- typ,
- o,
- False,
- False,
- False,
- self.msg,
- original_type=typ,
- chk=self.chk,
- )
- has_local_errors = local_errors.has_new_errors()
- if has_local_errors:
- self.msg.fail(
- message_registry.MISSING_MATCH_ARGS.format(
- typ.str_with_options(self.options)
- ),
- o,
- )
- return self.early_non_match()
- proper_match_args_type = get_proper_type(match_args_type)
- if isinstance(proper_match_args_type, TupleType):
- match_arg_names = get_match_arg_names(proper_match_args_type)
- if len(o.positionals) > len(match_arg_names):
- self.msg.fail(message_registry.CLASS_PATTERN_TOO_MANY_POSITIONAL_ARGS, o)
- return self.early_non_match()
- else:
- match_arg_names = [None] * len(o.positionals)
- for arg_name, pos in zip(match_arg_names, o.positionals):
- keyword_pairs.append((arg_name, pos))
- if arg_name is not None:
- match_arg_set.add(arg_name)
- #
- # Check for duplicate patterns
- #
- keyword_arg_set = set()
- has_duplicates = False
- for key, value in zip(o.keyword_keys, o.keyword_values):
- keyword_pairs.append((key, value))
- if key in match_arg_set:
- self.msg.fail(
- message_registry.CLASS_PATTERN_KEYWORD_MATCHES_POSITIONAL.format(key), value
- )
- has_duplicates = True
- elif key in keyword_arg_set:
- self.msg.fail(
- message_registry.CLASS_PATTERN_DUPLICATE_KEYWORD_PATTERN.format(key), value
- )
- has_duplicates = True
- keyword_arg_set.add(key)
- if has_duplicates:
- return self.early_non_match()
- #
- # Check keyword patterns
- #
- can_match = True
- for keyword, pattern in keyword_pairs:
- key_type: Type | None = None
- with self.msg.filter_errors() as local_errors:
- if keyword is not None:
- key_type = analyze_member_access(
- keyword,
- narrowed_type,
- pattern,
- False,
- False,
- False,
- self.msg,
- original_type=new_type,
- chk=self.chk,
- )
- else:
- key_type = AnyType(TypeOfAny.from_error)
- has_local_errors = local_errors.has_new_errors()
- if has_local_errors or key_type is None:
- key_type = AnyType(TypeOfAny.from_error)
- self.msg.fail(
- message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format(
- typ.str_with_options(self.options), keyword
- ),
- pattern,
- )
- inner_type, inner_rest_type, inner_captures = self.accept(pattern, key_type)
- if is_uninhabited(inner_type):
- can_match = False
- else:
- self.update_type_map(captures, inner_captures)
- if not is_uninhabited(inner_rest_type):
- rest_type = current_type
- if not can_match:
- new_type = UninhabitedType()
- return PatternType(new_type, rest_type, captures)
- def should_self_match(self, typ: Type) -> bool:
- typ = get_proper_type(typ)
- if isinstance(typ, Instance) and typ.type.is_named_tuple:
- return False
- for other in self.self_match_types:
- if is_subtype(typ, other):
- return True
- return False
- def can_match_sequence(self, typ: ProperType) -> bool:
- if isinstance(typ, UnionType):
- return any(self.can_match_sequence(get_proper_type(item)) for item in typ.items)
- for other in self.non_sequence_match_types:
- # We have to ignore promotions, as memoryview should match, but bytes,
- # which it can be promoted to, shouldn't
- if is_subtype(typ, other, ignore_promotions=True):
- return False
- sequence = self.chk.named_type("typing.Sequence")
- # If the static type is more general than sequence the actual type could still match
- return is_subtype(typ, sequence) or is_subtype(sequence, typ)
- def generate_types_from_names(self, type_names: list[str]) -> list[Type]:
- types: list[Type] = []
- for name in type_names:
- try:
- types.append(self.chk.named_type(name))
- except KeyError as e:
- # Some built in types are not defined in all test cases
- if not name.startswith("builtins."):
- raise e
- return types
- def update_type_map(
- self, original_type_map: dict[Expression, Type], extra_type_map: dict[Expression, Type]
- ) -> None:
- # Calculating this would not be needed if TypeMap directly used literal hashes instead of
- # expressions, as suggested in the TODO above it's definition
- already_captured = {literal_hash(expr) for expr in original_type_map}
- for expr, typ in extra_type_map.items():
- if literal_hash(expr) in already_captured:
- node = get_var(expr)
- self.msg.fail(
- message_registry.MULTIPLE_ASSIGNMENTS_IN_PATTERN.format(node.name), expr
- )
- else:
- original_type_map[expr] = typ
- def construct_sequence_child(self, outer_type: Type, inner_type: Type) -> Type:
- """
- If outer_type is a child class of typing.Sequence returns a new instance of
- outer_type, that is a Sequence of inner_type. If outer_type is not a child class of
- typing.Sequence just returns a Sequence of inner_type
- For example:
- construct_sequence_child(List[int], str) = List[str]
- TODO: this doesn't make sense. For example if one has class S(Sequence[int], Generic[T])
- or class T(Sequence[Tuple[T, T]]), there is no way any of those can map to Sequence[str].
- """
- proper_type = get_proper_type(outer_type)
- if isinstance(proper_type, UnionType):
- types = [
- self.construct_sequence_child(item, inner_type)
- for item in proper_type.items
- if self.can_match_sequence(get_proper_type(item))
- ]
- return make_simplified_union(types)
- sequence = self.chk.named_generic_type("typing.Sequence", [inner_type])
- if is_subtype(outer_type, self.chk.named_type("typing.Sequence")):
- proper_type = get_proper_type(outer_type)
- if isinstance(proper_type, TupleType):
- proper_type = tuple_fallback(proper_type)
- assert isinstance(proper_type, Instance)
- empty_type = fill_typevars(proper_type.type)
- partial_type = expand_type_by_instance(empty_type, sequence)
- return expand_type_by_instance(partial_type, proper_type)
- else:
- return sequence
- def early_non_match(self) -> PatternType:
- return PatternType(UninhabitedType(), self.type_context[-1], {})
- def get_match_arg_names(typ: TupleType) -> list[str | None]:
- args: list[str | None] = []
- for item in typ.items:
- values = try_getting_str_literals_from_type(item)
- if values is None or len(values) != 1:
- args.append(None)
- else:
- args.append(values[0])
- return args
- def get_var(expr: Expression) -> Var:
- """
- Warning: this in only true for expressions captured by a match statement.
- Don't call it from anywhere else
- """
- assert isinstance(expr, NameExpr)
- node = expr.node
- assert isinstance(node, Var)
- return node
- def get_type_range(typ: Type) -> mypy.checker.TypeRange:
- typ = get_proper_type(typ)
- if (
- isinstance(typ, Instance)
- and typ.last_known_value
- and isinstance(typ.last_known_value.value, bool)
- ):
- typ = typ.last_known_value
- return mypy.checker.TypeRange(typ, is_upper_bound=False)
- def is_uninhabited(typ: Type) -> bool:
- return isinstance(get_proper_type(typ), UninhabitedType)
|