Skip to content

Commit 48c6425

Browse files
miedzinskiilevkivskyi
authored andcommitted
Fix crash when annotating callable definition location (#3375)
* Fix crash when annotating callable definition location * Use more friendly range args * Set methods' fullname during 2nd pass
1 parent 3cd62e1 commit 48c6425

File tree

5 files changed

+40
-11
lines changed

5 files changed

+40
-11
lines changed

mypy/messages.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -607,13 +607,10 @@ def unexpected_keyword_argument(self, callee: CallableType, name: str,
607607
if callee.name:
608608
msg += ' for {}'.format(callee.name)
609609
self.fail(msg, context)
610-
if callee.definition:
611-
fullname = callee.definition.fullname()
612-
if fullname is not None and '.' in fullname:
613-
module_name = fullname.rsplit('.', 1)[0]
614-
path = self.modules[module_name].path
615-
self.note('{} defined here'.format(callee.name), callee.definition,
616-
file=path, origin=context)
610+
module = find_defining_module(self.modules, callee)
611+
if module:
612+
self.note('{} defined here'.format(callee.name), callee.definition,
613+
file=module.path, origin=context)
617614

618615
def duplicate_argument_value(self, callee: CallableType, index: int,
619616
context: Context) -> None:
@@ -941,6 +938,21 @@ def callable_name(type: CallableType) -> str:
941938
return 'function'
942939

943940

941+
def find_defining_module(modules: Dict[str, MypyFile], typ: CallableType) -> MypyFile:
942+
if not typ.definition:
943+
return None
944+
fullname = typ.definition.fullname()
945+
if fullname is not None and '.' in fullname:
946+
for i in range(fullname.count('.')):
947+
module_name = fullname.rsplit('.', i + 1)[0]
948+
try:
949+
return modules[module_name]
950+
except KeyError:
951+
pass
952+
assert False, "Couldn't determine module from CallableType"
953+
return None
954+
955+
944956
def temp_message_builder() -> MessageBuilder:
945957
"""Return a message builder usable for throwaway errors (which may not format properly)."""
946958
return MessageBuilder(Errors(), {})

mypy/semanal.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,8 @@ def visit_func_def(self, defn: FuncDef) -> None:
328328
self.function_stack.append(defn)
329329
# First phase of analysis for function.
330330
self.errors.push_function(defn.name())
331+
if not defn._fullname:
332+
defn._fullname = self.qualified_name(defn.name())
331333
if defn.type:
332334
assert isinstance(defn.type, CallableType)
333335
self.update_function_type_variables(defn.type, defn)

test-data/unit/check-functions.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1932,7 +1932,7 @@ main:3: note: "f" defined here
19321932

19331933
[case testMagicMethodPositionalOnlyArg]
19341934
class A(object):
1935-
def __eq__(self, other) -> bool: return True # We are all equal.
1935+
def __eq__(self, other) -> bool: return True # We are all equal. # N: "__eq__" of "A" defined here
19361936

19371937
a = A()
19381938
a.__eq__(a)
@@ -1944,7 +1944,7 @@ a.__eq__(other=a) # E: Unexpected keyword argument "other" for "__eq__" of "A"
19441944

19451945

19461946
class A(object):
1947-
def __eq__(self, other) -> bool: return True # We are all equal.
1947+
def __eq__(self, other) -> bool: return True # We are all equal. # N: "__eq__" of "A" defined here
19481948

19491949
a = A()
19501950
a.__eq__(a)

test-data/unit/check-kwargs.test

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ f(y=0) # E: Unexpected keyword argument "y" for "f"
320320
[case testKeywordArgumentAndCommentSignature2]
321321
import typing
322322
class A:
323-
def f(self, x): # type: (int) -> str
323+
def f(self, x): # type: (int) -> str # N: "f" of "A" defined here
324324
pass
325325
A().f(x='') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "int"
326326
A().f(x=0)
@@ -359,3 +359,18 @@ f(**c) # E: Keywords must be strings
359359
def f(**k): pass
360360
f(*(1, 2)) # E: Too many arguments for "f"
361361
[builtins fixtures/dict.pyi]
362+
363+
[case testUnexpectedMethodKwargInNestedClass]
364+
class A:
365+
class B:
366+
def __init__(self) -> None: # N: "B" defined here
367+
pass
368+
A.B(x=1) # E: Unexpected keyword argument "x" for "B"
369+
370+
[case testUnexpectedMethodKwargFromOtherModule]
371+
import m
372+
m.A(x=1) # E: Unexpected keyword argument "x" for "A"
373+
[file m.py]
374+
class A:
375+
def __init__(self) -> None: # N: "A" defined here
376+
pass

test-data/unit/semanal-classes.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ MypyFile:1(
270270
PassStmt:2()))
271271
AssignmentStmt:3(
272272
NameExpr(g* [m])
273-
NameExpr(f [m]))))
273+
NameExpr(f [__main__.A.f]))))
274274

275275
[case testIfStatementInClassBody]
276276
class A:

0 commit comments

Comments
 (0)