Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show closest candidates for misspellings : keyword argument case #7888

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion mypy/checkexpr.py
Expand Up @@ -1284,7 +1284,8 @@ def check_for_extra_actual_arguments(self,
assert actual_names, "Internal error: named kinds without names given"
act_name = actual_names[i]
assert act_name is not None
messages.unexpected_keyword_argument(callee, act_name, context)
act_type = actual_types[i]
messages.unexpected_keyword_argument(callee, act_name, act_type, context)
is_unexpected_arg_error = True
elif ((kind == nodes.ARG_STAR and nodes.ARG_STAR not in callee.arg_kinds)
or kind == nodes.ARG_STAR2):
Expand Down
17 changes: 16 additions & 1 deletion mypy/messages.py
Expand Up @@ -570,9 +570,24 @@ def too_many_positional_arguments(self, callee: CallableType,
msg = 'Too many positional arguments' + for_function(callee)
self.fail(msg, context)

def unexpected_keyword_argument(self, callee: CallableType, name: str,
def unexpected_keyword_argument(self, callee: CallableType, name: str, arg_type: Type,
context: Context) -> None:
msg = 'Unexpected keyword argument "{}"'.format(name) + for_function(callee)
# Suggest intended keyword, look for type match else fallback on any match.
matching_type_args = []
not_matching_type_args = []
for i, kwarg_type in enumerate(callee.arg_types):
callee_arg_name = callee.arg_names[i]
if callee_arg_name is not None and callee.arg_kinds[i] != ARG_STAR:
if is_subtype(arg_type, kwarg_type):
matching_type_args.append(callee_arg_name)
else:
not_matching_type_args.append(callee_arg_name)
matches = best_matches(name, matching_type_args)
if not matches:
matches = best_matches(name, not_matching_type_args)
if matches:
msg += "; did you mean {}?".format(pretty_or(matches[:3]))
self.fail(msg, context, code=codes.CALL_ARG)
module = find_defining_module(self.modules, callee)
if module:
Expand Down
51 changes: 51 additions & 0 deletions test-data/unit/check-kwargs.test
Expand Up @@ -86,6 +86,57 @@ def f(a: 'A') -> None: pass # N: "f" defined here
f(b=object()) # E: Unexpected keyword argument "b" for "f"
class A: pass

[case testKeywordMisspelling]
def f(other: 'A') -> None: pass # N: "f" defined here
f(otter=A()) # E: Unexpected keyword argument "otter" for "f"; did you mean "other"?
class A: pass

[case testMultipleKeywordsForMisspelling]
def f(thing : 'A', other: 'A', atter: 'A', btter: 'B') -> None: pass # N: "f" defined here
f(otter=A()) # E: Unexpected keyword argument "otter" for "f"; did you mean "other" or "atter"?
class A: pass
class B: pass

[case testKeywordMisspellingDifferentType]
def f(other: 'A') -> None: pass # N: "f" defined here
f(otter=B()) # E: Unexpected keyword argument "otter" for "f"; did you mean "other"?
class A: pass
class B: pass

[case testKeywordMisspellingInheritance]
def f(atter: 'A', btter: 'B', ctter: 'C') -> None: pass # N: "f" defined here
f(otter=B()) # E: Unexpected keyword argument "otter" for "f"; did you mean "btter" or "atter"?
class A: pass
class B(A): pass
class C: pass

[case testKeywordMisspellingFloatInt]
def f(atter: float, btter: int) -> None: pass # N: "f" defined here
x: int = 5
f(otter=x) # E: Unexpected keyword argument "otter" for "f"; did you mean "btter" or "atter"?

[case testKeywordMisspellingVarArgs]
def f(other: 'A', *atter: 'A') -> None: pass # N: "f" defined here
f(otter=A()) # E: Unexpected keyword argument "otter" for "f"; did you mean "other"?
class A: pass

[case testKeywordMisspellingOnlyVarArgs]
def f(*other: 'A') -> None: pass # N: "f" defined here
f(otter=A()) # E: Unexpected keyword argument "otter" for "f"
class A: pass

[case testKeywordMisspellingVarArgsDifferentTypes]
def f(other: 'B', *atter: 'A') -> None: pass # N: "f" defined here
f(otter=A()) # E: Unexpected keyword argument "otter" for "f"; did you mean "other"?
class A: pass
class B: pass

[case testKeywordMisspellingVarKwargs]
def f(other: 'A', **atter: 'A') -> None: pass
f(otter=A()) # E: Missing positional argument "other" in call to "f"
class A: pass
[builtins fixtures/dict.pyi]

[case testKeywordArgumentsWithDynamicallyTypedCallable]
from typing import Any
f = None # type: Any
Expand Down