From 18e7b8c7a01d6f256e81d4603a37d615ae33fe7a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 7 Jun 2019 07:30:21 +0200 Subject: [PATCH 1/4] Implement (the start of) the Any2StringAdd rewrite --- .../scala/fix/scala213/Any2StringAdd.scala | 46 +++++++++++++++++++ .../scala/fix/scala213/Any2StringAdd.scala | 43 +++++++++++++++++ .../META-INF/services/scalafix.v1.Rule | 1 + .../scala/fix/scala213/Any2StringAdd.scala | 24 ++++++++++ 4 files changed, 114 insertions(+) create mode 100644 input/src/main/scala/fix/scala213/Any2StringAdd.scala create mode 100644 output/src/main/scala/fix/scala213/Any2StringAdd.scala create mode 100644 rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala diff --git a/input/src/main/scala/fix/scala213/Any2StringAdd.scala b/input/src/main/scala/fix/scala213/Any2StringAdd.scala new file mode 100644 index 0000000..0c72fc8 --- /dev/null +++ b/input/src/main/scala/fix/scala213/Any2StringAdd.scala @@ -0,0 +1,46 @@ +/* +rule = fix.scala213.Any2StringAdd +*/ +package fix.scala213 + +abstract class Any2StringAdd { + // Strings: leave as-is, both literal strings and non-literal + def s = "bob" + def str1 = s + s + def str2 = s + "bob" + def str3 = "bob" + s + def str4 = "bob" + "fred" + + // Non-strings: add toString + def nil = Nil + s + + // Non-string, generic type: add toString + type A + def x: A + def generic = x + "bob" + + // Primitives: add toString + def unit = () + def bool = true + def byte = 1.toByte + def short = 1.toShort + def char = 'a' + def int = 1 + def long = 1L + def float = 1.0F + def double = 1.0 + // + def unit1 = unit + s + def bool1 = bool + s + def byte1 = byte + s // TODO + def short1 = short + s // TODO + def char1 = char + s // TODO + def int1 = int + s // TODO + def long1 = long + s // TODO + def float1 = float + s // TODO + def double1 = double + s // TODO + + // With infix operators, make sure to use parens + def parens1 = Nil ++ Nil + s // TODO + def parens2 = int + int + s // TODO +} diff --git a/output/src/main/scala/fix/scala213/Any2StringAdd.scala b/output/src/main/scala/fix/scala213/Any2StringAdd.scala new file mode 100644 index 0000000..19811fa --- /dev/null +++ b/output/src/main/scala/fix/scala213/Any2StringAdd.scala @@ -0,0 +1,43 @@ +package fix.scala213 + +abstract class Any2StringAdd { + // Strings: leave as-is, both literal strings and non-literal + def s = "bob" + def str1 = s + s + def str2 = s + "bob" + def str3 = "bob" + s + def str4 = "bob" + "fred" + + // Non-strings: add toString + def nil = Nil.toString + s + + // Non-string, generic type: add toString + type A + def x: A + def generic = x.toString + "bob" + + // Primitives: add toString + def unit = () + def bool = true + def byte = 1.toByte + def short = 1.toShort + def char = 'a' + def int = 1 + def long = 1L + def float = 1.0F + def double = 1.0 + // + def unit1 = unit.toString + s + def bool1 = bool.toString + s + def byte1 = byte + s // TODO + def short1 = short + s // TODO + def char1 = char + s // TODO + def int1 = int + s // TODO + def long1 = long + s // TODO + def float1 = float + s // TODO + def double1 = double + s // TODO + + // With infix operators, make sure to use parens + def parens1 = Nil ++ Nil + s // TODO + def parens2 = int + int + s // TODO +} diff --git a/rewrites/src/main/resources/META-INF/services/scalafix.v1.Rule b/rewrites/src/main/resources/META-INF/services/scalafix.v1.Rule index 5de30ed..653eaa1 100644 --- a/rewrites/src/main/resources/META-INF/services/scalafix.v1.Rule +++ b/rewrites/src/main/resources/META-INF/services/scalafix.v1.Rule @@ -1,3 +1,4 @@ +fix.scala213.Any2StringAdd fix.scala213.Core fix.scala213.ScalaSeq fix.scala213.Varargs diff --git a/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala b/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala new file mode 100644 index 0000000..1d7fa4e --- /dev/null +++ b/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala @@ -0,0 +1,24 @@ +package fix.scala213 + +import scalafix.v1._ + +import scala.meta._ + +object Any2StringAdd { + val any2stringadd = Symbol("scala/Predef.any2stringadd().") +} + +final class Any2StringAdd extends SemanticRule("fix.scala213.Any2StringAdd") { + import Any2StringAdd._ + + override def fix(implicit doc: SemanticDocument): Patch = { + doc.tree.collect { + case Term.ApplyInfix(lhs, Term.Name("+"), _, List(_)) if + lhs.synthetic.collect { + case ApplyTree(TypeApplyTree(IdTree(info), _), _) => info.symbol + }.contains(any2stringadd) + => + Patch.addRight(lhs, ".toString") + }.asPatch + } +} From 6829a8fca17e131da4f75a6f8953cac10767272a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 7 Jun 2019 12:22:50 +0200 Subject: [PATCH 2/4] Handle more any2stringAdd + applications --- .../src/main/scala/fix/scala213/Any2StringAdd.scala | 3 ++- .../src/main/scala/fix/scala213/Any2StringAdd.scala | 3 ++- .../src/main/scala/fix/scala213/Any2StringAdd.scala | 13 ++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/input/src/main/scala/fix/scala213/Any2StringAdd.scala b/input/src/main/scala/fix/scala213/Any2StringAdd.scala index 0c72fc8..a6b1bc0 100644 --- a/input/src/main/scala/fix/scala213/Any2StringAdd.scala +++ b/input/src/main/scala/fix/scala213/Any2StringAdd.scala @@ -41,6 +41,7 @@ abstract class Any2StringAdd { def double1 = double + s // TODO // With infix operators, make sure to use parens - def parens1 = Nil ++ Nil + s // TODO + def parens1 = Nil ++ Nil + s def parens2 = int + int + s // TODO + def parens3 = {Nil ++ Nil} + s } diff --git a/output/src/main/scala/fix/scala213/Any2StringAdd.scala b/output/src/main/scala/fix/scala213/Any2StringAdd.scala index 19811fa..bee9aab 100644 --- a/output/src/main/scala/fix/scala213/Any2StringAdd.scala +++ b/output/src/main/scala/fix/scala213/Any2StringAdd.scala @@ -38,6 +38,7 @@ abstract class Any2StringAdd { def double1 = double + s // TODO // With infix operators, make sure to use parens - def parens1 = Nil ++ Nil + s // TODO + def parens1 = (Nil ++ Nil).toString + s def parens2 = int + int + s // TODO + def parens3 = {Nil ++ Nil}.toString + s } diff --git a/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala b/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala index 1d7fa4e..8223ca3 100644 --- a/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala +++ b/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala @@ -5,7 +5,7 @@ import scalafix.v1._ import scala.meta._ object Any2StringAdd { - val any2stringadd = Symbol("scala/Predef.any2stringadd().") + val any2stringaddPlus = SymbolMatcher.exact("scala/Predef.any2stringadd#`+`().") } final class Any2StringAdd extends SemanticRule("fix.scala213.Any2StringAdd") { @@ -13,12 +13,11 @@ final class Any2StringAdd extends SemanticRule("fix.scala213.Any2StringAdd") { override def fix(implicit doc: SemanticDocument): Patch = { doc.tree.collect { - case Term.ApplyInfix(lhs, Term.Name("+"), _, List(_)) if - lhs.synthetic.collect { - case ApplyTree(TypeApplyTree(IdTree(info), _), _) => info.symbol - }.contains(any2stringadd) - => - Patch.addRight(lhs, ".toString") + case any2stringaddPlus(Term.ApplyInfix(lhs, _, _, _)) => + lhs match { + case _: Term.Name | _: Term.Select | _: Term.Block => Patch.addRight(lhs, ".toString") + case _ => Patch.addLeft(lhs, "(") + Patch.addRight(lhs, ").toString") + } }.asPatch } } From 7319678485636f85059c5e3b1deb0d30fcb58710 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 7 Jun 2019 13:44:14 +0200 Subject: [PATCH 3/4] Handle primitives plus string --- .../scala/fix/scala213/Any2StringAdd.scala | 23 ++++++++++++------- .../scala/fix/scala213/Any2StringAdd.scala | 23 ++++++++++++------- .../scala/fix/scala213/Any2StringAdd.scala | 22 +++++++++++++----- 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/input/src/main/scala/fix/scala213/Any2StringAdd.scala b/input/src/main/scala/fix/scala213/Any2StringAdd.scala index a6b1bc0..2978da0 100644 --- a/input/src/main/scala/fix/scala213/Any2StringAdd.scala +++ b/input/src/main/scala/fix/scala213/Any2StringAdd.scala @@ -32,16 +32,23 @@ abstract class Any2StringAdd { // def unit1 = unit + s def bool1 = bool + s - def byte1 = byte + s // TODO - def short1 = short + s // TODO - def char1 = char + s // TODO - def int1 = int + s // TODO - def long1 = long + s // TODO - def float1 = float + s // TODO - def double1 = double + s // TODO + def byte1 = byte + s + def byte2 = byte + byte + def short1 = short + s + def short2 = short + short + def char1 = char + s + def char2 = char + char + def int1 = int + s + def int2 = int + int + def long1 = long + s + def long2 = long + long + def float1 = float + s + def float2 = float + float + def double1 = double + s + def double2 = double + double // With infix operators, make sure to use parens def parens1 = Nil ++ Nil + s - def parens2 = int + int + s // TODO + def parens2 = int + int + s def parens3 = {Nil ++ Nil} + s } diff --git a/output/src/main/scala/fix/scala213/Any2StringAdd.scala b/output/src/main/scala/fix/scala213/Any2StringAdd.scala index bee9aab..bc6d70c 100644 --- a/output/src/main/scala/fix/scala213/Any2StringAdd.scala +++ b/output/src/main/scala/fix/scala213/Any2StringAdd.scala @@ -29,16 +29,23 @@ abstract class Any2StringAdd { // def unit1 = unit.toString + s def bool1 = bool.toString + s - def byte1 = byte + s // TODO - def short1 = short + s // TODO - def char1 = char + s // TODO - def int1 = int + s // TODO - def long1 = long + s // TODO - def float1 = float + s // TODO - def double1 = double + s // TODO + def byte1 = byte.toString + s + def byte2 = byte + byte + def short1 = short.toString + s + def short2 = short + short + def char1 = char.toString + s + def char2 = char + char + def int1 = int.toString + s + def int2 = int + int + def long1 = long.toString + s + def long2 = long + long + def float1 = float.toString + s + def float2 = float + float + def double1 = double.toString + s + def double2 = double + double // With infix operators, make sure to use parens def parens1 = (Nil ++ Nil).toString + s - def parens2 = int + int + s // TODO + def parens2 = (int + int).toString + s def parens3 = {Nil ++ Nil}.toString + s } diff --git a/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala b/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala index 8223ca3..0a99d43 100644 --- a/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala +++ b/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala @@ -5,7 +5,16 @@ import scalafix.v1._ import scala.meta._ object Any2StringAdd { - val any2stringaddPlus = SymbolMatcher.exact("scala/Predef.any2stringadd#`+`().") + val plusString = SymbolMatcher.exact( + "scala/Predef.any2stringadd#`+`().", + "scala/Byte#`+`().", + "scala/Short#`+`().", + "scala/Char#`+`().", + "scala/Int#`+`().", + "scala/Long#`+`().", + "scala/Float#`+`().", + "scala/Double#`+`().", + ) } final class Any2StringAdd extends SemanticRule("fix.scala213.Any2StringAdd") { @@ -13,11 +22,12 @@ final class Any2StringAdd extends SemanticRule("fix.scala213.Any2StringAdd") { override def fix(implicit doc: SemanticDocument): Patch = { doc.tree.collect { - case any2stringaddPlus(Term.ApplyInfix(lhs, _, _, _)) => - lhs match { - case _: Term.Name | _: Term.Select | _: Term.Block => Patch.addRight(lhs, ".toString") - case _ => Patch.addLeft(lhs, "(") + Patch.addRight(lhs, ").toString") - } + case plusString(Term.ApplyInfix(lhs, _, _, _)) => addToString(lhs) }.asPatch } + + private def addToString(term: Term) = term match { + case _: Term.Name | _: Term.Select | _: Term.Block => Patch.addRight(term, ".toString") + case _ => Patch.addLeft(term, "(") + Patch.addRight(term, ").toString") + } } From f36f9995216553b5c209b586260be5515acee933 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 8 Jun 2019 09:46:29 +0200 Subject: [PATCH 4/4] Avoid boxing primitives with Any2StringAdd --- .../main/scala/fix/scala213/Any2StringAdd.scala | 16 ++++++++-------- .../main/scala/fix/scala213/Any2StringAdd.scala | 12 +++++++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/output/src/main/scala/fix/scala213/Any2StringAdd.scala b/output/src/main/scala/fix/scala213/Any2StringAdd.scala index bc6d70c..710eaa9 100644 --- a/output/src/main/scala/fix/scala213/Any2StringAdd.scala +++ b/output/src/main/scala/fix/scala213/Any2StringAdd.scala @@ -29,23 +29,23 @@ abstract class Any2StringAdd { // def unit1 = unit.toString + s def bool1 = bool.toString + s - def byte1 = byte.toString + s + def byte1 = "" + byte + s def byte2 = byte + byte - def short1 = short.toString + s + def short1 = "" + short + s def short2 = short + short - def char1 = char.toString + s + def char1 = "" + char + s def char2 = char + char - def int1 = int.toString + s + def int1 = "" + int + s def int2 = int + int - def long1 = long.toString + s + def long1 = "" + long + s def long2 = long + long - def float1 = float.toString + s + def float1 = "" + float + s def float2 = float + float - def double1 = double.toString + s + def double1 = "" + double + s def double2 = double + double // With infix operators, make sure to use parens def parens1 = (Nil ++ Nil).toString + s - def parens2 = (int + int).toString + s + def parens2 = "" + (int + int) + s def parens3 = {Nil ++ Nil}.toString + s } diff --git a/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala b/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala index 0a99d43..6039cf6 100644 --- a/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala +++ b/rewrites/src/main/scala/fix/scala213/Any2StringAdd.scala @@ -5,8 +5,8 @@ import scalafix.v1._ import scala.meta._ object Any2StringAdd { - val plusString = SymbolMatcher.exact( - "scala/Predef.any2stringadd#`+`().", + val any2stringaddPlusString = SymbolMatcher.exact("scala/Predef.any2stringadd#`+`().") + val primitivePlusString = SymbolMatcher.exact( "scala/Byte#`+`().", "scala/Short#`+`().", "scala/Char#`+`().", @@ -22,7 +22,8 @@ final class Any2StringAdd extends SemanticRule("fix.scala213.Any2StringAdd") { override def fix(implicit doc: SemanticDocument): Patch = { doc.tree.collect { - case plusString(Term.ApplyInfix(lhs, _, _, _)) => addToString(lhs) + case any2stringaddPlusString(Term.ApplyInfix(lhs, _, _, _)) => addToString(lhs) + case primitivePlusString(Term.ApplyInfix(lhs, _, _, _)) => blankStringPlus(lhs) }.asPatch } @@ -30,4 +31,9 @@ final class Any2StringAdd extends SemanticRule("fix.scala213.Any2StringAdd") { case _: Term.Name | _: Term.Select | _: Term.Block => Patch.addRight(term, ".toString") case _ => Patch.addLeft(term, "(") + Patch.addRight(term, ").toString") } + + private def blankStringPlus(term: Term) = term match { + case _: Term.Name | _: Term.Select | _: Term.Block => Patch.addLeft(term, """"" + """) + case _ => Patch.addLeft(term, """"" + (""") + Patch.addRight(term, ")") + } }