Repository: python/mypy

Issue:
Misleading error message for multiple defined function
Consider this code:

```
def f() -> int: return 1
def f() -> int: return 2
```

The type checker gives this type error:

```
t.py, line 2: Name 'f' already defined (overload variants 
must be next to each other)
```

This is misleading, since the functions are next to each other and this is not a case of overloading but redefinition.



Discussion:
What would the ideal error message be in this case? This is what Haskell does:

``` haskell
foo :: Int
foo = 1
foo = 2
```

```
$ ghc Foo.hs 
[1 of 1] Compiling Main             ( Foo.hs, Foo.o )

Foo.hs:3:1:
    Multiple declarations of ‘foo’
    Declared at: Foo.hs:2:1
                 Foo.hs:3:1
```

This is what Elm does (a high standard to pursue):

```
Detected errors in 1 module.

-- DUPLICATE DEFINITION --------------------------------------------------------

Naming multiple top-level values `foo` makes things ambiguous. When you say
`foo` which one do you want?

10| foo =
    ^^^
Find all the top-level values named `foo` and do some renaming. Make sure the
names are distinct!
```

TypeScript says

```
tmp.ts(4,10): error TS2393: Duplicate function implementation.
tmp.ts(7,10): error TS2393: Duplicate function implementation.
```

The desired message is `Name 'f' already defined` or possibly something including the line number of the previous definition.

It looks to me like this error message is never actually used for multiply defined functions (because those don't exist in user code) or for overloads being out of order (I get different bad errors when I try that), so all there is to this issue is changing the message. I'm not familiar with the project at all though, can anyone confirm?


Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -2209,11 +2209,7 @@ def add_local(self, node: Union[Var, FuncBase], ctx: Context) -> None:
     def check_no_global(self, n: str, ctx: Context,
                         is_func: bool = False) -> None:
         if n in self.globals:
-            if is_func and isinstance(self.globals[n].node, FuncDef):
-                self.fail(("Name '{}' already defined (overload variants "
-                           "must be next to each other)").format(n), ctx)
-            else:
-                self.name_already_defined(n, ctx)
+            self.name_already_defined(n, ctx)
 
     def name_not_defined(self, name: str, ctx: Context) -> None:
         message = "Name '{}' is not defined".format(name)


Test patch:
diff --git a/mypy/test/data/check-functions.test b/mypy/test/data/check-functions.test
--- a/mypy/test/data/check-functions.test
+++ b/mypy/test/data/check-functions.test
@@ -1073,7 +1073,7 @@ from typing import Any
 x = None # type: Any
 if x:
     def f(): pass
-def f(): pass # E: Name 'f' already defined (overload variants must be next to each other)
+def f(): pass # E: Name 'f' already defined
 
 [case testIncompatibleConditionalFunctionDefinition]
 from typing import Any
@@ -1515,6 +1515,10 @@ def h(y): pass
 f = h
 f = g # E: Incompatible types in assignment (expression has type Callable[[Any, Any], Any], variable has type Callable[[Any], Any])
 
+[case testRedefineFunction2]
+def f() -> None: pass
+def f() -> None: pass # E: Name 'f' already defined
+
 
 -- Special cases
 -- -------------
diff --git a/mypy/test/data/check-overloading.test b/mypy/test/data/check-overloading.test
--- a/mypy/test/data/check-overloading.test
+++ b/mypy/test/data/check-overloading.test
@@ -91,12 +91,6 @@ class A:
   def f(self, x: 'B') -> 'B': pass
 class B: pass
 
-[case testNonConsecutiveOverloads]
-from typing import Any
-def f() -> None: pass
-def g() -> None: pass
-def f(x: Any) -> None: pass # E: Name 'f' already defined (overload variants must be next to each other)
-
 [case testOverloadsWithDifferentArgumentCounts]
 from typing import overload
 a, b = None, None # type: (A, B)