From 82c53135f7433e82fd9efbb1d4d74914bdbdc7fe Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Mon, 22 May 2017 11:34:30 -0700 Subject: [PATCH 1/7] Add line # of previous definition --- mypy/semanal.py | 11 ++++++++--- test-data/unit/check-classes.test | 2 +- test-data/unit/check-functions.test | 4 ++-- test-data/unit/check-overloading.test | 2 +- test-data/unit/semanal-errors.test | 10 +++++----- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index b14e3dd9076e..381a6160b6ab 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3315,7 +3315,7 @@ def check_no_global(self, n: str, ctx: Context, elif prev_is_overloaded: self.fail("Definition of '{}' missing 'overload'".format(n), ctx) else: - self.name_already_defined(n, ctx) + self.name_already_defined(n, ctx, self.globals[n]) def name_not_defined(self, name: str, ctx: Context) -> None: message = "Name '{}' is not defined".format(name) @@ -3324,8 +3324,13 @@ def name_not_defined(self, name: str, ctx: Context) -> None: message += ' {}'.format(extra) self.fail(message, ctx) - def name_already_defined(self, name: str, ctx: Context) -> None: - self.fail("Name '{}' already defined".format(name), ctx) + def name_already_defined(self, name: str, ctx: Context, + original_ctx: Optional[SymbolTableNode] = None) -> None: + if original_ctx: + extra_msg = ' in line {}'.format(original_ctx.node.get_line()) + else: + extra_msg = '' + self.fail("Name '{}' already defined{}".format(name, extra_msg), ctx) def fail(self, msg: str, ctx: Context, serious: bool = False, *, blocker: bool = False) -> None: diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index a257fa0375f0..3b9084b1217f 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -714,7 +714,7 @@ A() class A: pass class A: pass [out] -main:4: error: Name 'A' already defined +main:4: error: Name 'A' already defined in line 3 [case testDocstringInClass] import typing diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 8ac35e052659..feb789957837 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1212,7 +1212,7 @@ from typing import Any x = None # type: Any if x: def f(): pass -def f(): pass # E: Name 'f' already defined +def f(): pass # E: Name 'f' already defined in line 4 [case testIncompatibleConditionalFunctionDefinition] from typing import Any @@ -1835,7 +1835,7 @@ f = g # E: Incompatible types in assignment (expression has type Callable[[Any, [case testRedefineFunction2] def f() -> None: pass -def f() -> None: pass # E: Name 'f' already defined +def f() -> None: pass # E: Name 'f' already defined in line 1 -- Special cases diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 69289fae18c1..d4a1b448fdb2 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -1106,7 +1106,7 @@ def f(a: int) -> None: pass def f(a: str) -> None: pass [out] tmp/foo.pyi:2: error: Single overload definition, multiple required -tmp/foo.pyi:5: error: Name 'f' already defined +tmp/foo.pyi:5: error: Name 'f' already defined in line 2 [case testOverloadTuple] from foo import * diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 99584e77a977..0692b79c96de 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -165,7 +165,7 @@ import typing class A: pass class A: pass [out] -main:3: error: Name 'A' already defined +main:3: error: Name 'A' already defined in line 2 [case testMultipleMixedDefinitions] import typing @@ -173,8 +173,8 @@ x = 1 def x(): pass class x: pass [out] -main:3: error: Name 'x' already defined -main:4: error: Name 'x' already defined +main:3: error: Name 'x' already defined in line 2 +main:4: error: Name 'x' already defined in line 2 [case testNameNotImported] import typing @@ -1037,13 +1037,13 @@ t = 1 # E: Invalid assignment target [case testRedefineTypevar2] from typing import TypeVar t = TypeVar('t') -def t(): pass # E: Name 't' already defined +def t(): pass # E: Name 't' already defined in line 2 [out] [case testRedefineTypevar3] from typing import TypeVar t = TypeVar('t') -class t: pass # E: Name 't' already defined +class t: pass # E: Name 't' already defined in line 2 [out] [case testRedefineTypevar4] From 5b077aeb19f351f2b4e8293d71c1672855928054 Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Mon, 22 May 2017 14:39:01 -0700 Subject: [PATCH 2/7] CR fix --- mypy/semanal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 381a6160b6ab..024bb7960277 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3327,7 +3327,7 @@ def name_not_defined(self, name: str, ctx: Context) -> None: def name_already_defined(self, name: str, ctx: Context, original_ctx: Optional[SymbolTableNode] = None) -> None: if original_ctx: - extra_msg = ' in line {}'.format(original_ctx.node.get_line()) + extra_msg = ' on line {}'.format(original_ctx.node.get_line()) else: extra_msg = '' self.fail("Name '{}' already defined{}".format(name, extra_msg), ctx) From 39c30589235a479fe2401d3a1be170dfbcb2c90b Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Mon, 22 May 2017 14:46:30 -0700 Subject: [PATCH 3/7] Fix tests --- test-data/unit/check-classes.test | 2 +- test-data/unit/check-functions.test | 4 ++-- test-data/unit/check-overloading.test | 2 +- test-data/unit/semanal-errors.test | 10 +++++----- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 3b9084b1217f..3e9ab9924809 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -714,7 +714,7 @@ A() class A: pass class A: pass [out] -main:4: error: Name 'A' already defined in line 3 +main:4: error: Name 'A' already defined on line 3 [case testDocstringInClass] import typing diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index feb789957837..fb76185569d5 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1212,7 +1212,7 @@ from typing import Any x = None # type: Any if x: def f(): pass -def f(): pass # E: Name 'f' already defined in line 4 +def f(): pass # E: Name 'f' already defined on line 4 [case testIncompatibleConditionalFunctionDefinition] from typing import Any @@ -1835,7 +1835,7 @@ f = g # E: Incompatible types in assignment (expression has type Callable[[Any, [case testRedefineFunction2] def f() -> None: pass -def f() -> None: pass # E: Name 'f' already defined in line 1 +def f() -> None: pass # E: Name 'f' already defined on line 1 -- Special cases diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index d4a1b448fdb2..f3e5b993a325 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -1106,7 +1106,7 @@ def f(a: int) -> None: pass def f(a: str) -> None: pass [out] tmp/foo.pyi:2: error: Single overload definition, multiple required -tmp/foo.pyi:5: error: Name 'f' already defined in line 2 +tmp/foo.pyi:5: error: Name 'f' already defined on line 2 [case testOverloadTuple] from foo import * diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 0692b79c96de..622008f7fb95 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -165,7 +165,7 @@ import typing class A: pass class A: pass [out] -main:3: error: Name 'A' already defined in line 2 +main:3: error: Name 'A' already defined on line 2 [case testMultipleMixedDefinitions] import typing @@ -173,8 +173,8 @@ x = 1 def x(): pass class x: pass [out] -main:3: error: Name 'x' already defined in line 2 -main:4: error: Name 'x' already defined in line 2 +main:3: error: Name 'x' already defined on line 2 +main:4: error: Name 'x' already defined on line 2 [case testNameNotImported] import typing @@ -1037,13 +1037,13 @@ t = 1 # E: Invalid assignment target [case testRedefineTypevar2] from typing import TypeVar t = TypeVar('t') -def t(): pass # E: Name 't' already defined in line 2 +def t(): pass # E: Name 't' already defined on line 2 [out] [case testRedefineTypevar3] from typing import TypeVar t = TypeVar('t') -class t: pass # E: Name 't' already defined in line 2 +class t: pass # E: Name 't' already defined on line 2 [out] [case testRedefineTypevar4] From 8449ed5e6466e18b819b985b0d9c826267def622 Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Tue, 23 May 2017 09:49:15 -0700 Subject: [PATCH 4/7] Add failiing test --- test-data/unit/semanal-errors.test | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 622008f7fb95..7b9b4583b9a1 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1330,3 +1330,15 @@ a = ('spam', 'spam', 'eggs', 'spam') # type: Tuple[str] [out] main:3: error: Name 'a' already defined main:4: error: Name 'a' already defined + +[case testUnknownContextDuplicateDef] +try: + from m import A +except ImportError: + class A: + pass +[file m.py] +class A: + pass +[out] +main:4: error: Name 'A' already defined (line unavailable; possibly an import) From 295c2037ef918c1e0de83983353533366956c886 Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Tue, 23 May 2017 09:35:54 -0700 Subject: [PATCH 5/7] Fix the case where original context is None --- mypy/semanal.py | 5 ++++- test-data/unit/semanal-errors.test | 10 ++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 024bb7960277..648207445f45 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3327,7 +3327,10 @@ def name_not_defined(self, name: str, ctx: Context) -> None: def name_already_defined(self, name: str, ctx: Context, original_ctx: Optional[SymbolTableNode] = None) -> None: if original_ctx: - extra_msg = ' on line {}'.format(original_ctx.node.get_line()) + if original_ctx.node and original_ctx.node.get_line() != -1: + extra_msg = ' on line {}'.format(original_ctx.node.get_line()) + else: + extra_msg = ' (line unavailable; possibly an import)' else: extra_msg = '' self.fail("Name '{}' already defined{}".format(name, extra_msg), ctx) diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 7b9b4583b9a1..5b2d7c798de5 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1332,13 +1332,11 @@ main:3: error: Name 'a' already defined main:4: error: Name 'a' already defined [case testUnknownContextDuplicateDef] -try: - from m import A -except ImportError: - class A: - pass +from m import A +class A: + pass [file m.py] class A: pass [out] -main:4: error: Name 'A' already defined (line unavailable; possibly an import) +main:2: error: Name 'A' already defined (line unavailable; possibly an import) From e1bfb11d6952345a93cfc5e7d8f255f03fcb4125 Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Tue, 23 May 2017 11:40:59 -0700 Subject: [PATCH 6/7] CR fixes --- mypy/semanal.py | 2 +- test-data/unit/semanal-errors.test | 59 +++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 648207445f45..6e7fad1f7adf 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3330,7 +3330,7 @@ def name_already_defined(self, name: str, ctx: Context, if original_ctx.node and original_ctx.node.get_line() != -1: extra_msg = ' on line {}'.format(original_ctx.node.get_line()) else: - extra_msg = ' (line unavailable; possibly an import)' + extra_msg = ' (possibly by an import)' else: extra_msg = '' self.fail("Name '{}' already defined{}".format(name, extra_msg), ctx) diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 5b2d7c798de5..3efdfdc7408e 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1331,7 +1331,7 @@ a = ('spam', 'spam', 'eggs', 'spam') # type: Tuple[str] main:3: error: Name 'a' already defined main:4: error: Name 'a' already defined -[case testUnknownContextDuplicateDef] +[case testDuplicateDefFromImport] from m import A class A: pass @@ -1339,4 +1339,59 @@ class A: class A: pass [out] -main:2: error: Name 'A' already defined (line unavailable; possibly an import) +main:2: error: Name 'A' already defined (possibly by an import) + +[case testDuplicateDefDec] +from typing import Any +def dec(x: Any) -> Any: + return x +@dec +def f() -> None: + pass +@dec +def f() -> None: + pass +[out] +main:7: error: Name 'f' already defined + +[case testDuplicateDefOverload] +from typing import overload +if 1: + @overload + def f(x: int) -> None: + pass + @overload + def f(x: str) -> None: + pass + def f(x: Any) -> None: + pass +else: + def f(x: str) -> None: + pass +[out] +main:12: error: Name 'f' already defined on line 3 + + +[case testDuplicateDefNT] +from typing import NamedTuple +N = NamedTuple('N', [('a', int), + ('b', str)]) + +class N: + pass +[out] +main:5: error: Name 'N' already defined on line 2 + + +[case testDuplicateDefTypedDict] +from mypy_extensions import TypedDict +Point = TypedDict('Point', {'x': int, 'y': int}) + +class Point: + pass +[builtins fixtures/dict.pyi] + +[out] +main:4: error: Name 'Point' already defined on line 2 + + From 3d77de62c0cb3feba7e73fbf5ec4dd44c0ede63b Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Wed, 31 May 2017 19:19:26 -0700 Subject: [PATCH 7/7] CR --- test-data/unit/check-semanal-error.test | 16 +++++++++++++ test-data/unit/semanal-errors.test | 30 +++++++++++++++---------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/test-data/unit/check-semanal-error.test b/test-data/unit/check-semanal-error.test index 0a078290a1a7..1869bbe8e366 100644 --- a/test-data/unit/check-semanal-error.test +++ b/test-data/unit/check-semanal-error.test @@ -79,3 +79,19 @@ yield # E: 'yield' outside function [case testYieldFromOutsideFunction] x = 1 yield from x # E: 'yield from' outside function + +[case testImportFuncDup] +import m +def m() -> None: ... # ok + +[file m.py] +[out] + +[case testIgnoredImportDup] +import m # type: ignore +from m import f # type: ignore +def m() -> None: ... # ok +def f() -> None: ... # ok + +[out] + diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 3efdfdc7408e..9c7713d9cdb7 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1333,13 +1333,12 @@ main:4: error: Name 'a' already defined [case testDuplicateDefFromImport] from m import A -class A: +class A: # E: Name 'A' already defined (possibly by an import) pass [file m.py] class A: pass [out] -main:2: error: Name 'A' already defined (possibly by an import) [case testDuplicateDefDec] from typing import Any @@ -1348,11 +1347,10 @@ def dec(x: Any) -> Any: @dec def f() -> None: pass -@dec +@dec # E: Name 'f' already defined def f() -> None: pass [out] -main:7: error: Name 'f' already defined [case testDuplicateDefOverload] from typing import overload @@ -1366,32 +1364,40 @@ if 1: def f(x: Any) -> None: pass else: - def f(x: str) -> None: + def f(x: str) -> None: # E: Name 'f' already defined on line 3 pass [out] -main:12: error: Name 'f' already defined on line 3 - [case testDuplicateDefNT] from typing import NamedTuple N = NamedTuple('N', [('a', int), ('b', str)]) -class N: +class N: # E: Name 'N' already defined on line 2 pass [out] -main:5: error: Name 'N' already defined on line 2 - [case testDuplicateDefTypedDict] from mypy_extensions import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) -class Point: +class Point: # E: Name 'Point' already defined on line 2 pass [builtins fixtures/dict.pyi] [out] -main:4: error: Name 'Point' already defined on line 2 +[case testTypeVarClassDup] +from typing import TypeVar +T = TypeVar('T') +class T: ... # E: Name 'T' already defined on line 2 + +[out] +[case testAliasDup] +from typing import List +A = List[int] +class A: ... # E: Name 'A' already defined on line 2 + +[builtins fixtures/list.pyi] +[out]