Skip to content

Commit

Permalink
Emit deprecation warnings when referencing a rewrite by old name.
Browse files Browse the repository at this point in the history
This change allows rewrites to change their names without breaking
compatibility. Users using the old name will receive deprecation
warnings and have opportunity to switch to the new name.
  • Loading branch information
olafurpg committed Aug 17, 2017
1 parent 22863b7 commit 664fcba
Show file tree
Hide file tree
Showing 11 changed files with 64 additions and 24 deletions.
6 changes: 1 addition & 5 deletions scalafix-cli/src/main/scala/scalafix/cli/Cli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,7 @@ object ScalafixRewriteNames {
val result = cliCommand match {
case CliCommand.PrintAndExit(msg, exit) =>
if (exit.isOk) commonOptions.out.println(msg)
else {
ScalafixReporter.default
.copy(outStream = commonOptions.err)
.error(msg)
}
else commonOptions.reporter.error(msg)
exit
case CliCommand.RunScalafix(runner) =>
val exit = runner.run()
Expand Down
3 changes: 2 additions & 1 deletion scalafix-cli/src/main/scala/scalafix/cli/CliRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@ object CliRunner {
if (kind.isSyntactic) None
else computeAndCacheDatabase()
}
private val lazySemanticCtx: LazySemanticCtx = resolveDatabase
private val lazySemanticCtx: LazySemanticCtx =
new LazySemanticCtx(resolveDatabase, common.reporter)

// expands a single file into a list of files.
def expand(matcher: FilterMatcher)(path: AbsolutePath): Seq[FixFile] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import java.io.InputStream
import java.io.PrintStream
import scala.meta._
import scala.meta.io.AbsolutePath
import scalafix.internal.config.PrintStreamReporter
import scalafix.internal.config.ScalafixReporter
import scalafix.internal.rewrite.ProcedureSyntax
import scalafix.rewrite.ScalafixRewrites
import caseapp._
Expand All @@ -16,6 +18,8 @@ case class CommonOptions(
@Hidden err: PrintStream = System.err,
@Hidden stackVerbosity: Int = 20
) {
lazy val reporter: PrintStreamReporter =
ScalafixReporter.default.copy(outStream = out)
def workingPath = AbsolutePath(workingDirectory)
def workingDirectoryFile = new File(workingDirectory)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package object config {
configuration: String,
decoder: ConfDecoder[Rewrite]
): Configured[(Rewrite, internal.config.ScalafixConfig)] =
fromInput(Input.String(configuration), _ => None, Nil, decoder)
fromInput(Input.String(configuration), LazySemanticCtx.empty, Nil, decoder)

/** Load configuration from an input.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package scalafix.internal.config

import scalafix.SemanticCtx

// The challenge when loading a rewrite is that 1) if it's semantic it needs a
// semanticCtx constructor argument and 2) we don't know upfront if it's semantic.
// For example, to know if a classloaded rewrites is semantic or syntactic
// we have to test against it's Class[_]. For default rewrites, the interface
// to detect if a rewrite is semantic is different.
// LazySemanticCtx allows us to delay the computation of a semanticCtx right up until
// the moment we instantiate the rewrite.
//type LazySemanticCtx = RewriteKind => Option[SemanticCtx]
class LazySemanticCtx(
f: RewriteKind => Option[SemanticCtx],
val reporter: ScalafixReporter)
extends Function[RewriteKind, Option[SemanticCtx]] {
override def apply(v1: RewriteKind): Option[SemanticCtx] = f(v1)
}

object LazySemanticCtx {
lazy val empty = new LazySemanticCtx(_ => None, ScalafixReporter.default)
def apply(f: RewriteKind => Option[SemanticCtx]): LazySemanticCtx =
new LazySemanticCtx(f, ScalafixReporter.default)
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,20 @@ trait ScalafixMetaconfigReaders {
ScalafixRewrites.syntaxName2rewrite ++
semanticCtx.fold(Map.empty[String, Rewrite])(
ScalafixRewrites.name2rewrite)
ReaderUtil.fromMap(names).read(conf)
val result = ReaderUtil.fromMap(names).read(conf)
result match {
case Ok(rewrite) =>
rewrite.rewriteName
.reportDeprecationWarning(value, getSemanticCtx.reporter)
case _ =>
}
result
}

private lazy val semanticRewriteClass = classOf[SemanticRewrite]

def classloadRewrite(
semanticCtx: LazySemanticCtx): Class[_] => Seq[SemanticCtx] = { cls =>
val semanticRewrite =
cls.getClassLoader.loadClass("scalafix.rewrite.SemanticRewrite")
val kind =
if (semanticRewriteClass.isAssignableFrom(cls)) RewriteKind.Semantic
else RewriteKind.Syntactic
Expand Down Expand Up @@ -146,7 +151,7 @@ trait ScalafixMetaconfigReaders {
}

def baseSyntacticRewriteDecoder: ConfDecoder[Rewrite] =
baseRewriteDecoders(_ => None)
baseRewriteDecoders(LazySemanticCtx.empty)
def baseRewriteDecoders(semanticCtx: LazySemanticCtx): ConfDecoder[Rewrite] = {
MetaconfigPendingUpstream.orElse(
defaultRewriteDecoder(semanticCtx),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,4 @@ import scalafix.SemanticCtx

package object config extends ScalafixMetaconfigReaders {
type MetaParser = Parse[_ <: Tree]
// The challenge when loading a rewrite is that 1) if it's semantic it needs a
// semanticCtx constructor argument and 2) we don't know upfront if it's semantic.
// For example, to know if a classloaded rewrites is semantic or syntactic
// we have to test against it's Class[_]. For default rewrites, the interface
// to detect if a rewrite is semantic is different.
// LazySemanticCtx allows us to delay the computation of a semanticCtx right up until
// the moment we instantiate the rewrite.
type LazySemanticCtx = RewriteKind => Option[SemanticCtx]
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package scalafix.rewrite

import scalafix.internal.config.ScalafixReporter

/** A thin wrapper around a string name and optional deprecation warning. */
final case class RewriteIdentifier(
value: String,
deprecated: Option[scala.deprecated]
deprecated: Option[scalafix.util.Deprecated]
) {
override def toString: String = value
}
Expand All @@ -22,6 +24,16 @@ final case class RewriteName(identifiers: List[RewriteIdentifier]) {
def +(other: RewriteName): RewriteName =
new RewriteName((identifiers :: other.identifiers :: Nil).flatten)
override def toString: String = name
def reportDeprecationWarning(name: String, reporter: ScalafixReporter): Unit = {
identifiers.foreach { ident =>
if (ident.value == name) {
ident.deprecated.foreach { d =>
reporter.warn(
s"Name $name is deprecated. ${d.message} (since ${d.since})")
}
}
}
}
}

object RewriteName {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package scalafix.util

/** Identical to scala.deprecated except it's a case class. */
final case class Deprecated(message: String, since: String)
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import metaconfig.ConfDecoder

object ScalafixReflect {
def syntactic: ConfDecoder[Rewrite] =
fromLazySemanticCtx(_ => None)
fromLazySemanticCtx(LazySemanticCtx.empty)

def semantic(semanticCtx: SemanticCtx): ConfDecoder[Rewrite] =
fromLazySemanticCtx(_ => Some(semanticCtx))
fromLazySemanticCtx(LazySemanticCtx(_ => Some(semanticCtx)))

def fromLazySemanticCtx(semanticCtx: LazySemanticCtx): ConfDecoder[Rewrite] =
rewriteConfDecoder(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package scalafix.testkit
import scala.meta._
import scalafix.SemanticCtx
import scalafix.Rewrite
import scalafix.internal.config.LazySemanticCtx
import scalafix.internal.config.ScalafixConfig
import scalafix.reflect.ScalafixReflect
import org.scalatest.exceptions.TestFailedException
Expand Down Expand Up @@ -32,11 +33,12 @@ object DiffTest {
.collectFirst {
case Token.Comment(comment) =>
val decoder =
ScalafixReflect.fromLazySemanticCtx(_ => Some(semanticCtx))
ScalafixReflect.fromLazySemanticCtx(
LazySemanticCtx(_ => Some(semanticCtx)))
ScalafixConfig
.fromInput(
Input.VirtualFile(label, stripPrefix(comment)),
_ => Some(semanticCtx))(decoder)
LazySemanticCtx(_ => Some(semanticCtx)))(decoder)
.get
}
.getOrElse(throw new TestFailedException(
Expand Down

0 comments on commit 664fcba

Please sign in to comment.