Skip to content

Commit 6d0e637

Browse files
authored
New semantic analyzer: fix handling invalid attrs converters (#7096)
This is a hacky fix to better handle overloaded functions during semantic analysis. The original approach is not very clean so this doesn't make this worse, arguably. Fixes #6438.
1 parent efd68dd commit 6d0e637

File tree

2 files changed

+40
-5
lines changed

2 files changed

+40
-5
lines changed

mypy/plugins/attrs.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Plugin for supporting the attrs library (http://www.attrs.org)"""
2+
23
from collections import OrderedDict
34

45
from typing import Optional, Dict, List, cast, Tuple, Iterable
@@ -9,7 +10,7 @@
910
from mypy.fixup import lookup_qualified_stnode
1011
from mypy.nodes import (
1112
Context, Argument, Var, ARG_OPT, ARG_POS, TypeInfo, AssignmentStmt,
12-
TupleExpr, ListExpr, NameExpr, CallExpr, RefExpr, FuncBase,
13+
TupleExpr, ListExpr, NameExpr, CallExpr, RefExpr, FuncDef,
1314
is_class_var, TempNode, Decorator, MemberExpr, Expression,
1415
SymbolTableNode, MDEF, JsonDict, OverloadedFuncDef, ARG_NAMED_OPT, ARG_NAMED,
1516
TypeVarExpr
@@ -463,10 +464,13 @@ def _parse_converter(ctx: 'mypy.plugin.ClassDefContext',
463464
# TODO: Support complex converters, e.g. lambdas, calls, etc.
464465
if converter:
465466
if isinstance(converter, RefExpr) and converter.node:
466-
if (isinstance(converter.node, FuncBase)
467+
if (isinstance(converter.node, FuncDef)
467468
and converter.node.type
468469
and isinstance(converter.node.type, FunctionLike)):
469470
return Converter(converter.node.fullname())
471+
elif (isinstance(converter.node, OverloadedFuncDef)
472+
and is_valid_overloaded_converter(converter.node)):
473+
return Converter(converter.node.fullname())
470474
elif isinstance(converter.node, TypeInfo):
471475
return Converter(converter.node.fullname())
472476

@@ -490,6 +494,11 @@ def _parse_converter(ctx: 'mypy.plugin.ClassDefContext',
490494
return Converter(None)
491495

492496

497+
def is_valid_overloaded_converter(defn: OverloadedFuncDef) -> bool:
498+
return all((not isinstance(item, Decorator) or isinstance(item.func.type, FunctionLike))
499+
for item in defn.items)
500+
501+
493502
def _parse_assignments(
494503
lvalue: Expression,
495504
stmt: AssignmentStmt) -> Tuple[List[NameExpr], List[Expression]]:

test-data/unit/check-attr.test

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -577,10 +577,8 @@ class C:
577577

578578
[builtins fixtures/list.pyi]
579579

580-
-- This is tricky with new analyzer:
581-
-- We need to know the analyzed type of a function while still processing top-level.
582580
[case testAttrsUsingBadConverter]
583-
# flags: --no-new-semantic-analyzer --no-strict-optional
581+
# flags: --no-strict-optional
584582
import attr
585583
from typing import overload
586584
@overload
@@ -606,6 +604,34 @@ main:17: error: Argument "converter" has incompatible type overloaded function;
606604
main:18: note: Revealed type is 'def (bad: Any, bad_overloaded: Any) -> __main__.A'
607605
[builtins fixtures/list.pyi]
608606

607+
[case testAttrsUsingBadConverterReprocess]
608+
# flags: --no-strict-optional
609+
import attr
610+
from typing import overload
611+
forward: 'A'
612+
@overload
613+
def bad_overloaded_converter(x: int, y: int) -> int:
614+
...
615+
@overload
616+
def bad_overloaded_converter(x: str, y: str) -> str:
617+
...
618+
def bad_overloaded_converter(x, y=7):
619+
return x
620+
def bad_converter() -> str:
621+
return ''
622+
@attr.dataclass
623+
class A:
624+
bad: str = attr.ib(converter=bad_converter)
625+
bad_overloaded: int = attr.ib(converter=bad_overloaded_converter)
626+
reveal_type(A)
627+
[out]
628+
main:17: error: Cannot determine __init__ type from converter
629+
main:17: error: Argument "converter" has incompatible type "Callable[[], str]"; expected "Callable[[Any], str]"
630+
main:18: error: Cannot determine __init__ type from converter
631+
main:18: error: Argument "converter" has incompatible type overloaded function; expected "Callable[[Any], int]"
632+
main:19: note: Revealed type is 'def (bad: Any, bad_overloaded: Any) -> __main__.A'
633+
[builtins fixtures/list.pyi]
634+
609635
[case testAttrsUsingUnsupportedConverter]
610636
import attr
611637
class Thing:

0 commit comments

Comments
 (0)