Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1566,12 +1566,14 @@ def check_lvalue(self, lvalue: Lvalue) -> Tuple[Type, IndexExpr, Var]:
elif isinstance(lvalue, IndexExpr):
index_lvalue = lvalue
elif isinstance(lvalue, MemberExpr):
lvalue_type = self.expr_checker.analyze_ordinary_member_access(lvalue,
True)
lvalue_type = self.expr_checker.analyze_ordinary_member_access(lvalue, True)
self.store_type(lvalue, lvalue_type)
elif isinstance(lvalue, NameExpr):
lvalue_type = self.expr_checker.analyze_ref_expr(lvalue, lvalue=True)
self.store_type(lvalue, lvalue_type)
if lvalue.name == '_':
lvalue_type = AnyType()
else:
self.store_type(lvalue, lvalue_type)
elif isinstance(lvalue, TupleExpr) or isinstance(lvalue, ListExpr):
types = [self.check_lvalue(sub_expr)[0] for sub_expr in lvalue.items]
lvalue_type = TupleType(types, self.named_type('builtins.tuple'))
Expand Down
2 changes: 2 additions & 0 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,8 @@ def make_argument(arg: ast3.arg, default: Optional[ast3.expr], kind: int) -> Arg
seen_names = set() # type: Set[str]
for name in names:
if name.arg in seen_names:
if name.arg == '_':
continue
self.fail("duplicate argument '{}' in function definition".format(name.arg),
name.lineno, name.col_offset)
break
Expand Down
2 changes: 2 additions & 0 deletions mypy/fastparse2.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,8 @@ def get_type(i: int) -> Optional[Type]:
seen_names = set() # type: Set[str]
for name in names:
if name in seen_names:
if name == '_':
continue
self.fail("duplicate argument '{}' in function definition".format(name), line, 0)
break
seen_names.add(name)
Expand Down
3 changes: 2 additions & 1 deletion mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3294,7 +3294,8 @@ def name_not_defined(self, name: str, ctx: Context) -> None:
self.fail(message, ctx)

def name_already_defined(self, name: str, ctx: Context) -> None:
self.fail("Name '{}' already defined".format(name), ctx)
if name != '_':
self.fail("Name '{}' already defined".format(name), ctx)

def fail(self, msg: str, ctx: Context, serious: bool = False, *,
blocker: bool = False) -> None:
Expand Down
10 changes: 10 additions & 0 deletions test-data/unit/check-basic.test
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,16 @@ main:3: error: Incompatible types in assignment (expression has type "B", variab
main:7: error: Incompatible types in assignment (expression has type "A", variable has type "B")
main:9: error: Incompatible types in assignment (expression has type "B", variable has type "A")

[case testUnderscoreVariableIsFresh]
_ = 1
_ = ""
_ = "" # type: int
(_, _) = ("", 1.0 + "") # E: Unsupported left operand type for + ("float")
(_, _) = (1.0, "")
for (_, _) in ((1, "a"), (2, "b")): pass

def f(_: int, _: str) -> None: pass
Copy link
Member

Choose a reason for hiding this comment

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

This actually gives SyntaxError: duplicate argument '_' in function definition at runtime (also in Python 2 with annotations removed).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops :) thanks!


[case testGlobalDefinedInBlockWithType]

class A: pass
Expand Down