Skip to content

Add --warn-implicit-any #3141

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

Closed
wants to merge 3 commits into from
Closed

Add --warn-implicit-any #3141

wants to merge 3 commits into from

Conversation

pkch
Copy link
Contributor

@pkch pkch commented Apr 7, 2017

Experimental: attempt to address parts of #2901

@gvanrossum gvanrossum changed the title Add --warn-implicit-any [WIP] Add --warn-implicit-any Apr 13, 2017
@gvanrossum
Copy link
Member

Since you say "experimental" I'm not reviewing this yet and I've added "[WIP]" to the subject. (I think GMail may even thread notifications correctly because of the brackets.)

@pkch pkch changed the title [WIP] Add --warn-implicit-any Add --warn-implicit-any Apr 27, 2017
@pkch
Copy link
Contributor Author

pkch commented Apr 27, 2017

It's far from perfect, but since it caught quite a few unintentional Any expansions in my code, maybe it's worth considering.

@gvanrossum
Copy link
Member

I propose that @ddfisher should review this.

Copy link
Collaborator

@ilinum ilinum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I provided some cosmetic feedback but I unfortunately, I can't give any deep review since my knowledge of mypy is limited. Perhaps, @ddfisher can help.

Overall, I think this feature is great! We should try to finish it up and merge it.
Maybe we can name the flag --warn-implicit-any-generic or --disallow-implicit-any-generic.

mypy/semanal.py Outdated
@@ -3251,12 +3251,15 @@ def name_already_defined(self, name: str, ctx: Context) -> None:
self.fail("Name '{}' already defined".format(name), ctx)

def fail(self, msg: str, ctx: Context, serious: bool = False, *,
blocker: bool = False) -> None:
blocker: bool = False, implicit_any: bool = False) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like adding an extra parameter to fail function might not be the best way to do it.
I would try to keep this function generic for any kind of failures.

Perhaps, you can check if the error is in the caller.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Unfortunately, I need to suppress the error message based on the information (options, and whether the module being checked is a stub) that's unavailable inside TypeAnalyser methods where the error occurs. I am not sure what the best fix for this would be. If I don't add an extra parameter to fail, I'll have to pass all this information into TypeAnalyser, but that's not good either since TypeAnalyser should not care about those details.

@@ -3680,7 +3683,14 @@ def analyze(self, type: Type) -> None:
analyzer = TypeAnalyserPass3(self.fail)
type.accept(analyzer)

def fail(self, msg: str, ctx: Context, *, blocker: bool = False) -> None:
def fail(self, msg: str, ctx: Context, *, blocker: bool = False,
implicit_any: bool = False) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above - not sure if it's good to have an implicit_any parameter.

if not self.options.warn_implicit_any or self.errors.file.endswith('.pyi'):
return
# TempNode, so must have already reported in the first pass
if ctx.get_line() == -1:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a hack but I couldn't find a way to do it better. Perhaps, someone with more mypy experience may help with that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you talking about checking for stubs or checking for temporary nodes? I don't know if I would cosnider either of them a hack (my threshold of hackiness in the error-reporting code is much higher than in normal code); but if anyone has a better solution, it would be nice.

@@ -174,6 +175,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
return self.analyze_callable_type(t)
elif fullname == 'typing.Type':
if len(t.args) == 0:
self.implicit_any('Type without type args', t)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There doesn's seem to be a test case for this. It's probably worth having one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

@@ -183,6 +185,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
if self.nesting_level > 0:
self.fail('Invalid type: ClassVar nested inside other type', t)
if len(t.args) == 0:
self.implicit_any('ClassVar without type args', t)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There doesn's seem to be a test case for this. It's probably worth having one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

@@ -220,6 +224,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
# context. This is slightly problematic as it allows using the type 'Any'
# as a base class -- however, this will fail soon at runtime so the problem
# is pretty minor.
self.implicit_any('Assigning value of type Any', t)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There doesn's seem to be a test case for this. It's probably worth having one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

mypy/typeanal.py Outdated
@@ -259,6 +264,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
fallback=instance)
return instance
else:
self.implicit_any('Fallback', t)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be a debug statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, how can you tell that this cannot happen in a production run?

def fail(self, msg: str, ctx: Context, *, blocker: bool = False,
implicit_any: bool = False) -> None:
if implicit_any:
if not self.options.warn_implicit_any or self.errors.file.endswith('.pyi'):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a better way for checking for stub files but I can't seem to find it.

Why are you limiting the flag to non-stub files anyway?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also can't find it.

In stub files, a lot of the times the type argument in genertic types is omitted (e.g., Union[type, Tuple], def f() -> tuple:, def g() -> Iterable:, etc.). There'll be hundreds of warnings like this, making the flag unusable.
I am sure we don't want to change the stubs to avoid implicit Any (and even if we did, that change would have to happen first, before warn_implicit_any starts paying attention to stubs).

@ilinum
Copy link
Collaborator

ilinum commented May 24, 2017

Just remembered this doesn't cover the cases when not all type parameters are specified.

So like Dict[str] would not be covered.

@ilevkivskyi
Copy link
Member

@ilinum

So like Dict[str] would not be covered.

What do you mean by this? Dict[str] is already an error.

@ilinum
Copy link
Collaborator

ilinum commented May 30, 2017

@ilevkivskyi you are right. I looked at the code and it didn't seem like it would handle these kind of errors. But I just ran mypy now and it is an error.

@ilinum
Copy link
Collaborator

ilinum commented Jun 14, 2017

@pkch are you interested in working on this?

@pkch
Copy link
Contributor Author

pkch commented Jun 14, 2017

Ah yes, I'm going to make the fixes I'm aware of. However, I'm still not sure if the overall approach is acceptable.

ilinum added a commit to ilinum/mypy that referenced this pull request Jun 30, 2017
(--disallow-any=generics)

This code is based on python#3141 by pkch.

This option disallows implicit Anys from omitted type parameters to generic types.
For instance, `def x() -> List` would produce an error while
`def x() -> List[Any]` is allowed.

Note that with the flag enabled builtin generic types such as `list` and `set`
are also forbidden.
@ilinum
Copy link
Collaborator

ilinum commented Jun 30, 2017

I reworked this PR and submitted it as #3637.

Closing this PR in favor of the new one.

@ilinum ilinum closed this Jun 30, 2017
ilinum added a commit that referenced this pull request Jul 5, 2017
(--disallow-any=generics)

This code is based on #3141 by pkch.

This option disallows implicit Anys from omitted type parameters to generic types.
For instance, `def x() -> List` would produce an error while
`def x() -> List[Any]` is allowed.

Note that with the flag enabled builtin generic types such as `list` and `set`
are also forbidden.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants