Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions mypy/newsemanal/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2828,7 +2828,7 @@ def process_typevar_parameters(self, args: List[Expression],
# T = TypeVar('T', bound=Custom[Any])
# class Custom(Generic[T]):
# ...
analyzed = PlaceholderType('<unknown>', [], context.line)
analyzed = PlaceholderType(None, [], context.line)
upper_bound = analyzed
if isinstance(upper_bound, AnyType) and upper_bound.is_from_error:
self.fail("TypeVar 'bound' must be a type", param_value)
Expand Down Expand Up @@ -2893,7 +2893,7 @@ def analyze_value_types(self, items: List[Expression]) -> List[Type]:
# Type variables are special: we need to place them in the symbol table
# soon, even if some value is not ready yet, see process_typevar_parameters()
# for an example.
analyzed = PlaceholderType('<unknown>', [], node.line)
analyzed = PlaceholderType(None, [], node.line)
result.append(analyzed)
except TypeTranslationError:
self.fail('Type expected', node)
Expand Down
4 changes: 2 additions & 2 deletions mypy/newsemanal/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,8 +540,8 @@ def visit_forwardref_type(self, t: ForwardRef) -> Type:
return t

def visit_placeholder_type(self, t: PlaceholderType) -> Type:
n = self.api.lookup_fully_qualified(t.fullname)
if isinstance(n.node, PlaceholderNode):
n = None if t.fullname is None else self.api.lookup_fully_qualified(t.fullname)
if not n or isinstance(n.node, PlaceholderNode):
self.api.defer() # Still incomplete
return t
else:
Expand Down
4 changes: 2 additions & 2 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1871,9 +1871,9 @@ class str(Sequence[str]): ...
exist.
"""

def __init__(self, fullname: str, args: List[Type], line: int) -> None:
def __init__(self, fullname: Optional[str], args: List[Type], line: int) -> None:
super().__init__(line)
self.fullname = fullname # Only used for debugging
self.fullname = fullname # Must be a valid full name of an actual node (or None).
self.args = args

def accept(self, visitor: 'TypeVisitor[T]') -> T:
Expand Down
50 changes: 50 additions & 0 deletions test-data/unit/check-newsemanal.test
Original file line number Diff line number Diff line change
Expand Up @@ -2795,3 +2795,53 @@ S = TypeVar('S', covariant=True, contravariant=True) # E: TypeVar cannot be bot

class Yes: ...
[builtins fixtures/bool.pyi]

[case testNewAnalyzerVarTypeVarNoCrash]
from typing import Callable, TypeVar

FooT = TypeVar('FooT', bound='Foo')
class Foo: ...

f = lambda x: True # type: Callable[[FooT], bool]
reveal_type(f) # N: Revealed type is 'def [FooT <: __main__.Foo] (FooT`-1) -> builtins.bool'
[builtins fixtures/bool.pyi]

[case testNewAnalyzerVarTypeVarNoCrashImportCycle]
import a

[file a.py]
from b import B
from typing import TypeVar

FooT = TypeVar('FooT', bound='Foo')
class Foo: ...

[file b.py]
from a import FooT
from typing import Callable

f = lambda x: True # type: Callable[[FooT], bool]
reveal_type(f) # N: Revealed type is 'def [FooT <: a.Foo] (FooT`-1) -> builtins.bool'

class B: ...
[builtins fixtures/bool.pyi]

[case testNewAnalyzerFuncTypeVarNoCrashImportCycle]
import a

[file a.py]
from b import B
from typing import TypeVar

FooT = TypeVar('FooT', bound='Foo')
class Foo: ...

[file b.py]
from a import FooT
from typing import Callable

def f(x: FooT) -> bool: ...
reveal_type(f) # N: Revealed type is 'def [FooT <: a.Foo] (x: FooT`-1) -> builtins.bool'

class B: ...
[builtins fixtures/bool.pyi]