diff --git a/mypy/messages.py b/mypy/messages.py index f6cadf026788..ccfc70a8051f 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -612,13 +612,10 @@ def unexpected_keyword_argument(self, callee: CallableType, name: str, if callee.name: msg += ' for {}'.format(callee.name) self.fail(msg, context) - if callee.definition: - fullname = callee.definition.fullname() - if fullname is not None and '.' in fullname: - module_name = fullname.rsplit('.', 1)[0] - path = self.modules[module_name].path - self.note('{} defined here'.format(callee.name), callee.definition, - file=path, origin=context) + module = find_defining_module(self.modules, callee) + if module: + self.note('{} defined here'.format(callee.name), callee.definition, + file=module.path, origin=context) def duplicate_argument_value(self, callee: CallableType, index: int, context: Context) -> None: @@ -946,6 +943,21 @@ def callable_name(type: CallableType) -> str: return 'function' +def find_defining_module(modules: Dict[str, MypyFile], typ: CallableType) -> MypyFile: + if not typ.definition: + return None + fullname = typ.definition.fullname() + if fullname is not None and '.' in fullname: + for i in range(fullname.count('.')): + module_name = fullname.rsplit('.', i + 1)[0] + try: + return modules[module_name] + except KeyError: + pass + assert False, "Couldn't determine module from CallableType" + return None + + def temp_message_builder() -> MessageBuilder: """Return a message builder usable for throwaway errors (which may not format properly).""" return MessageBuilder(Errors(), {}) diff --git a/mypy/semanal.py b/mypy/semanal.py index 95a7bf6e3630..3bde5cadf21c 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -328,6 +328,8 @@ def visit_func_def(self, defn: FuncDef) -> None: self.function_stack.append(defn) # First phase of analysis for function. self.errors.push_function(defn.name()) + if not defn._fullname: + defn._fullname = self.qualified_name(defn.name()) if defn.type: assert isinstance(defn.type, CallableType) self.update_function_type_variables(defn.type, defn) diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index c978c0e83353..1688d61b8c22 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1932,7 +1932,7 @@ main:3: note: "f" defined here [case testMagicMethodPositionalOnlyArg] class A(object): - def __eq__(self, other) -> bool: return True # We are all equal. + def __eq__(self, other) -> bool: return True # We are all equal. # N: "__eq__" of "A" defined here a = A() a.__eq__(a) @@ -1944,7 +1944,7 @@ a.__eq__(other=a) # E: Unexpected keyword argument "other" for "__eq__" of "A" class A(object): - def __eq__(self, other) -> bool: return True # We are all equal. + def __eq__(self, other) -> bool: return True # We are all equal. # N: "__eq__" of "A" defined here a = A() a.__eq__(a) diff --git a/test-data/unit/check-kwargs.test b/test-data/unit/check-kwargs.test index 61c0a5a0d5d7..de229c76e631 100644 --- a/test-data/unit/check-kwargs.test +++ b/test-data/unit/check-kwargs.test @@ -320,7 +320,7 @@ f(y=0) # E: Unexpected keyword argument "y" for "f" [case testKeywordArgumentAndCommentSignature2] import typing class A: - def f(self, x): # type: (int) -> str + def f(self, x): # type: (int) -> str # N: "f" of "A" defined here pass A().f(x='') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "int" A().f(x=0) @@ -359,3 +359,18 @@ f(**c) # E: Keywords must be strings def f(**k): pass f(*(1, 2)) # E: Too many arguments for "f" [builtins fixtures/dict.pyi] + +[case testUnexpectedMethodKwargInNestedClass] +class A: + class B: + def __init__(self) -> None: # N: "B" defined here + pass +A.B(x=1) # E: Unexpected keyword argument "x" for "B" + +[case testUnexpectedMethodKwargFromOtherModule] +import m +m.A(x=1) # E: Unexpected keyword argument "x" for "A" +[file m.py] +class A: + def __init__(self) -> None: # N: "A" defined here + pass diff --git a/test-data/unit/semanal-classes.test b/test-data/unit/semanal-classes.test index bb79b4264106..96c6ffe32523 100644 --- a/test-data/unit/semanal-classes.test +++ b/test-data/unit/semanal-classes.test @@ -270,7 +270,7 @@ MypyFile:1( PassStmt:2())) AssignmentStmt:3( NameExpr(g* [m]) - NameExpr(f [m])))) + NameExpr(f [__main__.A.f])))) [case testIfStatementInClassBody] class A: