-
Notifications
You must be signed in to change notification settings - Fork 3.1k
[REVERTED] Add infer Product with Serializable linter flag #5990
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad you got this going!
@@ -74,6 +74,7 @@ trait Warnings { | |||
val Inaccessible = LintWarning("inaccessible", "Warn about inaccessible types in method signatures.", true) | |||
val NullaryOverride = LintWarning("nullary-override", "Warn when non-nullary `def f()' overrides nullary `def f'.", true) | |||
val InferAny = LintWarning("infer-any", "Warn when a type argument is inferred to be `Any`.", true) | |||
val InferProductWithSerializable = LintWarning("infer-product-with-serializable", "Warn when a type argument is inferred to be `Product with Serializable`.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not do what it says it does.
> scala -Xlint:infer-any -Xlint:infer-product-with-serializable
[info] Running scala.tools.nsc.MainGenericRunner -usejavacp -Xlint:infer-any -Xlint:infer-product-with-serializable
Welcome to Scala 2.12.3-20170710-215443-d31a518 (OpenJDK 64-Bit Server VM, Java 1.8.0_131).
Type in expressions for evaluation. Or try :help.
scala> def f[A](as: A*) = 12
f: [A](as: A*)Int
scala> f(1, "2")
<console>:13: warning: a type was inferred to be `Any`; this may indicate a programming error.
f(1, "2")
^
res0: Int = 12
scala> f((1,2), List(3, 4))
res1: Int = 12
scala>
Instead it does (as the test cases say):
scala> val f = if (true) (1,2) else List(3, 4)
<console>:11: warning: a type was inferred to be `Product with Serializable`; this may indicate a programming error
val f = if (true) (1,2) else List(3, 4)
^
f: Product with Serializable = (1,2)
scala>
which is 👍 👍, but personally I think it should do both of the above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, the problems of copying semi-related examples. I need to update the docs as well. Adding inference checks for parameters makes sense.
// if enabled, validate that the now inferred val or def type isn't PwS | ||
if (settings.warnInferProductWithSerializable && context.reportErrors) { | ||
tpe match { | ||
case RefinedType(ProductRootTpe :: SerializableTpe :: _, _) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly check that scope.isEmpty
here? I'm unsure how it would really make a difference, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm kind of unclear of the purpose there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RefinedType
is defined as case class RefinedType(override val parents: List[Type], override val decls: Scope)
, you could check that decls
is empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost forgot to fix this check. Updated as well.
@@ -712,6 +712,7 @@ trait Definitions extends api.StandardDefinitions { | |||
def tupleComponents(tp: Type) = tp.dealiasWiden.typeArgs | |||
|
|||
lazy val ProductRootClass: ClassSymbol = requiredClass[scala.Product] | |||
lazy val ProductRootTpe: Type = typeOf[scala.Product] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ProductRootClass.tpe
seems to be more in line with what is done elsewhere in this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to me!
Also, having changed |
Alright, I've updated the docs, added parameter checking, did a little refactoring for infer-any to support PwS in the same way, added the type to JavaUniverseForce, added more tests for parameter inference, and checked for an empty scope. I think that about covers all the current comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very nice work!
case _ => | ||
} | ||
} | ||
tpe |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need the check here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Namer check is for vals and defs, while the Infer check is for generic parameters. I can't find a suitable place in the compiler that would work for both situations without any duplication unfortunately, though that's mainly due to my unfamiliarity with the codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. Would it make sense to add the infer-any
check here as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be handy feature improvement. I'll go ahead and add it in.
case _ => | ||
}) | ||
// Ditto for Product with Serializable | ||
def canWarnAboutPwS = canWarnAbout(tps => (tps exists (_ contains ProductRootClass)) && (tps exists (_ contains SerializableClass))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you write a few more tests that exploit this path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can add some more that are similar to the infer-any checks, though I'm not as clear about how this parameter situation arises compared to vals and defs.
Is it clear that the type will always be |
The typically inferred type is fully Also, I've pushed an update to the partest. I've added a couple more tests there. |
a861726
to
4f3c783
Compare
if (settings.warnInferAny && canWarnAboutAny) { | ||
warnIfInferred { | ||
_.typeSymbol match { | ||
case (AnyClass | AnyValClass) => true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parens are extra.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
Dangling issue for improvement in this area scala/bug#9211 I remember there was once a partest that occasionally printed |
I've been unable to find a situation to cause the reverse ordering of types like that, but I can change the RefinedType check to look for the opposite ordering. I'm not confident that changing them to check for any location besides the first two types would be appropriate, but I could also go that route if there aren't any issues. |
Maybe you can also figure out why the following doesn't warn:
The same is true for |
AFAIR we had a reversed order at some point during the migration from ant to sbt. I don't remember the exact reason, probably a change in the classpath. |
I've pushed an update that adds val and def checks for Any and AnyVal (along with neg and neg-neg test updates) along with updates to the command line docs. |
@@ -97,6 +98,7 @@ trait Warnings { | |||
def warnInaccessible = lint contains Inaccessible | |||
def warnNullaryOverride = lint contains NullaryOverride | |||
def warnInferAny = lint contains InferAny | |||
def warnInferProductWithSerializable = lint contains InferProductWithSerializable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't be averse to using warnInferPwS
everywhere. Then you preserve alignment, which is king.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to use the PwS acronym for brevity.
I was surprised the following doesn't currently warn, and I guess still won't.
|
This adds a new lint warning, -Xlint:infer-pws which warns when an inferred value or method type is Product with Serializable. This is oftentimes a programmer error from mixing up what would otherwise be incompatible types (which could be caught by the -Xlint:infer-any flag otherwise) that have the common PwS supertype. As this inferred type tends to be useless, this lint flag helps avoid errors related to this inference. In addition, this also extends the same val and def inference check to -Xlint:infer-any to make it consistent with the new linter setting. Variable and method definitions that are inferred to be Any or AnyVal will also add a warning unless they have explicit types ascribed.
Is there anything else you all would like me to change in this PR? Or can it be merged for 2.12.4? |
Reviewers, could one of you add the "approved" stamp? |
LGTM in case that's still the stamp of approval. |
The newfangled github stamp is the preferred indicator these days, but it's good to see scabot can still apply reviewed labels. |
Maybe there's a difference between "leaving review comments" (one-offs) vs "starting a review" where the full review has the approve button? IATM like the politicians said in ads, "I approved this |
Thanks for the reviews! |
under
but since this PR was merged (at least, I'm assuming this is the PR responsible, I didn't bisect), it gives:
this was caught by the Scala 2.12 community build; the new warning causes several projects with |
ah, I guess this is "adds val and def checks for Any and AnyVal" |
@jvz can you look through the new warnings in https://scala-ci.typesafe.com/view/All/job/scala-2.12.x-integrate-community-build/1992/consoleFull and make sure that they are actually intended...? we can always temporarily disable |
(there is a larger discussion to be had... not here on this PR... about how much, and what kind, of linting it's appropriate to keep adding to scalac. it would be really nice if linting were its own phase (or perhaps a compiler plugin) and were Scalafix based, rather than just inserted into compiler code everywhere) |
It's like having to write I can attest to the great head ache of wading through community build hiccups. It's like walking on gravel in thin-soled shoes. Was the contributor sufficiently warned? Besides the CLA, there ought to be a release form you have to fill out. |
The
|
Bookmarking:
I thought something like that came in up the discussion? |
This doesn't sound correct. My check is supposed to only be finding things with just the first two types, and it wouldn't really make sense in that example to declare a type. The discussion did bring up the question about what if a type was inferred to be @SethTisue as for the thunk one, that looks like the appropriate warning being detected, though it does seem to be a bit overzealous in that you already do have an explicit type for Also, as to adding linter checks to the compiler, yes, I'd rather see it in a separate tool in the future that would not only be easier to modify (scalac's source code is hard to follow), though when I put this PR together, I wasn't really aware of scalacheck, wartremover, et al. Perhaps this would be a nice focus in Dotty? |
Oh, and that check for the inferred Any, I believe I added that after the PwS check. See the review comments by @lrytz |
I'm sorry we had to revert this (see scala/bug#10535), but the release deadline is too close to refine this in time for 2.12.4. I would love to get a refinement into 2.12.5! |
This adds a new lint warning, -Xlint:infer-product-with-serializable
which warns when an inferred value or method type is Product with
Serializable. This is oftentimes a programmer error from mixing up what
would otherwise be incompatible types (which could be caught by the
-Xlint:infer-any flag otherwise) that have the common PwS supertype. As
this inferred type tends to be useless, this lint flag helps avoid
errors related to this inference.
Requesting review by @adriaanm in particular as this seems related to his expertise.