Skip to content

Commit cd6712d

Browse files
authored
Original node wins in case of a redefinition (especially if it is a class) (#5565)
Fixes #5534
1 parent 5bf3c74 commit cd6712d

File tree

3 files changed

+158
-1
lines changed

3 files changed

+158
-1
lines changed

mypy/semanal.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,10 +387,12 @@ def _visit_func_def(self, defn: FuncDef) -> None:
387387
# a common stack of namespaces. As the 3 kinds of namespaces have
388388
# different semantics, this wouldn't always work, but it might still
389389
# be a win.
390+
# Also we can re-use some logic in self.add_symbol().
390391
if self.is_class_scope():
391392
# Method definition
392393
assert self.type is not None, "Type not set at class scope"
393394
defn.info = self.type
395+
add_symbol = True
394396
if not defn.is_decorated and not defn.is_overload:
395397
if (defn.name() in self.type.names and
396398
self.type.names[defn.name()].node != defn):
@@ -399,7 +401,9 @@ def _visit_func_def(self, defn: FuncDef) -> None:
399401
if not self.set_original_def(n, defn):
400402
self.name_already_defined(defn.name(), defn,
401403
self.type.names[defn.name()])
402-
self.type.names[defn.name()] = SymbolTableNode(MDEF, defn)
404+
add_symbol = False
405+
if add_symbol:
406+
self.type.names[defn.name()] = SymbolTableNode(MDEF, defn)
403407
self.prepare_method_signature(defn, self.type)
404408
elif self.is_func_scope():
405409
# Nested function
@@ -3277,15 +3281,21 @@ def add_symbol(self, name: str, node: SymbolTableNode,
32773281
context: Context) -> None:
32783282
# NOTE: This logic mostly parallels SemanticAnalyzerPass1.add_symbol. If you change
32793283
# this, you may have to change the other method as well.
3284+
# TODO: Combine these methods in the first and second pass into a single one.
32803285
if self.is_func_scope():
32813286
assert self.locals[-1] is not None
32823287
if name in self.locals[-1]:
32833288
# Flag redefinition unless this is a reimport of a module.
32843289
if not (node.kind == MODULE_REF and
32853290
self.locals[-1][name].node == node.node):
32863291
self.name_already_defined(name, context, self.locals[-1][name])
3292+
return
32873293
self.locals[-1][name] = node
32883294
elif self.type:
3295+
existing = self.type.names.get(name)
3296+
if existing and existing.node != node.node:
3297+
self.name_already_defined(name, context, existing)
3298+
return
32893299
self.type.names[name] = node
32903300
else:
32913301
existing = self.globals.get(name)
@@ -3301,13 +3311,15 @@ def add_symbol(self, name: str, node: SymbolTableNode,
33013311
ok = True
33023312
if not ok:
33033313
self.name_already_defined(name, context, existing)
3314+
return
33043315
self.globals[name] = node
33053316

33063317
def add_local(self, node: Union[Var, FuncDef, OverloadedFuncDef], ctx: Context) -> None:
33073318
assert self.locals[-1] is not None, "Should not add locals outside a function"
33083319
name = node.name()
33093320
if name in self.locals[-1]:
33103321
self.name_already_defined(name, ctx, self.locals[-1][name])
3322+
return
33113323
node._fullname = name
33123324
self.locals[-1][name] = SymbolTableNode(LDEF, node)
33133325

mypy/semanal_pass1.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ def add_symbol(self, name: str, node: SymbolTableNode,
348348
if not (node.kind == MODULE_REF and
349349
self.sem.locals[-1][name].node == node.node):
350350
self.sem.name_already_defined(name, context, self.sem.locals[-1][name])
351+
return
351352
self.sem.locals[-1][name] = node
352353
else:
353354
assert self.sem.type is None # Pass 1 doesn't look inside classes
@@ -364,5 +365,6 @@ def add_symbol(self, name: str, node: SymbolTableNode,
364365
ok = True
365366
if not ok:
366367
self.sem.name_already_defined(name, context, existing)
368+
return
367369
elif not existing:
368370
self.sem.globals[name] = node

test-data/unit/check-incremental.test

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4885,6 +4885,149 @@ def f(x: str) -> None: pass
48854885
[out2]
48864886
main:2: error: Argument 1 to "f" has incompatible type "int"; expected "str"
48874887

4888+
-- These tests should just not crash
4889+
[case testOverrideByBadVar]
4890+
import a
4891+
[file a.py]
4892+
import lib
4893+
x = 1
4894+
[file a.py.2]
4895+
import lib
4896+
x = 2
4897+
[file lib.py]
4898+
class Slow:
4899+
pass
4900+
4901+
s: Slow
4902+
from cext import Slow # type: ignore
4903+
[out]
4904+
[out2]
4905+
4906+
[case testOverrideByBadVarAlias]
4907+
import a
4908+
[file a.py]
4909+
import lib
4910+
x = 1
4911+
[file a.py.2]
4912+
import lib
4913+
x = 2
4914+
[file lib.py]
4915+
class Slow:
4916+
pass
4917+
4918+
A = Slow
4919+
from cext import Slow # type: ignore
4920+
[out]
4921+
[out2]
4922+
4923+
[case testOverrideByBadVarClass]
4924+
import a
4925+
[file a.py]
4926+
import lib
4927+
x = 1
4928+
[file a.py.2]
4929+
import lib
4930+
x = 2
4931+
[file lib.py]
4932+
class C:
4933+
class Slow:
4934+
pass
4935+
s: Slow
4936+
from cext import Slow # type: ignore
4937+
[out]
4938+
[out2]
4939+
4940+
[case testOverrideByBadVarClassAlias]
4941+
import a
4942+
[file a.py]
4943+
import lib
4944+
x = 1
4945+
[file a.py.2]
4946+
import lib
4947+
x = 2
4948+
[file lib.py]
4949+
class C:
4950+
class Slow:
4951+
pass
4952+
A = Slow
4953+
from cext import Slow # type: ignore
4954+
[out]
4955+
[out2]
4956+
4957+
[case testOverrideByBadVarExisting]
4958+
import a
4959+
[file a.py]
4960+
import lib
4961+
x = 1
4962+
[file a.py.2]
4963+
import lib
4964+
x = 2
4965+
[file lib.py]
4966+
class Slow:
4967+
pass
4968+
4969+
s: Slow
4970+
from cext import Slow # type: ignore
4971+
[file cext.py]
4972+
Slow = 1
4973+
[out]
4974+
[out2]
4975+
4976+
[case testOverrideByBadVarAliasExisting]
4977+
import a
4978+
[file a.py]
4979+
import lib
4980+
x = 1
4981+
[file a.py.2]
4982+
import lib
4983+
x = 2
4984+
[file lib.py]
4985+
class Slow:
4986+
pass
4987+
4988+
A = Slow
4989+
from cext import Slow # type: ignore
4990+
[file cext.py]
4991+
Slow = 1
4992+
[out]
4993+
[out2]
4994+
4995+
[case testOverrideByBadFunction]
4996+
import a
4997+
[file a.py]
4998+
import lib
4999+
x = 1
5000+
[file a.py.2]
5001+
import lib
5002+
x = 2
5003+
[file lib.py]
5004+
class C:
5005+
class Slow:
5006+
pass
5007+
5008+
s: Slow
5009+
def Slow() -> None: ... # type: ignore
5010+
[out]
5011+
[out2]
5012+
5013+
[case testOverrideByBadVarLocal]
5014+
import a
5015+
[file a.py]
5016+
import lib
5017+
x = 1
5018+
[file a.py.2]
5019+
import lib
5020+
x = 2
5021+
[file lib.py]
5022+
def outer() -> None:
5023+
class Slow:
5024+
pass
5025+
5026+
s: Slow
5027+
from cext import Slow # type: ignore
5028+
[out]
5029+
[out2]
5030+
48885031
[case testFollowImportSkipNotInvalidatedOnPresent]
48895032
# flags: --follow-imports=skip
48905033
# cmd: mypy -m main

0 commit comments

Comments
 (0)