-
Notifications
You must be signed in to change notification settings - Fork 3.1k
-Xlint
now includes -Xlint:named-booleans
to lint unnamed boolean literal args
#10612
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
48844ed
to
16445b5
Compare
currentTyper.silent(_.typed(expr, mode, pt), reportAmbiguousErrors = false) match { | ||
case analyzer.SilentResultValue(result) => | ||
trace("success: ")(showAttributed(result, true, true, settings.Yshowsymkinds.value)) | ||
trace("success: ")(showAttributed(result, printKinds = settings.Yshowsymkinds.value)) |
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 was a bug. it suggests that requiring names can be broadly useful. (Here, the many booleans are not distinguished by type.)
16445b5
to
737b37d
Compare
Wasn't there a merged retronym PR #2164 Edit: superseded by #2203 I agree with the old paulp comment that it's better to improve API, for example Also there is no lint to make sure I did not accidentally incur an arg block, by writing named args out of order. There is a ticket about the rewrite not using temporaries of constants, so an application that only switches constants could skip the block and maybe warn. The possibly legit use of long tuples of booleans is in tests, but it would be nice to have a cleaner way to create a table and use it, for that use case. |
737b37d
to
be92c18
Compare
The festival held once every ten years is called The Gathering of the Names, in memory of all those unnamed program elements which shall go unnamed 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.
This looks great, thanks!
@@ -79,10 +79,10 @@ trait ContextOps { self: TastyUniverse => | |||
??? | |||
} | |||
|
|||
@inline final def assert(assertion: Boolean, msg: => Any): Unit = | |||
@inline final def assert(@deprecatedName assertion: Boolean, msg: => Any): Unit = |
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, a secret handshake!
Can we mention Would a similar lint make sense for |
I considered whether other literals suffer the same issue (lack of expressiveness): I will try using less deprecatedName for magic names like I used to exclude constructors; that idea was to exclude mere wrappers. But it's hard to know what semantically merely wraps, as opposed to I will mention deprecatedName wherever possible. Also no special handling of update. |
Worth documenting that refchecks compares Edit: the comment is stale, except for the bit about what you see at the Apply node after case class rewrite of the Select. |
da5fe5b
to
4cc74f3
Compare
4cc74f3
to
c7281e6
Compare
Squashed, in a surge of confidence. Avoided "boolean default arg" in main code, allowed here/there in tests. |
c7281e6
to
b0cc56e
Compare
That is,
The |
ff2addf
to
77320f3
Compare
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.
It occured to me while looking at this, making an exception for the first parameter might be a useful heuristic. It would cover things like assert
or literalBool
. But it might also be too surprising - what do you think?
src/compiler/scala/tools/nsc/transform/async/TransformUtils.scala
Outdated
Show resolved
Hide resolved
Re-arranged the commits (with a small diff: https://github.com/scala/scala/compare/77320f3bffca6d6e295a137278e481f8d405e371..e7da16c208b25b4919fca0de562a5da730318d96)
That could be narrowed down to "if the first parameter is a boolean and there are no other boolean parameters". |
OK I'll just book the education for getting the symbol of an array apply. You may issue me a paper certificate. Feel free to squash, I was just busy yesterday and tentative. I'm glad for your simplifications. |
👍 What do you think about the heuristic for |
74dda37
to
b3dacf8
Compare
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.
LGTM otherwise!
} | ||
loop(fn, 0) | ||
} | ||
def isAssertParadigm(params: List[Symbol]): Boolean = !sym.isConstructor && !sym.isCaseApplyOrUnapply && { |
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 not convinced we should make a difference between constructors and other methods 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.
classes are less likely to have a name that makes its parameter meaningful, unlike a method which is of narrower semantic scope.
val EmptyInlineInfo = InlineInfo(false, None, SortedMap.empty, None)
I wound up adding names to all four args.
I thought I had given it one last scan. Now the diff is long and dreary enough that I tune out rather quickly. This effort is less about a lint than about how to write a method. Whose idea was it to have more than one parameter anyway? If you accept my theory mitigating the lrytz exclusion for constructors and case apply, you may enable merging with a green review. |
@som-snytt mind updating the PR description to lead with user-facing documentation? |
If Keats were alive today (for some interval), he would pen I should say, he would pun Then critics for hundreds of years following would debate whether edit is a button or a menu item, and whether Keats intended it ironically. |
-Xlint:named-booleans
to lint unnamed boolean literal args [ci: last-only]
I'm seeing this when building 2.13 from sbt without bootstrapping.
|
@retronym I thought I fixed that, but maybe I broke it instead. Probably I should have silenced warnings the old-fashioned way until re-starr; I wound up not making accommodations in regular code (improving the heuristic instead), but left some in tests. I'll review this PR (now) for those changes that should wait for re-starr. It's been so long since I had to wait for re-starr, I kind of forgot what it was like, the thrill and the sense of anticipation. Oh wait, that is Christmas morning. Edit: I remember now, the fix was for specifying You wouldn't believe how often I |
I had a good experience with this in a closed-source codebase. I got two helpful warnings and no annoying ones. |
I need it to also lint
|
@@ -24,10 +24,10 @@ import scala.tools.nsc.Reporting.WarningCategory | |||
trait MatchTreeMaking extends MatchCodeGen with Debugging { | |||
import global._, definitions._, CODE._ | |||
|
|||
final case class Suppression(suppressExhaustive: Boolean, suppressUnreachable: Boolean) | |||
final case class Suppression private (suppressExhaustive: Boolean, suppressUnreachable: Boolean) |
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 is charming but makes copy
hard to use (under -Xsource:3
).
-Xlint:named-booleans
to lint unnamed boolean literal args [ci: last-only]-Xlint:named-booleans
to lint unnamed boolean literal args [ci: last-only]
Fixes scala/scala#10612 (cherry picked from commit a1ca2b3)
Fixes scala/scala#10612 (cherry picked from commit a1ca2b3)
-Xlint:named-booleans
to lint unnamed boolean literal args [ci: last-only]-Xlint
now includes -Xlint:named-booleans
to lint unnamed boolean literal args
It's likely we'll adjust this for 2.13.14; let's discuss on #10704 |
Lint unnamed boolean literal args [ci: last-only]
Lint unnamed boolean literal args [ci: last-only]
Summary
Since boolean literals are not expressive of intention when passed as arguments, suggest using a named argument. Instead of
throwBalloons(true, false)
, the intent is clearer with:The lint should warn only for boolean flags which may invert the meaning of the method call. It only warns if the parameter is of type
Boolean
, so thatSome(true)
orgetOrElse(false)
does not warn.Enabling/disabling
The new lint is part of the package of default lints enabled when you pass
-Xlint
.To enable the new lint in isolation, use
-Xlint:booleanLiterals
.To disable the new lint while leaving other default lints enabled, use
-Xlint:-booleanLiterals
.Details
As a special exemption to reduce false positives, the lint will not warn if there is only one boolean parameter in first position, such as
assert(false, msg)
. Consequently, methods taking a single boolean parameter are expected to have a meaningful name that makes the semantics of the parameter obvious, such asOption.when(true)
.Compare:
Since class names tend to be nouns instead of specific verbs, constructors (and also proxies, i.e., case class
apply
) will warn:It's terrible to rely on naming for expressiveness or (the illusion of) type safety, as opposed to actual types that ensure you can't accidentally swap
start
andend
values (which one assumes is unlikely only if one reads from left to right or if one does not routinely mix things up), but this lint provides some "support" for the common case. By "support", we understand that it "enables" this bad behavior.The lint will not warn if the parameter is annotated
deprecatedName
:Note that it does not warn for tuples, but in future, named tuples should be encouraged for this idiom:
Possible future improvements could be to warn for other anonymous literals such as
null
, primitives such as42
, or special cases of zero or empty such asNone
,Nil
, orSet.empty
.Further details
The corresponding parameter must be of
Boolean
type.Some(true)
does not warn; nor doesbs.fold(false)
.Exclude methods with exactly one boolean parameter which is in first position, like
assert
. Such methods are expected to be tastefully named, so that the boolean needn't be.Parameters marked
@deprecatedName
are excluded.Stratagems include a judicious mix of use named arg, sprinkle deprecatedName, or leverage default args (by omitting the literal arg in favor of a default). Also nowarn. These workarounds are mostly applied in test code.