Skip to content
4 changes: 2 additions & 2 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -1069,7 +1069,7 @@ def read_plugins_snapshot(manager: BuildManager) -> dict[str, str] | None:
if snapshot is None:
return None
if not isinstance(snapshot, dict):
manager.log(f"Could not load plugins snapshot: cache is not a dict: {type(snapshot)}")
manager.log(f"Could not load plugins snapshot: cache is not a dict: {type(snapshot)}") # type: ignore[unreachable]
return None
return snapshot

Expand Down Expand Up @@ -1285,7 +1285,7 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> CacheMeta | No
if meta is None:
return None
if not isinstance(meta, dict):
manager.log(f"Could not load cache for {id}: meta cache is not a dict: {repr(meta)}")
manager.log(f"Could not load cache for {id}: meta cache is not a dict: {repr(meta)}") # type: ignore[unreachable]
return None
m = cache_meta_from_dict(meta, data_json)
t2 = time.time()
Expand Down
7 changes: 5 additions & 2 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
LITERAL_TYPE,
MDEF,
NOT_ABSTRACT,
SYMBOL_FUNCBASE_TYPES,
AssertStmt,
AssignmentExpr,
AssignmentStmt,
Expand Down Expand Up @@ -2780,7 +2781,7 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None:
def determine_type_of_member(self, sym: SymbolTableNode) -> Type | None:
if sym.type is not None:
return sym.type
if isinstance(sym.node, FuncBase):
if isinstance(sym.node, SYMBOL_FUNCBASE_TYPES):
return self.function_type(sym.node)
if isinstance(sym.node, TypeInfo):
if sym.node.typeddict_type:
Expand Down Expand Up @@ -4340,7 +4341,9 @@ def simple_rvalue(self, rvalue: Expression) -> bool:
if isinstance(rvalue, (IntExpr, StrExpr, BytesExpr, FloatExpr, RefExpr)):
return True
if isinstance(rvalue, CallExpr):
if isinstance(rvalue.callee, RefExpr) and isinstance(rvalue.callee.node, FuncBase):
if isinstance(rvalue.callee, RefExpr) and isinstance(
rvalue.callee.node, SYMBOL_FUNCBASE_TYPES
):
typ = rvalue.callee.node.type
if isinstance(typ, CallableType):
return not typ.variables
Expand Down
2 changes: 0 additions & 2 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2581,8 +2581,6 @@ def check_argument_types(
for actual, actual_type, actual_kind, callee_arg_type, callee_arg_kind in zip(
actuals, actual_types, actual_kinds, callee_arg_types, callee_arg_kinds
):
if actual_type is None:
continue # Some kind of error was already reported.
# Check that a *arg is valid as varargs.
expanded_actual = mapper.expand_actual_type(
actual_type,
Expand Down
8 changes: 4 additions & 4 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -1111,10 +1111,10 @@ def analyze_class_attribute_access(
t = erase_typevars(expand_type_by_instance(t, isuper), {tv.id for tv in def_vars})

is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or (
isinstance(node.node, FuncBase) and node.node.is_class
isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_class
)
is_staticmethod = (is_decorated and cast(Decorator, node.node).func.is_static) or (
isinstance(node.node, FuncBase) and node.node.is_static
isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_static
)
t = get_proper_type(t)
if isinstance(t, FunctionLike) and is_classmethod:
Expand Down Expand Up @@ -1156,7 +1156,7 @@ def analyze_class_attribute_access(
mx.not_ready_callback(name, mx.context)
return AnyType(TypeOfAny.from_error)
else:
assert isinstance(node.node, FuncBase)
assert isinstance(node.node, SYMBOL_FUNCBASE_TYPES)
typ = function_type(node.node, mx.named_type("builtins.function"))
# Note: if we are accessing class method on class object, the cls argument is bound.
# Annotated and/or explicit class methods go through other code paths above, for
Expand Down Expand Up @@ -1415,7 +1415,7 @@ def is_valid_constructor(n: SymbolNode | None) -> bool:
This includes normal functions, overloaded functions, and decorators
that return a callable type.
"""
if isinstance(n, FuncBase):
if isinstance(n, SYMBOL_FUNCBASE_TYPES):
return True
if isinstance(n, Decorator):
return isinstance(get_proper_type(n.type), FunctionLike)
Expand Down
12 changes: 4 additions & 8 deletions mypy/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,12 @@ def infer_constraints_for_callable(
param_spec_arg_kinds = []

incomplete_star_mapping = False
for i, actuals in enumerate(formal_to_actual):
for i, actuals in enumerate(formal_to_actual): # TODO: isn't this `enumerate(arg_types)`?
for actual in actuals:
if actual is None and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2):
if actual is None and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2): # type: ignore[unreachable]
# We can't use arguments to infer ParamSpec constraint, if only some
# are present in the current inference pass.
incomplete_star_mapping = True
incomplete_star_mapping = True # type: ignore[unreachable]
break

for i, actuals in enumerate(formal_to_actual):
Expand Down Expand Up @@ -545,11 +545,7 @@ def any_constraints(options: list[list[Constraint] | None], eager: bool) -> list
for option in valid_options:
if option in trivial_options:
continue
if option is not None:
merged_option: list[Constraint] | None = [merge_with_any(c) for c in option]
else:
merged_option = None
merged_options.append(merged_option)
merged_options.append([merge_with_any(c) for c in option])
return any_constraints(list(merged_options), eager)

# If normal logic didn't work, try excluding trivially unsatisfiable constraint (due to
Expand Down
40 changes: 12 additions & 28 deletions mypy/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from mypy import errorcodes as codes
from mypy.error_formatter import ErrorFormatter
from mypy.errorcodes import IMPORT, IMPORT_NOT_FOUND, IMPORT_UNTYPED, ErrorCode, mypy_error_codes
from mypy.message_registry import ErrorMessage
from mypy.options import Options
from mypy.scope import Scope
from mypy.util import DEFAULT_SOURCE_OFFSET, is_typeshed_file
Expand Down Expand Up @@ -1066,34 +1065,19 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]:
(file, -1, -1, -1, -1, "note", f'In class "{e.type}":', e.allow_dups, None)
)

if isinstance(e.message, ErrorMessage):
result.append(
(
file,
e.line,
e.column,
e.end_line,
e.end_column,
e.severity,
e.message.value,
e.allow_dups,
e.code,
)
)
else:
result.append(
(
file,
e.line,
e.column,
e.end_line,
e.end_column,
e.severity,
e.message,
e.allow_dups,
e.code,
)
result.append(
(
file,
e.line,
e.column,
e.end_line,
e.end_column,
e.severity,
e.message,
e.allow_dups,
e.code,
)
)

prev_import_context = e.import_ctx
prev_function_or_member = e.function_or_member
Expand Down
4 changes: 0 additions & 4 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2132,12 +2132,8 @@ def report_protocol_problems(
is_module = False
skip = []
if isinstance(subtype, TupleType):
if not isinstance(subtype.partial_fallback, Instance):
return
subtype = subtype.partial_fallback
elif isinstance(subtype, TypedDictType):
if not isinstance(subtype.fallback, Instance):
return
subtype = subtype.fallback
elif isinstance(subtype, TypeType):
if not isinstance(subtype.item, Instance):
Expand Down
17 changes: 13 additions & 4 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ class Node(Context):
def __str__(self) -> str:
ans = self.accept(mypy.strconv.StrConv(options=Options()))
if ans is None:
return repr(self)
# Some visitors might have empty bodies and actually return `None`
return repr(self) # type: ignore[unreachable]
return ans

def str_with_options(self, options: Options) -> str:
Expand Down Expand Up @@ -870,7 +871,9 @@ def deserialize(cls, data: JsonDict) -> FuncDef:

# All types that are both SymbolNodes and FuncBases. See the FuncBase
# docstring for the rationale.
SYMBOL_FUNCBASE_TYPES = (OverloadedFuncDef, FuncDef)
# See https://github.com/python/mypy/pull/13607#issuecomment-1236357236
# TODO: we want to remove this at some point and just use `FuncBase` ideally.
SYMBOL_FUNCBASE_TYPES: Final = (OverloadedFuncDef, FuncDef)


class Decorator(SymbolNode, Statement):
Expand Down Expand Up @@ -2552,6 +2555,11 @@ def fullname(self) -> str:
return self._fullname


# All types that are both SymbolNodes and Expressions.
# Use when common children of them are needed.
SYMBOL_NODE_EXPRESSION_TYPES: Final = (TypeVarLikeExpr,)


class TypeVarExpr(TypeVarLikeExpr):
"""Type variable expression TypeVar(...).

Expand Down Expand Up @@ -3250,7 +3258,7 @@ def get_method(self, name: str) -> FuncBase | Decorator | None:
for cls in self.mro:
if name in cls.names:
node = cls.names[name].node
if isinstance(node, FuncBase):
if isinstance(node, SYMBOL_FUNCBASE_TYPES):
return node
elif isinstance(node, Decorator): # Two `if`s make `mypyc` happy
return node
Expand Down Expand Up @@ -4009,7 +4017,8 @@ def __str__(self) -> str:
):
a.append(" " + str(key) + " : " + str(value))
else:
a.append(" <invalid item>")
# Used in debugging:
a.append(" <invalid item>") # type: ignore[unreachable]
a = sorted(a)
a.insert(0, "SymbolTable(")
a[-1] += ")"
Expand Down
2 changes: 1 addition & 1 deletion mypy/plugins/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ def tuple_mul_callback(ctx: MethodContext) -> Type:
value = arg_type.last_known_value.value
if isinstance(value, int):
return ctx.type.copy_modified(items=ctx.type.items * value)
elif isinstance(ctx.type, LiteralType):
elif isinstance(arg_type, LiteralType):
value = arg_type.value
if isinstance(value, int):
return ctx.type.copy_modified(items=ctx.type.items * value)
Expand Down
13 changes: 11 additions & 2 deletions mypy/plugins/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,16 @@
import mypy.plugin
import mypy.semanal
from mypy.argmap import map_actuals_to_formals
from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, CallExpr, FuncItem, NameExpr, Var
from mypy.nodes import (
ARG_POS,
ARG_STAR2,
SYMBOL_FUNCBASE_TYPES,
ArgKind,
Argument,
CallExpr,
NameExpr,
Var,
)
from mypy.plugins.common import add_method_to_class
from mypy.typeops import get_all_type_vars
from mypy.types import (
Expand Down Expand Up @@ -108,7 +117,7 @@ def _analyze_class(ctx: mypy.plugin.ClassDefContext) -> dict[str, _MethodInfo |
for name in _ORDERING_METHODS:
if name in cls.names and name not in comparison_methods:
node = cls.names[name].node
if isinstance(node, FuncItem) and isinstance(node.type, CallableType):
if isinstance(node, SYMBOL_FUNCBASE_TYPES) and isinstance(node.type, CallableType):
comparison_methods[name] = _MethodInfo(node.is_static, node.type)
continue

Expand Down
5 changes: 2 additions & 3 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
REVEAL_LOCALS,
REVEAL_TYPE,
RUNTIME_PROTOCOL_DECOS,
SYMBOL_FUNCBASE_TYPES,
TYPE_VAR_KIND,
TYPE_VAR_TUPLE_KIND,
VARIANCE_NOT_READY,
Expand Down Expand Up @@ -3076,8 +3077,6 @@ def visit_import_all(self, i: ImportAll) -> None:
for name, node in m.names.items():
fullname = i_id + "." + name
self.set_future_import_flags(fullname)
if node is None:
continue
# if '__all__' exists, all nodes not included have had module_public set to
# False, and we can skip checking '_' because it's been explicitly included.
if node.module_public and (not name.startswith("_") or "__all__" in m.names):
Expand Down Expand Up @@ -5704,7 +5703,7 @@ def visit_call_expr(self, expr: CallExpr) -> None:
reveal_type_node = self.lookup("reveal_type", expr, suppress_errors=True)
if (
reveal_type_node
and isinstance(reveal_type_node.node, FuncBase)
and isinstance(reveal_type_node.node, SYMBOL_FUNCBASE_TYPES)
and reveal_type_node.fullname in IMPORTED_REVEAL_TYPE_NAMES
):
reveal_imported = True
Expand Down
8 changes: 4 additions & 4 deletions mypy/server/astdiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method'

from mypy.expandtype import expand_type
from mypy.nodes import (
SYMBOL_FUNCBASE_TYPES,
UNBOUND_IMPORTED,
Decorator,
FuncBase,
FuncDef,
FuncItem,
MypyFile,
Expand Down Expand Up @@ -234,16 +234,16 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb
The representation is nested tuples and dicts. Only externally
visible attributes are included.
"""
if isinstance(node, FuncBase):
if isinstance(node, SYMBOL_FUNCBASE_TYPES):
# TODO: info
if node.type:
signature = snapshot_type(node.type)
signature: tuple[object, ...] = snapshot_type(node.type)
else:
signature = snapshot_untyped_signature(node)
impl: FuncDef | None = None
if isinstance(node, FuncDef):
impl = node
elif isinstance(node, OverloadedFuncDef) and node.impl:
elif node.impl:
impl = node.impl.func if isinstance(node.impl, Decorator) else node.impl
is_trivial_body = impl.is_trivial_body if impl else False
dataclass_transform_spec = find_dataclass_transform_spec(node)
Expand Down
3 changes: 2 additions & 1 deletion mypy/server/astmerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@

from mypy.nodes import (
MDEF,
SYMBOL_NODE_EXPRESSION_TYPES,
AssertTypeExpr,
AssignmentStmt,
Block,
Expand Down Expand Up @@ -301,7 +302,7 @@ def visit_super_expr(self, node: SuperExpr) -> None:

def visit_call_expr(self, node: CallExpr) -> None:
super().visit_call_expr(node)
if isinstance(node.analyzed, SymbolNode):
if isinstance(node.analyzed, SYMBOL_NODE_EXPRESSION_TYPES):
node.analyzed = self.fixup(node.analyzed)

def visit_newtype_expr(self, node: NewTypeExpr) -> None:
Expand Down
3 changes: 2 additions & 1 deletion mypy/server/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class 'mod.Cls'. This can also refer to an attribute inherited from a
GDEF,
LDEF,
MDEF,
SYMBOL_FUNCBASE_TYPES,
AssertTypeExpr,
AssignmentStmt,
AwaitExpr,
Expand Down Expand Up @@ -501,7 +502,7 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None:
if isinstance(rvalue.callee.node, TypeInfo):
# use actual __init__ as a dependency source
init = rvalue.callee.node.get("__init__")
if init and isinstance(init.node, FuncBase):
if init and isinstance(init.node, SYMBOL_FUNCBASE_TYPES):
fname = init.node.fullname
else:
fname = rvalue.callee.fullname
Expand Down
7 changes: 4 additions & 3 deletions mypy/server/mergecheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,19 @@ def check_consistency(o: object) -> None:
continue

fn = sym.fullname
# Skip None names, since they are ambiguous.
# Skip None and empty names, since they are ambiguous.
# TODO: Everything should have a proper full name?
if fn is None:
if not fn:
continue

# Skip stuff that should be expected to have duplicate names
if isinstance(sym, (Var, Decorator)):
continue
if isinstance(sym, FuncDef) and sym.is_overload:
continue

if fn not in m:
m[sym.fullname] = sym
m[fn] = sym
continue

# We have trouble and need to decide what to do about it.
Expand Down
4 changes: 1 addition & 3 deletions mypy/stubgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1507,9 +1507,7 @@ def is_blacklisted_path(path: str) -> bool:


def normalize_path_separators(path: str) -> str:
if sys.platform == "win32":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If #18539 or a similar fix is merged, then this change should no longer be necessary I believe.

return path.replace("\\", "/")
return path
return path.replace("\\", "/") if sys.platform == "win32" else path


def collect_build_targets(
Expand Down
Loading
Loading