-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement support for "mypy: ignore" comments #17875
base: master
Are you sure you want to change the base?
Conversation
…nction changes to allow us to detect it otherwise.
dbe89a6
to
44242fb
Compare
This comment has been minimized.
This comment has been minimized.
@github-actions projected_memory_kv: Optional[pt.Tensor] = None) -> pt.Tensor: # mypy: ignore Incredible... looks like this PR already has a consumer :) The demand of the people for this feature is overwhelming. OK... more like "1" than "overwhelming"... but anyway.... Note to self: go PR to remove that line over there after this PR, if they don't notice it themselves... |
This comment has been minimized.
This comment has been minimized.
…ents note also that the 'not start of line' constraint had to be removed from the regex, because now each comment is encountered individually and thus they are at the start
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
This comment has been minimized.
This comment has been minimized.
Well... that's not good. |
This comment has been minimized.
This comment has been minimized.
We do a lil "finding out there are bugs in |
This comment has been minimized.
This comment has been minimized.
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
This comment has been minimized.
This comment has been minimized.
Diff from mypy_primer, showing the effect of this PR on open source code: spark (https://github.com/apache/spark)
+ python/pyspark/pandas/supported_api_gen.py:398: SyntaxWarning: invalid escape sequence '\_'
+ return func_str[:-1] + "\_" # noqa: W605
sockeye (https://github.com/awslabs/sockeye)
+ sockeye/layers.py:642: error: Unused "type: ignore" comment [unused-ignore]
steam.py (https://github.com/Gobot1234/steam.py): 12.27x faster (43.2s -> 3.5s in a single noisy sample)
+ steam/utils.py:143: error: f-string: expecting a valid expression after '{' [syntax]
- steam/types/trade.py:69: error: Overwriting TypedDict field "instanceid" while merging [misc]
- steam/types/trade.py:69: error: Overwriting TypedDict field "classid" while merging [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "assetid" while merging [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "amount" while merging [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "appid" while merging [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "contextid" while merging [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "instanceid" while merging [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "classid" while merging [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "missing" while merging [misc]
- steam/types/vdf.py:31: error: Incompatible return value type (implicitly returns "None", expected "Never") [empty-body]
- steam/types/vdf.py:33: error: Incompatible return value type (implicitly returns "None", expected "Never") [empty-body]
- steam/types/manifest.py:67: error: Duplicate TypedDict key "review_percentage" [misc]
- steam/types/manifest.py:68: error: Duplicate TypedDict key "review_score" [misc]
- steam/types/manifest.py:71: error: Duplicate TypedDict key "steam_release_date" [misc]
- steam/types/manifest.py:73: error: Duplicate TypedDict key "store_tags" [misc]
- steam/_const.py:29: error: Module "steam.clan" has no attribute "Clan" [attr-defined]
- steam/_const.py:30: error: Module "steam.group" has no attribute "Group" [attr-defined]
- steam/_const.py:37: error: Unused "type: ignore" comment [unused-ignore]
- steam/_const.py:57: error: Unused "type: ignore" comment [unused-ignore]
- steam/_const.py:69: error: Unused "type: ignore" comment [unused-ignore]
- steam/_const.py:73: error: Function is missing a return type annotation [no-untyped-def]
- steam/_const.py:73: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
- steam/_const.py:75: error: Unused "type: ignore" comment [unused-ignore]
- steam/_const.py:90: error: Unused "type: ignore" comment [unused-ignore]
- steam/_const.py:100: error: Unused "type: ignore" comment [unused-ignore]
- steam/_const.py:109: error: Unused "type: ignore" comment [unused-ignore]
- steam/_const.py:110: error: Unused "type: ignore" comment [unused-ignore]
- steam/_const.py:118: error: Unused "type: ignore" comment [unused-ignore]
- steam/_const.py:146: error: Invalid base class [misc]
- steam/_const.py:194: error: Cannot assign to a method [method-assign]
- steam/_const.py:194: error: Incompatible types in assignment (expression has type "Callable[[_IDComparable, object], bool]", variable has type "Callable[[object, object], bool]") [assignment]
- steam/_const.py:195: error: Cannot assign to a method [method-assign]
- steam/_const.py:195: error: Incompatible types in assignment (expression has type "Callable[[_IDComparable], int]", variable has type "Callable[[object], int]") [assignment]
- steam/types/user.py:16: error: Module "steam.user" has no attribute "User" [attr-defined]
- steam/types/user.py:139: error: Cannot resolve name "Author" (possible cyclic definition) [misc]
- steam/types/user.py:143: error: Parameter 1 of Literal[...] is invalid [valid-type]
- steam/invite.py:16: error: Module "steam.clan" has no attribute "Clan" [attr-defined]
- steam/invite.py:19: error: Module "steam.group" has no attribute "Group" [attr-defined]
- steam/invite.py:21: error: Module "steam.user" has no attribute "User" [attr-defined]
- steam/invite.py:90: error: Attributes without a default cannot follow attributes with one [misc]
- steam/invite.py:109: error: Attributes without a default cannot follow attributes with one [misc]
- steam/invite.py:118: error: Cannot resolve name "ChatGroupInvite" (possible cyclic definition) [misc]
- steam/invite.py:134: error: Call to abstract method "accept" of "_Invite" with trivial body via super() is unsafe [safe-super]
- steam/enums.py:98: error: Function is missing a return type annotation [no-untyped-def]
- steam/enums.py:98: note: Use "-> None" if function does not return a value
- steam/enums.py:106: error: Invalid base class [misc]
- steam/enums.py:108: error: Unused "type: ignore" comment [unused-ignore]
- steam/enums.py:111: error: Unused "type: ignore" comment [unused-ignore]
- steam/enums.py:124: error: Unused "type: ignore" comment [unused-ignore]
- steam/enums.py:181: error: Invalid base class [misc]
- steam/enums.py:198: error: Unused "type: ignore" comment [unused-ignore]
- steam/enums.py:247: error: Unused "type: ignore" comment [unused-ignore]
- steam/enums.py:635: error: Unused "type: ignore" comment [unused-ignore]
- steam/enums.py:638: error: Dict entry 0 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:639: error: Dict entry 1 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:640: error: Dict entry 2 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:641: error: Dict entry 3 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:642: error: Dict entry 4 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:643: error: Dict entry 5 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:644: error: Dict entry 6 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:645: error: Dict entry 7 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:646: error: Dict entry 8 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:647: error: Dict entry 9 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:648: error: Dict entry 10 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:649: error: Dict entry 11 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:650: error: Dict entry 12 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:651: error: Dict entry 13 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:652: error: Dict entry 14 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:653: error: Dict entry 15 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:654: error: Dict entry 16 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:655: error: Dict entry 17 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:656: error: Dict entry 18 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:657: error: Dict entry 19 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:658: error: Dict entry 20 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:659: error: Dict entry 21 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:660: error: Dict entry 22 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:661: error: Dict entry 23 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:662: error: Dict entry 24 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:663: error: Dict entry 25 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:664: error: Dict entry 26 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:665: error: Dict entry 27 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:666: error: Dict entry 28 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:670: error: Unused "type: ignore" comment [unused-ignore]
- steam/enums.py:673: error: Dict entry 0 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:674: error: Dict entry 1 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:675: error: Dict entry 2 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:676: error: Dict entry 3 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:677: error: Dict entry 4 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:678: error: Dict entry 5 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:679: error: Dict entry 6 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:680: error: Dict entry 7 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:681: error: Dict entry 8 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:682: error: Dict entry 9 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:683: error: Dict entry 10 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
- steam/enums.py:684: error: Dict entry 11 has incompatible type "int": "str"; expected "Language": "str" [dict-item]
... (truncated 1173 lines) ...
core (https://github.com/home-assistant/core): 6.32x faster (301.8s -> 47.8s in a single noisy sample)
+ Warning: unused section(s) in mypy.ini: [mypy-homeassistant.core], [mypy-homeassistant.exceptions], [mypy-homeassistant.helpers.area_registry], [mypy-homeassistant.helpers.condition], [mypy-homeassistant.helpers.debounce], [mypy-homeassistant.helpers.deprecation], [mypy-homeassistant.helpers.device_registry], [mypy-homeassistant.helpers.discovery], [mypy-homeassistant.helpers.dispatcher], [mypy-homeassistant.helpers.entity], [mypy-homeassistant.helpers.entity_platform], [mypy-homeassistant.helpers.entity_values], [mypy-homeassistant.helpers.event], [mypy-homeassistant.helpers.reload], [mypy-homeassistant.helpers.script], [mypy-homeassistant.helpers.script_variables], [mypy-homeassistant.helpers.singleton], [mypy-homeassistant.helpers.sun], [mypy-homeassistant.helpers.translation], [mypy-homeassistant.loader], [mypy-homeassistant.requirements], [mypy-homeassistant.runner], [mypy-homeassistant.setup], [mypy-homeassistant.util.async_], [mypy-homeassistant.util.color], [mypy-homeassistant.util.decorator], [mypy-homeassistant.util.location], [mypy-homeassistant.util.logging], [mypy-homeassistant.util.process], [mypy-homeassistant.util.unit_system], [mypy-homeassistant.components.yalexs_ble.*], [mypy-homeassistant.components.youtube.*], [mypy-homeassistant.components.zeroconf.*], [mypy-homeassistant.components.zodiac.*], [mypy-homeassistant.components.zone.*], [mypy-homeassistant.components.zwave_js.*], [mypy-tests.*]
+ homeassistant/components/yale_smart_alarm/lock.py:55: error: f-string: single '}' is not allowed [syntax]
- Warning: unused section(s) in mypy.ini: [mypy-tests.*]
- homeassistant/components/zeroconf/models.py:7: error: Class cannot subclass "Zeroconf" (has type "Any") [misc]
- homeassistant/components/zeroconf/models.py:16: error: Class cannot subclass "AsyncZeroconf" (has type "Any") [misc]
- homeassistant/components/owntracks/helper.py:6: error: Unused "type: ignore" comment [unused-ignore]
- homeassistant/util/ulid.py:44: error: Returning Any from function declared to return "str" [no-any-return]
- homeassistant/components/mqtt/async_client.py:40: error: Class cannot subclass "MQTTClient" (has type "Any") [misc]
- homeassistant/components/http/static.py:21: error: Class cannot subclass "StaticResource" (has type "Any") [misc]
- homeassistant/util/dt.py:116: error: Returning Any from function declared to return "tzinfo | None" [no-any-return]
- homeassistant/util/dt.py:227: error: Returning Any from function declared to return "datetime | None" [no-any-return]
- homeassistant/util/yaml/objects.py:32: error: Unused "type: ignore" comment [unused-ignore]
- homeassistant/util/package.py:82: error: Returning Any from function declared to return "bool" [no-any-return]
- homeassistant/components/http/web_runner.py:12: error: Class cannot subclass "BaseSite" (has type "Any") [misc]
- homeassistant/components/http/web_runner.py:62: error: Trying to assign name "_server" that is not in "__slots__" of type "homeassistant.components.http.web_runner.HomeAssistantTCPSite" [misc]
- homeassistant/components/recorder/table_managers/__init__.py:36: error: Returning Any from function declared to return "int | None" [no-any-return]
- homeassistant/helpers/json.py:175: error: Returning Any from function declared to return "str" [no-any-return]
- homeassistant/helpers/json.py:188: error: Returning Any from function declared to return "bytes" [no-any-return]
- homeassistant/auth/jwt_wrapper.py:29: error: Class cannot subclass "PyJWS" (has type "Any") [misc]
- homeassistant/auth/jwt_wrapper.py:38: error: Returning Any from function declared to return "tuple[bytes, bytes, dict[Any, Any], bytes]" [no-any-return]
- homeassistant/auth/jwt_wrapper.py:56: error: Class cannot subclass "PyJWT" (has type "Any") [misc]
- homeassistant/core.py:358: error: Untyped decorator makes function "job_type" untyped [misc]
- homeassistant/core.py:479: error: Untyped decorator makes function "is_running" untyped [misc]
- homeassistant/core.py:484: error: Untyped decorator makes function "is_stopping" untyped [misc]
- homeassistant/core.py:1277: error: Untyped decorator makes function "_as_dict" untyped [misc]
- homeassistant/core.py:1292: error: Returning Any from function declared to return "ReadOnlyDict[str, str | None]" [no-any-return]
- homeassistant/core.py:1294: error: Untyped decorator makes function "_as_read_only_dict" untyped [misc]
- homeassistant/core.py:1299: error: Untyped decorator makes function "json_fragment" untyped [misc]
- homeassistant/core.py:1300: error: Variable "homeassistant.helpers.json.json_fragment" is not valid as a type [valid-type]
- homeassistant/core.py:1300: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
- homeassistant/core.py:1302: error: Returning Any from function declared to return json_fragment? [no-any-return]
- homeassistant/core.py:1315: error: Untyped decorator makes function "idx" untyped [misc]
- homeassistant/core.py:1353: error: Untyped decorator makes function "time_fired" untyped [misc]
- homeassistant/core.py:1358: error: Untyped decorator makes function "_as_dict" untyped [misc]
- homeassistant/core.py:1381: error: Returning Any from function declared to return "ReadOnlyDict[str, Any]" [no-any-return]
- homeassistant/core.py:1383: error: Untyped decorator makes function "_as_read_only_dict" untyped [misc]
- homeassistant/core.py:1399: error: Untyped decorator makes function "json_fragment" untyped [misc]
- homeassistant/core.py:1400: error: Variable "homeassistant.helpers.json.json_fragment" is not valid as a type [valid-type]
- homeassistant/core.py:1400: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
- homeassistant/core.py:1402: error: Returning Any from function declared to return json_fragment? [no-any-return]
- homeassistant/core.py:1840: error: Untyped decorator makes function "name" untyped [misc]
- homeassistant/core.py:1847: error: Untyped decorator makes function "last_changed_timestamp" untyped [misc]
- homeassistant/core.py:1852: error: Untyped decorator makes function "last_reported_timestamp" untyped [misc]
- homeassistant/core.py:1857: error: Untyped decorator makes function "_as_dict" untyped [misc]
- homeassistant/core.py:1896: error: Returning Any from function declared to return "ReadOnlyDict[str, datetime | Collection[Any]]" [no-any-return]
- homeassistant/core.py:1898: error: Untyped decorator makes function "_as_read_only_dict" untyped [misc]
- homeassistant/core.py:1913: error: Untyped decorator makes function "as_dict_json" untyped [misc]
- homeassistant/core.py:1918: error: Untyped decorator makes function "json_fragment" untyped [misc]
- homeassistant/core.py:1919: error: Variable "homeassistant.helpers.json.json_fragment" is not valid as a type [valid-type]
- homeassistant/core.py:1919: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
- homeassistant/core.py:1921: error: Returning Any from function declared to return json_fragment? [no-any-return]
- homeassistant/core.py:1923: error: Untyped decorator makes function "as_compressed_state" untyped [misc]
- homeassistant/core.py:1951: error: Untyped decorator makes function "as_compressed_state_json" untyped [misc]
- homeassistant/loader.py:781: error: Untyped decorator makes function "manifest_json_fragment" untyped [misc]
- homeassistant/loader.py:782: error: Variable "homeassistant.helpers.json.json_fragment" is not valid as a type [valid-type]
- homeassistant/loader.py:782: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
- homeassistant/loader.py:784: error: Returning Any from function declared to return json_fragment? [no-any-return]
- homeassistant/loader.py:786: error: Untyped decorator makes function "name" untyped [misc]
- homeassistant/loader.py:791: error: Untyped decorator makes function "disabled" untyped [misc]
- homeassistant/loader.py:796: error: Untyped decorator makes function "domain" untyped [misc]
- homeassistant/loader.py:801: error: Untyped decorator makes function "dependencies" untyped [misc]
- homeassistant/loader.py:806: error: Untyped decorator makes function "after_dependencies" untyped [misc]
- homeassistant/loader.py:811: error: Untyped decorator makes function "requirements" untyped [misc]
- homeassistant/loader.py:816: error: Untyped decorator makes function "config_flow" untyped [misc]
- homeassistant/loader.py:821: error: Untyped decorator makes function "documentation" untyped [misc]
- homeassistant/loader.py:826: error: Untyped decorator makes function "issue_tracker" untyped [misc]
- homeassistant/loader.py:831: error: Untyped decorator makes function "loggers" untyped [misc]
- homeassistant/loader.py:836: error: Untyped decorator makes function "quality_scale" untyped [misc]
- homeassistant/loader.py:841: error: Untyped decorator makes function "iot_class" untyped [misc]
- homeassistant/loader.py:846: error: Untyped decorator makes function "integration_type" untyped [misc]
- homeassistant/loader.py:855: error: Untyped decorator makes function "import_executor" untyped [misc]
- homeassistant/loader.py:862: error: Untyped decorator makes function "has_translations" untyped [misc]
- homeassistant/loader.py:867: error: Untyped decorator makes function "has_services" untyped [misc]
- homeassistant/loader.py:919: error: Untyped decorator makes function "single_config_entry" untyped [misc]
- homeassistant/loader.py:1709: error: Returning Any from function declared to return "str | None" [no-any-return]
- homeassistant/helpers/start.py:69: error: Returning Any from function declared to return "bool" [no-any-return]
- homeassistant/components/recorder/models/state.py:61: error: Unused "type: ignore" comment [unused-ignore]
... (truncated 1342 lines) ...
|
We do a lil "finding out there are bugs in 3.8 |
Fixes #12358
[needs tests. also, I think I can fix it to make it almost "the right way"]
Appendix A: the original text of this pr, before I radically improved it.
This change implements, #12358, `# mypy: ignore` comments that are analogous to `# type: ignore` comments, but specific only to mypy (or, one supposes, any other program that decides to honor them. While I was investigating this issue, it occurred to me that the only thing we need to do is make `# mypy: ignore` comments _appear_ to mypy as `# type: ignore` comments. And so, this PR implements that in what I think everyone will agree is "the wrong way", by **simply doing a re.sub on source code** before it is passed to the ast-parser. Because mypy is a typechecker, and therefore operates on the types of python programs instead of their values, it should almost never make a difference that we are making this change in the code in a textual way.Let us briefly review why this might be a good idea:
ast.parse
to get the syntax tree of the python program it is analyzing.ast.parse
is just a helper function wrapper over the python built-incompile
. This means we unfortunately cannot(?) monkey-patch the desired behavior into our parser, which was my first instinct. It also means we can't just copy and modify the parser to suit our needs (or, we would have to use C FFI, or something, to do this, which would be inconvenient), which was my second instinct. So, if mypy wants to continue using ast.parse as its ast-parser, the "right way" to do this is for ast.parse to also tell us about other comments or directives, such as proposed in Add comments parsing flag to ast.parse cpython#101494. I think this is a great idea and should be done. However, even it if was done immediately, it would only get into CPython for 3.13, which I think means we would need to wait until 3.13 was the minimum supported version of mypy for us to use this strategy. Which might be in the year 2028? There must be a better way!# mypy: ignore
comments ourselves, in another pass orthogonal to the ast-parsing. For instance,util.get_mypy_comments
does something like this (although that problem is easier because you can count on those comments being at the start of the line). Possibly using Python's tokenize. This is a more-realistic "right way" option.# mypy: ignore
from our original strategy is really in a comment, and then enact a compromise of the two strategies.Some of those alternatives are not ridiculous. But I already have this feature done this way. Actually now that I'm typing this I'm thinking, you know, I should just also do the tokenize thing. Oh, wait, ok, that's actually hard again because the only thing separating a #mypy: ignore from #mypy: ignore_errors is its position at the start of the line. Oh, wait, actually, you can use a (?![-_]), good.
As it stands, this pr
Breaks:
Literals of strings that would be matched by the regex
.*#\s*mypy:\s*ignore.*
For instance, now this does not raise a type error, as it should:
it's quite possible none of these have ever been used in the history of python.
(Incidentally, about that regex: I have discovered that there is no arbitrary whitespace allowed between the
type
and the:
in type-ignore comments. So, neither is there in mypy-ignore comments.)Does not break:
--pretty output. That's handled by another system, so the source code is not changed in that output. Which will make the problem with Literals more of a problem if it ever comes up, come to think of it.
Other downside: presumably a slight penalty hit, since we're doing another scan over each file. This is not a huge concern, I think, given that, eg, I discovered that util.get_mypy_comments does multiple passes even though it isn't strictly necessary.