Closed gabro closed 7 years ago
My reasons why this kind of refactoring would be useful:
1) It's an established coding style to annotate non-private members with their return type. My reasons for this are:
If this ticket gets accepted, I think it would be great if users could have a bit of flexibility and choose between making the type explicit for all members, or only for non-private ones.
By the way, I would rename the ticket to "Explicit return type for members" (instead of methods)
If this ticket get accepted, I think it would be great if users could have a bit of flexibility and choose between making the type explicit for all members, or only for non-private ones.
That's what I was thinking about too. I would like to choose between private/public and def/values.
I also believe some cases would be a tad awkward, like
-val someValue = "something"
+val someValue: String = "something"
but I'm not exactly sure of where to draw the line here.
This is a great idea! I am all on the benefits of explicit type annotations, this rewrite been a personal dream of mine for a long time :)
This rewrite will pretty much be the same rewrite as ExplicitImplicit modulo some configuration bits.
The main reason behind https://github.com/scalameta/scalameta/pull/848 was to experiment with using the new denotations section in the Scalameta semanticdb to implement this rewrite. However there are still some remaining issues
Hold that thought, wrote my response above in a rush. For explicit type annotations, the second bullet is not a problem since the type annotation is inserted at definition site, which we compile with scalahost. The first problem is still valid, but we can already start exploring implementing this rewrite under the assumption that we will later have the tools necessary to pretty-print the fully qualified references.
Thanks for the analysis, sounds promising!
About the configuration part, do you have any plan on including a config per-rewrite?
Or should we just explode the cartesian product of rewrites x options
?
On rewrite config I've considered reserving the path custom.*
, or something like that. Rewrites could access the config as a ctx.config.custom: metaconfig.Conf
. Then they can optionally parse it into a case class or just access specific fields directly. I'll have to update the docs on this.
I just merged https://github.com/scalacenter/scalafix/pull/163 which upgraded to the latest meta and re-enabled ExplicitImplicit.
I'm thinking of renaming ExplicitImplicit to ExplicitTypes and then open an ExplicitTypeSettings (or similar) with options like
explicitTypes.what = Implicits/Public/Protected/Private/..
explicitTypes.skipLiterals = true # skips stuff like `val x = "blah"`
...
Suggestions for reasonable configurations options are welcome! Please include original and expected output.
Maybe ExplicitReturnTypes
?
@olafurpg Awesome stuff! I'll definitely try it out. About the configuration options, I think what you have now is already pretty great. Maybe, I'd consider splitting explicitTypes.what
into explicitTypes.memberVisibility
and explicitTypes.memberKind
, e.g.,
explicitTypes.memberVisibility = All/Public/Protected/Private # this could be cumulative, e.g., Public+Protected should be possible
explicitTypes.memberKind = All/Implicit/Field/Method # Also this could be cumulative
Configuration with roughly that shape @dotta has been merged into master, see https://github.com/scalacenter/scalafix/blob/db6152304aaef19c4a8ba4fc77b732264bf52fec/scalafix-core/src/main/scala/scalafix/config/ExplicitReturnTypesConfig.scala However, ExplicitReturnTypes does not respect this configuration at the moment. It only works for implicit vals.
I've renamed the title of this issue to be about making ExplicitReturnTypes respect this configuration.
I believe this would be a great issue for someone who wants to contribute and learn more about scalafix. Don't hesitate to ask me if you have any questions.
I'm so sorry for making issue reference dirty! I sent a PR for this :)
I ran into an odd edge case yesterday, not related to this pr but worth keeping in mind. I was calling a library api that had a public builder for a private type. In this case type inference works, but type annotation fails.
@ShaneDelmore Mind you if I ask you to provide your edge case sample codes with me? I'll check it on my PR branch.
@taisukeoe It would be my pleasure, thanks for your awesome work on this so far.
Here is a simple scastie reproduction, let me know if you need more than that.
https://scastie.scala-lang.org/ShaneDelmore/9nCslzmcTqKTPo5BWwPupg/1
The problem comes from this pattern: There is a builder, which definitely has the type InstrumentFactory https://github.com/kamon-io/Kamon/blob/kamon-1.0-develop/kamon-core/src/main/scala/kamon/metric/InstrumentFactory.scala#L79
and a class which is private https://github.com/kamon-io/Kamon/blob/kamon-1.0-develop/kamon-core/src/main/scala/kamon/metric/InstrumentFactory.scala#L29
I actually think this is a bug in Scala, but as this rewrite is used with the compilers we have now I thought you should know about it.
@ShaneDelmore Thanks for providing info. Though the Scastie reproduction doesn't have either type inference nor type annotation (and successfully runs), what should I do to reproduce?
Just in case, I confirmed ExplicitReturnType rewrite works for a private class and a builder, as follows. Please let me know if this test case isn't relevant for your case.
Oh wow, that is the completely wrong scastie link. Oops, give me a few minutes and I will recreate a proper reproduction.
Hopefully this scastie works, sometimes it doesn't appear to save my code so in case this scastie link is also broken I will put the reproduction code below as well:
https://scastie.scala-lang.org/wdskxrQQRbeB2ZHkDdXaoA
app.scala: import kamon.metric.InstrumentFactory import com.typesafe.config._
object Main extends App { val thisWorks = InstrumentFactory.fromConfig(ConfigFactory.load()) val thisDoesNotWork: InstrumentFactory = InstrumentFactory.fromConfig(ConfigFactory.load()) println("Finished") }
build.sbt: scalaVersion := "2.12.2" scalacOptions ++= Seq( "-deprecation", "-encoding", "UTF-8", "-feature", "-unchecked" )
resolvers += Resolver.bintrayRepo("kamon-io", "snapshots")
libraryDependencies += "com.typesafe" % "config" % "1.3.1" libraryDependencies += "io.kamon" %% "kamon-core" % "1.0.0-alpha1-b2b43016b2b652ebcd686959048e3c5fe427e649"
@ShaneDelmore Thanks! I could reproduce it as follows:
package factory{
private [factory] class Factory
object Factory{
def f(): factory.Factory = new Factory()
}
}
object G {
//type annotation fails to compile!
val f: factory.Factory = factory.Factory.f()
}
While I would also think it's a Scala problem, it would be great if there is a way NOT to avoid rewriting this case in ExplicitReturnType rewrite. No one doesn't want to rewrite anything into not compile-able source codes!
However, I'm wondering if there is any smart way to check if it's dennotated type is declared in Private or not. Should I search mirror.database.denotations
everytime?
It seems we may want to report this issue into scala/bug issue tracker for now.
Bug filed at https://github.com/scala/bug/issues/10366
Following an offline chat with @dotta at Scala Italy, adding an explicit return type should be within the realm of the possible and it could greatly improve compile times.
This kind of mimics the idea of ExplicitImplicit.
@olafurpg do you see any blocker for this one?