Skip to content
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

Implement VolatileLazyVal rewrite rule. #7

Merged
merged 1 commit into from
Sep 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 4 additions & 10 deletions cli/src/main/scala/scalafix/cli/ArgParserImplicits.scala
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
package scalafix.cli

import scalafix.rewrite.ProcedureSyntax
import scalafix.rewrite.Rewrite
import scalafix.rewrite.VolatileLazyVal

import java.io.InputStream
import java.io.PrintStream

import caseapp.core.ArgParser

object ArgParserImplicits {
def nameMap[T](t: sourcecode.Text[T]*): Map[String, T] = {
t.map(x => x.source -> x.value).toMap
}

val rewriteMap: Map[String, Rewrite] = nameMap(
ProcedureSyntax
)
implicit val rewriteRead: ArgParser[Rewrite] = ArgParser.instance[Rewrite] {
str =>
rewriteMap.get(str) match {
Rewrite.name2rewrite.get(str) match {
case Some(x) => Right(x)
case _ =>
Left(
s"invalid input $str, must be one of ${rewriteMap.keys.mkString(", ")}")
val availableKeys = Rewrite.name2rewrite.keys.mkString(", ")
Left(s"invalid input $str, must be one of $availableKeys")
}
}

Expand Down
10 changes: 5 additions & 5 deletions cli/src/main/scala/scalafix/cli/Cli.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package scalafix.cli

import scala.collection.GenSeq
import scalafix.FixResult
import scalafix.Fixed
import scalafix.Scalafix
import scalafix.cli.ArgParserImplicits._
import scalafix.rewrite.Rewrite
Expand Down Expand Up @@ -30,7 +30,7 @@ case class CommonOptions(
case class ScalafixOptions(
@HelpMessage(
s"Rules to run, one of: ${Rewrite.default.mkString(", ")}"
) rewrites: List[Rewrite] = Rewrite.default,
) rewrites: List[Rewrite] = Rewrite.default.toList,
@Hidden @HelpMessage(
"Files to fix. Runs on all *.scala files if given a directory."
) @ExtraName("f") files: List[String] = List.empty[String],
Expand All @@ -56,13 +56,13 @@ object Cli extends AppOf[ScalafixOptions] {

def handleFile(file: File, config: ScalafixOptions): Unit = {
Scalafix.fix(FileOps.readFile(file), config.rewrites) match {
case FixResult.Success(code) =>
case Fixed.Success(code) =>
if (config.inPlace) {
FileOps.writeFile(file, code)
} else config.common.out.write(code.getBytes)
case FixResult.Failure(e) =>
case Fixed.Failure(e) =>
config.common.err.write(s"Failed to fix $file. Cause: $e".getBytes)
case e: FixResult.ParseError =>
case e: Fixed.ParseError =>
if (config.files.contains(file)) {
// Only log if user explicitly specified that file.
config.common.err.write(e.toString.getBytes())
Expand Down
6 changes: 3 additions & 3 deletions cli/src/main/scala/scalafix/cli/Scalafix210.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package scalafix.cli

import scalafix.FixResult
import scalafix.Fixed
import scalafix.Scalafix
import scalafix.rewrite.Rewrite
import scalafix.util.logger

class Scalafix210 {
def fix(originalContents: String, filename: String): String = {
Scalafix.fix(originalContents, Rewrite.default) match {
case FixResult.Success(fixedCode) => fixedCode
case FixResult.Error(e) =>
case Fixed.Success(fixedCode) => fixedCode
case Fixed.Failure(e) =>
logger.warn(s"Failed to fix $filename. Cause ${e.getMessage}")
originalContents
}
Expand Down
25 changes: 0 additions & 25 deletions core/src/main/scala/scalafix/FixResult.scala

This file was deleted.

24 changes: 24 additions & 0 deletions core/src/main/scala/scalafix/Fixed.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package scalafix

import scala.meta.inputs.Position

abstract sealed class Fixed {
def get: String = this match {
case Fixed.Success(code) => code
case Fixed.Failure(e) => throw e
}
}

object Fixed {
case class Success(code: String) extends Fixed
class Failure(val e: Throwable) extends Fixed
object Failure {
def apply(exception: Throwable): Failure = new Failure(exception)
def unapply(arg: Failure): Option[Throwable] = {
Some(arg.e)
}
}

case class ParseError(pos: Position, message: String, exception: Throwable)
extends Failure(exception)
}
12 changes: 6 additions & 6 deletions core/src/main/scala/scalafix/Scalafix.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import scala.util.control.NonFatal
import scalafix.rewrite.Rewrite

object Scalafix {
def fix(code: String, rewriters: Seq[Rewrite] = Rewrite.default): FixResult = {
def fix(code: String, rewriters: Seq[Rewrite] = Rewrite.default): Fixed = {
fix(Input.String(code), rewriters)
}

def fix(code: Input, rewriters: Seq[Rewrite]): FixResult = {
rewriters.foldLeft[FixResult](
FixResult.Success(String.copyValueOf(code.chars))) {
case (newCode: FixResult.Success, rewriter) =>
def fix(code: Input, rewriters: Seq[Rewrite]): Fixed = {
rewriters.foldLeft[Fixed](
Fixed.Success(String.copyValueOf(code.chars))) {
case (newCode: Fixed.Success, rewriter) =>
try rewriter.rewrite(Input.String(newCode.code))
catch {
case NonFatal(e) => FixResult.Failure(e)
case NonFatal(e) => Fixed.Failure(e)
}
case (failure, _) => failure
}
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/scalafix/rewrite/ProcedureSyntax.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package scalafix.rewrite

import scala.meta._
import scalafix.FixResult
import scalafix.Fixed

object ProcedureSyntax extends Rewrite {
override def rewrite(code: Input): FixResult = {
override def rewrite(code: Input): Fixed = {
withParsed(code) { ast =>
val toPrepend = ast.collect {
case t: Defn.Def if t.decltpe.exists(_.tokens.isEmpty) =>
Expand All @@ -21,7 +21,7 @@ object ProcedureSyntax extends Rewrite {
sb.append(token.syntax)
}
val result = sb.toString()
FixResult.Success(result)
Fixed.Success(result)
}
}
}
19 changes: 13 additions & 6 deletions core/src/main/scala/scalafix/rewrite/Rewrite.scala
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
package scalafix.rewrite

import scala.meta._
import scalafix.FixResult
import scalafix.Fixed

abstract class Rewrite {

def rewrite(code: Input): FixResult
def rewrite(code: Input): Fixed

protected def withParsed(code: Input)(f: Tree => FixResult): FixResult = {
protected def withParsed(code: Input)(f: Tree => Fixed): Fixed = {
code.parse[Source] match {
case Parsed.Success(ast) => f(ast)
case Parsed.Error(pos, msg, details) =>
FixResult.ParseError(pos, msg, details)
Fixed.ParseError(pos, msg, details)
}
}
}

object Rewrite {
val default: List[Rewrite] = List(
ProcedureSyntax
private def nameMap[T](t: sourcecode.Text[T]*): Map[String, T] = {
t.map(x => x.source -> x.value).toMap
}

val name2rewrite: Map[String, Rewrite] = nameMap[Rewrite](
ProcedureSyntax,
VolatileLazyVal
)

val default: Seq[Rewrite] = name2rewrite.values.toSeq
}
34 changes: 34 additions & 0 deletions core/src/main/scala/scalafix/rewrite/VolatileLazyVal.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package scalafix.rewrite

import scala.meta._
import scalafix.Fixed
import scalafix.util.logger

object VolatileLazyVal extends Rewrite {
private object NonVolatileLazyVal {
def unapply(defn: Defn.Val): Option[Token] = {
defn.mods.collectFirst {
case x if x.syntax == "@volatile" =>
None
case x if x.syntax == "lazy" =>
Some(x.tokens.head)
}
}.flatten
}
override def rewrite(code: Input): Fixed = {
withParsed(code) { ast =>
val toPrepend: Seq[Token] = ast.collect {
case NonVolatileLazyVal(tok) => tok
}
val sb = new StringBuilder
ast.tokens.foreach { token =>
if (toPrepend.contains(token)) {
sb.append("@volatile ")
}
sb.append(token.syntax)
}
val result = sb.toString()
Fixed.Success(result)
}
}
}
2 changes: 1 addition & 1 deletion core/src/test/scala/scalafix/ScalafixSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class ScalafixSuite extends FunSuite with DiffAssertions {

test("on parse error") {
val obtained = Scalafix.fix("object A {")
assert(obtained.isInstanceOf[FixResult.ParseError])
assert(obtained.isInstanceOf[Fixed.ParseError])
}

}
57 changes: 57 additions & 0 deletions core/src/test/scala/scalafix/rewrite/LazyValSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package scalafix.rewrite

import scala.meta.inputs.Input
import scalafix.Fixed
import scalafix.util.DiffAssertions

import org.scalatest.FunSuiteLike

class RewriteSuite(rewrite: Rewrite) extends FunSuiteLike with DiffAssertions {

def rewriteTest(name: String, original: String, expected: String): Unit = {
test(name) {
rewrite.rewrite(Input.String(original)) match {
case Fixed.Success(obtained) =>
assertNoDiff(obtained, expected)
case Fixed.Failure(e) =>
throw e
}
}
}

}

class LazyValSuite extends RewriteSuite(VolatileLazyVal) {

rewriteTest(
"basic",
"""|object a {
|
|val foo = 1
|
| lazy val x = 2
| @volatile lazy val dontChangeMe = 2
|
| class foo {
| lazy val z = {
| reallyHardStuff()
| }
| }
|}
""".stripMargin,
"""|object a {
|
|val foo = 1
|
| @volatile lazy val x = 2
| @volatile lazy val dontChangeMe = 2
|
| class foo {
| @volatile lazy val z = {
| reallyHardStuff()
| }
| }
|}
""".stripMargin
)
}