Author: Michael R. Crusoe <michael.crusoe@gmail.com>
Description: Add file from upstream missing from their sdist
--- /dev/null
+++ mypy/misc/proper_plugin.py
@@ -0,0 +1,131 @@
+from mypy.plugin import Plugin, FunctionContext
+from mypy.types import (
+    Type, Instance, CallableType, UnionType, get_proper_type, ProperType,
+    get_proper_types, TupleType, NoneTyp, AnyType
+)
+from mypy.nodes import TypeInfo
+from mypy.subtypes import is_proper_subtype
+
+from typing_extensions import Type as typing_Type
+from typing import Optional, Callable
+
+
+class ProperTypePlugin(Plugin):
+    """
+    A plugin to ensure that every type is expanded before doing any special-casing.
+
+    This solves the problem that we have hundreds of call sites like:
+
+        if isinstance(typ, UnionType):
+            ...  # special-case union
+
+    But after introducing a new type TypeAliasType (and removing immediate expansion)
+    all these became dangerous because typ may be e.g. an alias to union.
+    """
+    def get_function_hook(self, fullname: str
+                          ) -> Optional[Callable[[FunctionContext], Type]]:
+        if fullname == 'builtins.isinstance':
+            return isinstance_proper_hook
+        if fullname == 'mypy.types.get_proper_type':
+            return proper_type_hook
+        if fullname == 'mypy.types.get_proper_types':
+            return proper_types_hook
+        return None
+
+
+def isinstance_proper_hook(ctx: FunctionContext) -> Type:
+    right = get_proper_type(ctx.arg_types[1][0])
+    for arg in ctx.arg_types[0]:
+        if (is_improper_type(arg) or
+                isinstance(get_proper_type(arg), AnyType) and is_dangerous_target(right)):
+            if is_special_target(right):
+                return ctx.default_return_type
+            ctx.api.fail('Never apply isinstance() to unexpanded types;'
+                         ' use mypy.types.get_proper_type() first', ctx.context)
+    return ctx.default_return_type
+
+
+def is_special_target(right: ProperType) -> bool:
+    """Whitelist some special cases for use in isinstance() with improper types."""
+    if isinstance(right, CallableType) and right.is_type_obj():
+        if right.type_object().fullname() == 'builtins.tuple':
+            # Used with Union[Type, Tuple[Type, ...]].
+            return True
+        if right.type_object().fullname() in ('mypy.types.Type',
+                                              'mypy.types.ProperType',
+                                              'mypy.types.TypeAliasType'):
+            # Special case: things like assert isinstance(typ, ProperType) are always OK.
+            return True
+        if right.type_object().fullname() in ('mypy.types.UnboundType',
+                                              'mypy.types.TypeVarType',
+                                              'mypy.types.RawExpressionType',
+                                              'mypy.types.EllipsisType',
+                                              'mypy.types.StarType',
+                                              'mypy.types.TypeList',
+                                              'mypy.types.CallableArgument',
+                                              'mypy.types.PartialType',
+                                              'mypy.types.ErasedType'):
+            # Special case: these are not valid targets for a type alias and thus safe.
+            # TODO: introduce a SyntheticType base to simplify this?
+            return True
+    elif isinstance(right, TupleType):
+        return all(is_special_target(t) for t in get_proper_types(right.items))
+    return False
+
+
+def is_improper_type(typ: Type) -> bool:
+    """Is this a type that is not a subtype of ProperType?"""
+    typ = get_proper_type(typ)
+    if isinstance(typ, Instance):
+        info = typ.type
+        return info.has_base('mypy.types.Type') and not info.has_base('mypy.types.ProperType')
+    if isinstance(typ, UnionType):
+        return any(is_improper_type(t) for t in typ.items)
+    return False
+
+
+def is_dangerous_target(typ: ProperType) -> bool:
+    """Is this a dangerous target (right argument) for an isinstance() check?"""
+    if isinstance(typ, TupleType):
+        return any(is_dangerous_target(get_proper_type(t)) for t in typ.items)
+    if isinstance(typ, CallableType) and typ.is_type_obj():
+        return typ.type_object().has_base('mypy.types.Type')
+    return False
+
+
+def proper_type_hook(ctx: FunctionContext) -> Type:
+    """Check if this get_proper_type() call is not redundant."""
+    arg_types = ctx.arg_types[0]
+    if arg_types:
+        arg_type = get_proper_type(arg_types[0])
+        proper_type = get_proper_type_instance(ctx)
+        if is_proper_subtype(arg_type, UnionType.make_union([NoneTyp(), proper_type])):
+            # Minimize amount of spurious errors from overload machinery.
+            # TODO: call the hook on the overload as a whole?
+            if isinstance(arg_type, (UnionType, Instance)):
+                ctx.api.fail('Redundant call to get_proper_type()', ctx.context)
+    return ctx.default_return_type
+
+
+def proper_types_hook(ctx: FunctionContext) -> Type:
+    """Check if this get_proper_types() call is not redundant."""
+    arg_types = ctx.arg_types[0]
+    if arg_types:
+        arg_type = arg_types[0]
+        proper_type = get_proper_type_instance(ctx)
+        item_type = UnionType.make_union([NoneTyp(), proper_type])
+        ok_type = ctx.api.named_generic_type('typing.Iterable', [item_type])
+        if is_proper_subtype(arg_type, ok_type):
+            ctx.api.fail('Redundant call to get_proper_types()', ctx.context)
+    return ctx.default_return_type
+
+
+def get_proper_type_instance(ctx: FunctionContext) -> Instance:
+    types = ctx.api.modules['mypy.types']  # type: ignore
+    proper_type_info = types.names['ProperType']
+    assert isinstance(proper_type_info.node, TypeInfo)
+    return Instance(proper_type_info.node, [])
+
+
+def plugin(version: str) -> typing_Type[ProperTypePlugin]:
+    return ProperTypePlugin
