Repository: python/mypy

Issue:
(🎁) Report better errors with syntax errors from the future
[look at this playground](https://mypy-play.net/?mypy=latest&python=3.10&flags=verbose&gist=3f5dec26c6d92dd57c94041454c63c37)

even though "Python 3.10" (`--python-version 3.10`) is specified it still fails with a syntax error. This is not exactly obvious from the error:
```
main.py:2: error: invalid syntax
Found 1 error in 1 file (errors prevented further checking)
```

Perhaps something a little more telling could be said?
```
main.py:2: error: invalid syntax
main.py:2: note: perhaps this syntax comes from the future(🔮), you are only on ancient(🦕) python 3.9 after all
Found 1 error in 1 file (errors prevented further checking)
```

If should probably only get mentioned if you specify a `--python-version` higher than the interpreter.


Discussion:
We would need the ability to parse future python in order to make this error message reliable, at which point there is really no benefit to this feature (as the only reason its a problem is that we can't parse future python).

A shorter note that includes the version but isn't opinionated on the "perhaps" bit might be better, as I'd hate to give a misleading error message maybe 95% of the time (given that most of my syntax errors are not because I'm using future syntax, but because I forgot a colon or something).

Another (breaking) option is to make using newer `python_version` than current version cause mypy to immediately error unless you pass say `--unsafe-allow-future-python-version`.
That is true, but as mentioned in the op, if this message is only shown when invoked with a future python-version, it would be much more reliable.

Resolving patch:
diff --git a/mypy/fastparse.py b/mypy/fastparse.py
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -272,12 +272,12 @@ def parse(
         options = Options()
     errors.set_file(fnam, module)
     is_stub_file = fnam.endswith(".pyi")
+    if is_stub_file:
+        feature_version = defaults.PYTHON3_VERSION[1]
+    else:
+        assert options.python_version[0] >= 3
+        feature_version = options.python_version[1]
     try:
-        if is_stub_file:
-            feature_version = defaults.PYTHON3_VERSION[1]
-        else:
-            assert options.python_version[0] >= 3
-            feature_version = options.python_version[1]
         # Disable deprecation warnings about \u
         with warnings.catch_warnings():
             warnings.filterwarnings("ignore", category=DeprecationWarning)
@@ -294,10 +294,14 @@ def parse(
             # start of the f-string. This would be misleading, as mypy will report the error as the
             # lineno within the file.
             e.lineno = None
+        message = e.msg
+        if feature_version > sys.version_info.minor and message.startswith("invalid syntax"):
+            python_version_str = f"{options.python_version[0]}.{options.python_version[1]}"
+            message += f"; you likely need to run mypy using Python {python_version_str} or newer"
         errors.report(
             e.lineno if e.lineno is not None else -1,
             e.offset,
-            e.msg,
+            message,
             blocker=True,
             code=codes.SYNTAX,
         )


Test patch:
diff --git a/test-data/unit/check-newsyntax.test b/test-data/unit/check-newsyntax.test
--- a/test-data/unit/check-newsyntax.test
+++ b/test-data/unit/check-newsyntax.test
@@ -151,3 +151,9 @@ v = 1
 reveal_type(f'{v}') # N: Revealed type is "builtins.str"
 reveal_type(f'{1}') # N: Revealed type is "builtins.str"
 [builtins fixtures/f_string.pyi]
+
+[case testFeatureVersionSuggestion]
+# flags: --python-version 3.99
+this is what future python looks like public static void main String[] args await goto exit
+[out]
+main:2: error: invalid syntax; you likely need to run mypy using Python 3.99 or newer