taion: Hold on, this breaks on http://mypy.readthedocs.io/en/latest/kinds_of_types.html#class-name-forward-references.
taion: This is ready to go now.
taion: Sorry for the repeated updates. This is now fixed.
myint: Thanks!

I was testing this against the CPython standard library and it seemed to crash when run against one of the test files ([`test_functools.py`](https://github.com/python/cpython/blob/bdb8315c21825487b54852ff0511fb4881ea2181/Lib/test/test_functools.py)). You can reproduce it by doing the above or more specifically:

```
$ wget https://raw.githubusercontent.com/python/cpython/bdb8315c21825487b54852ff0511fb4881ea2181/Lib/test/test_functools.py

$ ./py/bin/pyflakes test_functools.py
Traceback (most recent call last):
  File "./py/bin/pyflakes", line 11, in <module>
    load_entry_point('pyflakes==1.6.0', 'console_scripts', 'pyflakes')()
  File "/Users/myint/tmp/pyflakes/py/lib/python3.6/site-packages/pyflakes/api.py", line 208, in main
    warnings = checkRecursive(args, reporter)
  File "/Users/myint/tmp/pyflakes/py/lib/python3.6/site-packages/pyflakes/api.py", line 165, in checkRecursive
    warnings += checkPath(sourcePath, reporter)
  File "/Users/myint/tmp/pyflakes/py/lib/python3.6/site-packages/pyflakes/api.py", line 112, in checkPath
    return check(codestr, filename, reporter)
  File "/Users/myint/tmp/pyflakes/py/lib/python3.6/site-packages/pyflakes/api.py", line 73, in check
    w = checker.Checker(tree, filename)
  File "/Users/myint/tmp/pyflakes/py/lib/python3.6/site-packages/pyflakes/checker.py", line 498, in __init__
    self.runDeferred(self._deferredFunctions)
  File "/Users/myint/tmp/pyflakes/py/lib/python3.6/site-packages/pyflakes/checker.py", line 535, in runDeferred
    handler()
  File "/Users/myint/tmp/pyflakes/py/lib/python3.6/site-packages/pyflakes/checker.py", line 929, in handleForwardAnnotation
    parsed = ast.parse(annotation.s).body[0].value
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ast.py", line 35, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "<unknown>", line 1
    This is a new annotation
                ^
SyntaxError: invalid syntax
```
taion: Ah, my bad. Didn't handle annotations that weren't actually annotations. I've addressed it now and added test cases.

I've also added a new reported error here since there weren't any existing errors that covered this, and I don't think there are any reasons to want illegal annotations in normal code.

If you think this new error is reasonable, could you please advise on whether the naming is appropriate, given that this will be user-facing? The alternative would be to refer to this as a "string literal annotation" rather than a "forward annotation".
taion: Rebased and squashed.

Can you confirm that you're okay with the naming and the semantics around the new error? Something like `a: 'A B'` or `a: 'A; B'` is legal Python, but I think it's vanishing unlikely to actually be intentionally used.
diff --git a/pyflakes/checker.py b/pyflakes/checker.py
index d8f093a..a070822 100644
--- a/pyflakes/checker.py
+++ b/pyflakes/checker.py
@@ -922,6 +922,39 @@ class Checker(object):
         self.popScope()
         self.scopeStack = saved_stack
 
+    def handleAnnotation(self, annotation, node):
+        if isinstance(annotation, ast.Str):
+            # Defer handling forward annotation.
+            def handleForwardAnnotation():
+                try:
+                    tree = ast.parse(annotation.s)
+                except SyntaxError:
+                    self.report(
+                        messages.ForwardAnnotationSyntaxError,
+                        node,
+                        annotation.s,
+                    )
+                    return
+
+                body = tree.body
+                if len(body) != 1 or not isinstance(body[0], ast.Expr):
+                    self.report(
+                        messages.ForwardAnnotationSyntaxError,
+                        node,
+                        annotation.s,
+                    )
+                    return
+
+                parsed_annotation = tree.body[0].value
+                for descendant in ast.walk(parsed_annotation):
+                    ast.copy_location(descendant, annotation)
+
+                self.handleNode(parsed_annotation, node)
+
+            self.deferFunction(handleForwardAnnotation)
+        else:
+            self.handleNode(annotation, node)
+
     def ignore(self, node):
         pass
 
@@ -1160,9 +1193,11 @@ class Checker(object):
                 if arg in args[:idx]:
                     self.report(messages.DuplicateArgument, node, arg)
 
-        for child in annotations + defaults:
-            if child:
-                self.handleNode(child, node)
+        for annotation in annotations:
+            self.handleAnnotation(annotation, node)
+
+        for default in defaults:
+            self.handleNode(default, node)
 
         def runFunction():
 
@@ -1375,7 +1410,7 @@ class Checker(object):
             # Otherwise it's not really ast.Store and shouldn't silence
             # UndefinedLocal warnings.
             self.handleNode(node.target, node)
-        self.handleNode(node.annotation, node)
+        self.handleAnnotation(node.annotation, node)
         if node.value:
             # If the assignment has value, handle the *value* now.
             self.handleNode(node.value, node)
diff --git a/pyflakes/messages.py b/pyflakes/messages.py
index 9e9406c..670f95f 100644
--- a/pyflakes/messages.py
+++ b/pyflakes/messages.py
@@ -231,3 +231,11 @@ class AssertTuple(Message):
     Assertion test is a tuple, which are always True.
     """
     message = 'assertion is always true, perhaps remove parentheses?'
+
+
+class ForwardAnnotationSyntaxError(Message):
+    message = 'syntax error in forward annotation %r'
+
+    def __init__(self, filename, loc, annotation):
+        Message.__init__(self, filename, loc)
+        self.message_args = (annotation,)

Typing.TYPE_CHECKING imports raise F401
```
$ pip install  flake8

```

```
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from my_module.test_class import MyClass

object: 'MyClass' = None
```
Flake 8 gives the following error in this scenario:

`my_module\test_class.py:4:5: F401 'my_module.test_class.MyClass' imported but unused`

My current work around is this:

```
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from my_module.test_class import MyClass  # noqa

object: 'MyClass' = None
```

Any better solutions? Couldwe somehow automatically ignore all F401 errors within the `if TYPE_CHECKING:` block?
diff --git a/pyflakes/test/test_other.py b/pyflakes/test/test_other.py
index ba052f1..14f213c 100644
--- a/pyflakes/test/test_other.py
+++ b/pyflakes/test/test_other.py
@@ -1890,3 +1890,77 @@ class TestAsyncStatements(TestCase):
             class C:
                 foo: not_a_real_type = None
         ''', m.UndefinedName)
+        self.flakes('''
+        from foo import Bar
+        bar: Bar
+        ''')
+        self.flakes('''
+        from foo import Bar
+        bar: 'Bar'
+        ''')
+        self.flakes('''
+        import foo
+        bar: foo.Bar
+        ''')
+        self.flakes('''
+        import foo
+        bar: 'foo.Bar'
+        ''')
+        self.flakes('''
+        from foo import Bar
+        def f(bar: Bar): pass
+        ''')
+        self.flakes('''
+        from foo import Bar
+        def f(bar: 'Bar'): pass
+        ''')
+        self.flakes('''
+        from foo import Bar
+        def f(bar) -> Bar: return bar
+        ''')
+        self.flakes('''
+        from foo import Bar
+        def f(bar) -> 'Bar': return bar
+        ''')
+        self.flakes('''
+        bar: 'Bar'
+        ''', m.UndefinedName)
+        self.flakes('''
+        bar: 'foo.Bar'
+        ''', m.UndefinedName)
+        self.flakes('''
+        from foo import Bar
+        bar: str
+        ''', m.UnusedImport)
+        self.flakes('''
+        from foo import Bar
+        def f(bar: str): pass
+        ''', m.UnusedImport)
+        self.flakes('''
+        def f(a: A) -> A: pass
+        class A: pass
+        ''', m.UndefinedName, m.UndefinedName)
+        self.flakes('''
+        def f(a: 'A') -> 'A': return a
+        class A: pass
+        ''')
+        self.flakes('''
+        a: A
+        class A: pass
+        ''', m.UndefinedName)
+        self.flakes('''
+        a: 'A'
+        class A: pass
+        ''')
+        self.flakes('''
+        a: 'A B'
+        ''', m.ForwardAnnotationSyntaxError)
+        self.flakes('''
+        a: 'A; B'
+        ''', m.ForwardAnnotationSyntaxError)
+        self.flakes('''
+        a: '1 + 2'
+        ''')
+        self.flakes('''
+        a: 'a: "A"'
+        ''', m.ForwardAnnotationSyntaxError)

pip install -e .
parse_log_pytest
pytest
flake8 pytest
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
certifi==2021.5.30
flake8==5.0.4
importlib-metadata==4.2.0
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
mccabe==0.7.0
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pycodestyle==2.9.1
-e git+https://github.com/PyCQA/pyflakes.git@8d0f995dafda7105a6966057fd11383f1ea6fb71#egg=pyflakes
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: pyflakes
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - flake8==5.0.4
      - importlib-metadata==4.2.0
      - mccabe==0.7.0
      - pycodestyle==2.9.1
prefix: /opt/conda/envs/pyflakes

pyflakes/test/test_other.py::TestAsyncStatements::test_variable_annotations
pyflakes/test/test_other.py::Test::test_attrAugmentedAssignment pyflakes/test/test_other.py::Test::test_breakInsideLoop pyflakes/test/test_other.py::Test::test_breakOutsideLoop pyflakes/test/test_other.py::Test::test_classFunctionDecorator pyflakes/test/test_other.py::Test::test_classNameDefinedPreviously pyflakes/test/test_other.py::Test::test_classNameUndefinedInClassBody pyflakes/test/test_other.py::Test::test_classRedefinedAsFunction pyflakes/test/test_other.py::Test::test_classRedefinition pyflakes/test/test_other.py::Test::test_classWithReturn pyflakes/test/test_other.py::Test::test_classWithYield pyflakes/test/test_other.py::Test::test_classWithYieldFrom pyflakes/test/test_other.py::Test::test_comparison pyflakes/test/test_other.py::Test::test_containment pyflakes/test/test_other.py::Test::test_continueInFinally pyflakes/test/test_other.py::Test::test_continueInsideLoop pyflakes/test/test_other.py::Test::test_continueOutsideLoop pyflakes/test/test_other.py::Test::test_defaultExceptLast pyflakes/test/test_other.py::Test::test_defaultExceptNotLast pyflakes/test/test_other.py::Test::test_doubleAssignmentConditionally pyflakes/test/test_other.py::Test::test_doubleAssignmentWithUse pyflakes/test/test_other.py::Test::test_duplicateArgs pyflakes/test/test_other.py::Test::test_ellipsis pyflakes/test/test_other.py::Test::test_extendedSlice pyflakes/test/test_other.py::Test::test_functionDecorator pyflakes/test/test_other.py::Test::test_functionRedefinedAsClass pyflakes/test/test_other.py::Test::test_globalDeclaredInDifferentScope pyflakes/test/test_other.py::Test::test_identity pyflakes/test/test_other.py::Test::test_localReferencedBeforeAssignment pyflakes/test/test_other.py::Test::test_loopControl pyflakes/test/test_other.py::Test::test_modernProperty pyflakes/test/test_other.py::Test::test_moduleWithReturn pyflakes/test/test_other.py::Test::test_moduleWithYield pyflakes/test/test_other.py::Test::test_moduleWithYieldFrom pyflakes/test/test_other.py::Test::test_redefinedClassFunction pyflakes/test/test_other.py::Test::test_redefinedFunction pyflakes/test/test_other.py::Test::test_redefinedIfElseFunction pyflakes/test/test_other.py::Test::test_redefinedIfElseInListComp pyflakes/test/test_other.py::Test::test_redefinedIfFunction pyflakes/test/test_other.py::Test::test_redefinedInDictComprehension pyflakes/test/test_other.py::Test::test_redefinedInGenerator pyflakes/test/test_other.py::Test::test_redefinedInSetComprehension pyflakes/test/test_other.py::Test::test_redefinedTryExceptFunction pyflakes/test/test_other.py::Test::test_redefinedTryFunction pyflakes/test/test_other.py::Test::test_starredAssignmentErrors pyflakes/test/test_other.py::Test::test_starredAssignmentNoError pyflakes/test/test_other.py::Test::test_unaryPlus pyflakes/test/test_other.py::Test::test_undefinedBaseClass pyflakes/test/test_other.py::Test::test_varAugmentedAssignment pyflakes/test/test_other.py::TestUnusedAssignment::test_assert_static pyflakes/test/test_other.py::TestUnusedAssignment::test_assert_tuple pyflakes/test/test_other.py::TestUnusedAssignment::test_assert_tuple_empty pyflakes/test/test_other.py::TestUnusedAssignment::test_assert_with_message pyflakes/test/test_other.py::TestUnusedAssignment::test_assert_without_message pyflakes/test/test_other.py::TestUnusedAssignment::test_assignInForLoop pyflakes/test/test_other.py::TestUnusedAssignment::test_assignInListComprehension pyflakes/test/test_other.py::TestUnusedAssignment::test_assignToGlobal pyflakes/test/test_other.py::TestUnusedAssignment::test_assignToMember pyflakes/test/test_other.py::TestUnusedAssignment::test_assignToNonlocal pyflakes/test/test_other.py::TestUnusedAssignment::test_assignmentInsideLoop pyflakes/test/test_other.py::TestUnusedAssignment::test_augmentedAssignmentImportedFunctionCall pyflakes/test/test_other.py::TestUnusedAssignment::test_closedOver pyflakes/test/test_other.py::TestUnusedAssignment::test_dictComprehension pyflakes/test/test_other.py::TestUnusedAssignment::test_doubleClosedOver pyflakes/test/test_other.py::TestUnusedAssignment::test_exceptWithoutNameInFunction pyflakes/test/test_other.py::TestUnusedAssignment::test_exceptWithoutNameInFunctionTuple pyflakes/test/test_other.py::TestUnusedAssignment::test_exceptionUnusedInExcept pyflakes/test/test_other.py::TestUnusedAssignment::test_exceptionUnusedInExceptInFunction pyflakes/test/test_other.py::TestUnusedAssignment::test_exceptionUsedInExcept pyflakes/test/test_other.py::TestUnusedAssignment::test_f_string pyflakes/test/test_other.py::TestUnusedAssignment::test_generatorExpression pyflakes/test/test_other.py::TestUnusedAssignment::test_ifexp pyflakes/test/test_other.py::TestUnusedAssignment::test_listUnpacking pyflakes/test/test_other.py::TestUnusedAssignment::test_setComprehensionAndLiteral pyflakes/test/test_other.py::TestUnusedAssignment::test_tracebackhideSpecialVariable pyflakes/test/test_other.py::TestUnusedAssignment::test_tupleUnpacking pyflakes/test/test_other.py::TestUnusedAssignment::test_unusedVariable pyflakes/test/test_other.py::TestUnusedAssignment::test_unusedVariableAsLocals pyflakes/test/test_other.py::TestUnusedAssignment::test_unusedVariableNoLocals pyflakes/test/test_other.py::TestUnusedAssignment::test_variableUsedInLoop pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementAttributeName pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementComplicatedTarget pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementListNames pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementNameDefinedInBody pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementNoNames pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementSingleName pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementSingleNameRedefined pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementSingleNameUndefined pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementSubscript pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementSubscriptUndefined pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementTupleNames pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementTupleNamesRedefined pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementTupleNamesUndefined pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementUndefinedInExpression pyflakes/test/test_other.py::TestUnusedAssignment::test_withStatementUndefinedInside pyflakes/test/test_other.py::TestUnusedAssignment::test_yieldFromUndefined pyflakes/test/test_other.py::TestAsyncStatements::test_asyncDef pyflakes/test/test_other.py::TestAsyncStatements::test_asyncDefAwait pyflakes/test/test_other.py::TestAsyncStatements::test_asyncDefUndefined pyflakes/test/test_other.py::TestAsyncStatements::test_asyncFor pyflakes/test/test_other.py::TestAsyncStatements::test_asyncWith pyflakes/test/test_other.py::TestAsyncStatements::test_asyncWithItem pyflakes/test/test_other.py::TestAsyncStatements::test_continueInAsyncForFinally pyflakes/test/test_other.py::TestAsyncStatements::test_formatstring pyflakes/test/test_other.py::TestAsyncStatements::test_loopControlInAsyncFor pyflakes/test/test_other.py::TestAsyncStatements::test_loopControlInAsyncForElse pyflakes/test/test_other.py::TestAsyncStatements::test_matmul
pyflakes/messages.py pyflakes/checker.py
pyflakes/messages.py pyflakes/checker.py