![JAR search and dependency download from the Maven repository](/logo.png)
ongo.reactivemongo-scalafix_2.12.1.1.0-RC14.source-code.Upgrade.scala Maven / Gradle / Ivy
The newest version!
package reactivemongo.scalafix
import scalafix.v1._
import scala.meta._
final class Upgrade extends SemanticRule("ReactiveMongoUpgrade") { self =>
override def fix(implicit doc: SemanticDocument): Patch =
Patch.fromIterable(doc.tree.children.map(transformer))
// ---
private def transformer(
implicit
doc: SemanticDocument): Tree => Patch = {
val fixes = Seq[Fix](
coreUpgrade,
apiUpgrade, gridfsUpgrade, bsonUpgrade, streamingUpgrade, playUpgrade)
val fixImport: PartialFunction[Importer, Patch] =
(fixes.foldLeft(PartialFunction.empty[Importer, Patch]) {
_ orElse _.`import`
}).orElse({ case _ => Patch.empty })
val pipeline = fixes.foldLeft[PartialFunction[Tree, PatchDirective]]({
case Import(importers) =>
Patch.fromIterable(importers.map(fixImport))
}) {
_ orElse _.refactor
}
{ tree: Tree =>
def recurse() = Patch.fromIterable(tree.children.map(transformer))
pipeline.lift(tree).map { p =>
if (p.patch.nonEmpty) {
p.patch
} else if (p.recurse && tree.children.nonEmpty) {
recurse()
} else {
Patch.empty
}
}.getOrElse {
//println(s"tree = ${tree.structure}")
if (tree.children.nonEmpty) {
recurse()
} else {
Patch.empty
}
}
}
}
private def streamingUpgrade(implicit doc: SemanticDocument) = Fix(
refactor = {
case t @ Term.Apply.After_4_6_0(
Term.Select(c @ Term.Name(_), Term.Name("responseSource")), _) if (
c.symbol.info.exists(
_.signature.toString startsWith "AkkaStreamCursor")) =>
migrationRequired(t, "Use bulkSource")
case t @ Term.Apply.After_4_6_0(
Term.Select(c @ Term.Name(_), Term.Name("responsePublisher")), _) if (
c.symbol.info.exists(
_.signature.toString startsWith "AkkaStreamCursor")) =>
migrationRequired(t, "Use bulkPublisher")
case t @ Term.Apply.After_4_6_0(
Term.Select(c @ Term.Name(_), Term.Name("responseEnumerator")), _) if (
c.symbol.info.exists(
_.signature.toString startsWith "PlayIterateesCursor")) =>
migrationRequired(t, "Use bulkEnumerator")
})
private def coreUpgrade(implicit doc: SemanticDocument) = Fix(
`import` = {
case Importer(
s @ Term.Select(
Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("core")),
Term.Name("actors")), Term.Name("Exceptions")
), importees) => {
val patches = Seq.newBuilder[Patch]
importees.foreach {
case i @ Importee.Name(n @ Name.Indeterminate(
"ChannelNotFound" | "NodeSetNotReachable")) =>
val nme = Name.Indeterminate(s"${n.syntax}Exception")
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(s, List(importee"${nme}")))).atomic
case _ =>
}
Patch.fromIterable(patches.result())
}
case Importer(
s @ Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("core")),
Term.Name("errors")
),
importees
) => {
val patches = Seq.newBuilder[Patch]
importees.foreach {
case i @ Importee.Name(
Name.Indeterminate("DetailedDatabaseException" |
"GenericDatabaseException")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(s, List(importee"DatabaseException")))).atomic
case i @ Importee.Name(
Name.Indeterminate("ConnectionException" |
"ConnectionNotInitialized" |
"DriverException" | "GenericDriverException")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(s, List(importee"ReactiveMongoException")))).atomic
case _ =>
}
Patch.fromIterable(patches.result())
}
},
refactor = {
case t @ Type.Name(n @ ("ChannelNotFound" | "NodeSetNotReachable")) if (
testSym(t)(_ startsWith "reactivemongo/core/actors/Exceptions")) =>
Patch.replaceTree(t, s"${n}Exception")
case t @ Type.Name("DetailedDatabaseException" |
"GenericDatabaseException") if (testSym(t)(
_ startsWith "reactivemongo/core/errors/")) =>
Patch.replaceTree(t, "DatabaseException")
case t @ Type.Name("ConnectionException" |
"ConnectionNotInitialized" |
"DriverException" |
"GenericDriverException") if (testSym(t)(
_ startsWith "reactivemongo/core/errors/")) =>
Patch.replaceTree(t, "ReactiveMongoException")
})
private object QueryBuilderNaming {
private val mapping = Map(
"partial" -> "allowPartialResults",
"sortOption" -> "sort",
"projectionOption" -> "projection",
"hintOption" -> "hint",
"explainFlag" -> "explain",
"snapshotFlag" -> "snapshot",
"commentString" -> "comment",
"maxTimeMsOption" -> "maxTimeMs")
def unapply(name: String): Option[String] = mapping.get(name)
}
private final class InsertExtractor(implicit doc: SemanticDocument) {
def unapply(tree: Tree): Option[Tuple3[Option[Type], Term, List[Term]]] = tree match {
case t @ Term.Apply.After_4_6_0(
Term.Select(c, Term.Name("insert")), Term.ArgClause(args, _)) if (
t.symbol.info.exists { i =>
i.signature match {
case MethodSignature(_, List(a, b) :: _, _) if {
i.toString.startsWith(
"reactivemongo/api/collections/GenericCollection") &&
a.symbol.info.exists(_.displayName == "document") &&
b.symbol.info.exists(_.displayName == "writeConcern")
} => true
case _ =>
false
}
}) =>
Some((Option.empty[Type], c, args))
case t @ Term.Apply.After_4_6_0(Term.ApplyType.After_4_6_0(
Term.Select(c, Term.Name("insert")), Type.ArgClause(List(tpe))),
Term.ArgClause(args, _)) if (
t.symbol.info.exists { i =>
i.signature match {
case MethodSignature(_, List(a, b) :: _, _) if {
i.toString.startsWith(
"reactivemongo/api/collections/GenericCollection") &&
a.symbol.info.exists(_.displayName == "document") &&
b.symbol.info.exists(_.displayName == "writeConcern")
} => true
case _ =>
false
}
}) =>
Some((Some(tpe), c, args))
case _ =>
None
}
}
private final class UpdateExtractor(implicit doc: SemanticDocument) {
def unapply(tree: Tree): Option[Tuple3[List[Type], Term, List[Term]]] = tree match {
case t @ Term.Apply.After_4_6_0(Term.ApplyType.After_4_6_0(
Term.Select(c, Term.Name("update")), Type.ArgClause(tpeParams)),
Term.ArgClause(args, _)) if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/collections/")) =>
Some(Tuple3(tpeParams, c, args))
case t @ Term.Apply.After_4_6_0(
Term.Select(c, Term.Name("update")),
Term.ArgClause(args, _)) if (
t.symbol.info.exists { i =>
i.signature match {
case MethodSignature(_, funArgs :: _, _) =>
(funArgs.size > 2 && i.toString.startsWith(
"reactivemongo/api/collections/GenericCollection"))
case _ =>
false
}
}) =>
Some(Tuple3(List.empty[Type], c, args))
case _ =>
None
}
}
private def apiUpgrade(implicit doc: SemanticDocument) = Fix(
`import` = {
case Importer(
cmdPkg @ Term.Select(
apiPkg @ Term.Select(Term.Name("reactivemongo"), Term.Name("api")),
Term.Name("commands")
),
importees
) => {
val patches = Seq.newBuilder[Patch]
importees.foreach {
case i @ Importee.Name(Name.Indeterminate("CollStatsResult")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(apiPkg, List(importee"CollectionStats")))).atomic
case i @ Importee.Name(Name.Indeterminate("CommandError")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(cmdPkg, List(importee"CommandException")))).atomic
case i @ Importee.Name(Name.Indeterminate("LastError")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(cmdPkg, List(importee"WriteResult")))).atomic
case i @ Importee.Name(Name.Indeterminate(
"GetLastError" | "WriteConcern")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(apiPkg, List(importee"WriteConcern")))).atomic
case i @ Importee.Name(
Name.Indeterminate(
"BoxedAnyVal" | "MultiBulkWriteResult" |
"UpdateWriteResult" | "UnitBox")) =>
patches += Patch.removeImportee(i)
case _ =>
}
Patch.fromIterable(patches.result())
}
case Importer(
apiPkg @ Term.Select(Term.Name("reactivemongo"), Term.Name("api")),
importees
) => {
val patches = Seq.newBuilder[Patch]
importees.foreach {
case i @ Importee.Name(Name.Indeterminate("BSONSerializationPack")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(
Term.Select(Term.Select(
apiPkg,
Term.Name("bson")), Term.Name("collection")),
List(importee"BSONSerializationPack")))).atomic
case i @ Importee.Name(Name.Indeterminate(
"DefaultDB" | "GenericDB")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(apiPkg, List(importee"DB")))).atomic
case i @ Importee.Name(Name.Indeterminate("MongoDriver")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(apiPkg, List(importee"AsyncDriver")))).atomic
case i @ Importee.Name(Name.Indeterminate("QueryOpts")) =>
patches += Patch.removeImportee(i)
case _ =>
}
Patch.fromIterable(patches.result())
}
},
refactor = {
val Insert = new InsertExtractor
val Update = new UpdateExtractor
{
case t @ Term.Apply.After_4_6_0(n @ (Term.Name("Index") | Term.Select(
Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("api")),
Term.Name("indexes")
),
Term.Name("Index")
)), Term.ArgClause(args, _)) if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/indexes/Index")) => {
val orderedArgs = Seq(
"key", "name", "unique", "background", "dropDups", "sparse",
"version", "partialFilter", "options")
val argMap = scala.collection.mutable.Map.empty[String, Term]
args.zipWithIndex.foreach {
case (Term.Assign(Term.Name(nme), a), _) =>
argMap.put(nme, a)
case (a, idx) =>
argMap.put(orderedArgs(idx), a)
}
argMap.remove("dropDups")
argMap ++= Map[String, Term](
"expireAfterSeconds" -> q"Option.empty[Int]",
"storageEngine" -> q"None",
"weights" -> q"None",
"defaultLanguage" -> q"Option.empty[String]",
"languageOverride" -> q"Option.empty[String]",
"textIndexVersion" -> q"Option.empty[Int]",
"sphereIndexVersion" -> q"Option.empty[Int]",
"bits" -> q"Option.empty[Int]",
"min" -> q"Option.empty[Double]",
"max" -> q"Option.empty[Double]",
"bucketSize" -> q"Option.empty[Double]",
"collation" -> q"None",
"wildcardProjection" -> q"None")
val argSyntax = argMap.map {
case (nme, term) => s"${nme} = ${term.syntax}"
}.mkString(", ")
Patch.replaceTree(t, s"${n.syntax}($argSyntax)")
}
// ---
case t @ Type.Select(Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("api")),
Term.Name("commands")), Type.Name("LastError")) =>
Patch.replaceTree(t, "reactivemongo.api.commands.WriteResult")
case t @ Type.Name("LastError") if (t.symbol.info.exists(_.toString startsWith "reactivemongo/api/commands/LastError")) =>
Patch.replaceTree(t, "WriteResult")
// ---
case t @ Type.Select(Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("api")),
Term.Name("commands")), Type.Name("GetLastError")) =>
Patch.replaceTree(t, "reactivemongo.api.WriteConcern")
case t @ Type.Name("GetLastError") if (t.symbol.info.exists(_.toString startsWith "reactivemongo/api/commands/GetLastError")) =>
Patch.replaceTree(t, "WriteConcern")
// ---
case t @ Type.Name("Index") if (t.symbol.info.exists(_.toString startsWith "reactivemongo/api/indexes/Index")) =>
Patch.replaceTree(t, "Index.Default")
case t @ Term.Name("CommandError") if (t.symbol.info.exists(_.toString startsWith "reactivemongo/api/commands/CommandError")) =>
Patch.replaceTree(t, "CommandException")
// ---
case t @ (Type.Name("QueryOpts") | Type.Select(Term.Select(
Term.Name("reactivemongo"), Term.Name("api")),
Type.Name("QueryOpts"))) =>
Patch.replaceTree(t, s"Nothing /* No longer exists: ${t.syntax} */")
case t @ Term.Apply.After_4_6_0(_, _) if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/QueryOpts")) =>
migrationRequired(t, "Directly use query builder") -> false
// ---
case t @ Term.Apply.After_4_6_0(
Term.Select(c, Term.Name("runValueCommand")),
Term.ArgClause(args, _)) if (t.symbol.info.exists(_.toString startsWith "reactivemongo/api/collections/GenericCollectionWithCommands")) =>
Patch.replaceTree(t, s"""${c.syntax}.runCommand(${args.map(_.syntax) mkString ", "})""")
case t @ Type.Apply.After_4_6_0(Type.Select(Term.Select(Term.Select(
Term.Name("reactivemongo"), Term.Name("api")),
Term.Name("commands")), Type.Name("BoxedAnyVal")),
Type.ArgClause(List(tpe))) =>
Patch.replaceTree(t, tpe.syntax)
case t @ Type.Apply.After_4_6_0(Type.Name("BoxedAnyVal"),
Type.ArgClause(List(tpe))) if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/commands/BoxedAnyVal")) =>
Patch.replaceTree(t, tpe.syntax)
case t @ Type.Singleton(Term.Name("UnitBox")) =>
Patch.replaceTree(t, "Unit")
case t @ Type.Singleton(Term.Select(Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("api")),
Term.Name("commands")), Term.Name("UnitBox"))) =>
Patch.replaceTree(t, "Unit")
case t @ Term.ApplyType.After_4_6_0(
Term.Select(s, Term.Name("unboxed")),
Type.ArgClause(List(_, r, c))) if (
t.symbol.info.exists(_.toString startsWith "reactivemongo/api/commands/Command.CommandWithPackRunner")) =>
Patch.replaceTree(t, s"${s.syntax}.apply[${r.syntax}, ${c.syntax}]")
case t @ Update((tpeParams, c, args)) if (!c.symbol.info.exists(
_.toString startsWith "reactivemongo/api/gridfs/GridFS#files")) => {
val appTArgs: String = tpeParams match {
case q :: u :: Nil => s"[$q, $u]"
case _ => ""
}
val orderedArgs = Seq(
"selector", "update", "writeConcern", "upsert", "multi")
val argMap = scala.collection.mutable.Map.empty[String, Term]
args.zipWithIndex.foreach {
case (Term.Assign(Term.Name(nme), a), _) =>
argMap.put(nme, a)
case (a, idx) =>
argMap.put(orderedArgs(idx), a)
}
val builderApply: String = argMap.get("writeConcern").fold("") { wc =>
s"(writeConcern = ${wc.syntax})"
}
val oneArgNames = Seq(
"selector" -> "q",
"update" -> "u",
"upsert" -> "upsert",
"multi" -> "multi")
val oneArgs = oneArgNames.flatMap {
case (n, r) => argMap.get(n).map { v =>
s"$r = ${v.syntax}"
}.toSeq
}.mkString("(", ", ", ")")
Patch.replaceTree(
t, s"${c.syntax}.update${builderApply}.one${appTArgs}${oneArgs}")
}
// ---
case t @ Insert((tpe, c, args)) => {
val appTArg = tpe.fold("") { at => s"[${at}]" }
args match {
case document :: Nil => document match {
case Term.Assign(_, d) =>
Patch.replaceTree(
t, s"${c.syntax}.insert.one${appTArg}($d)")
case _ =>
Patch.replaceTree(
t, s"${c.syntax}.insert.one${appTArg}($document)")
}
case List(a, b) => {
val (document, writeConcern) = a match {
case Term.Assign(Term.Name("writeConcern"), wc) =>
b -> wc
case _ => b match {
case Term.Assign(_, doc) =>
doc -> a
case _ =>
b -> a
}
}
val docArg = document match {
case Term.Assign(_, d) => d
case _ => document
}
Patch.replaceTree(t, s"${c.syntax}.insert(writeConcern = $writeConcern).one${appTArg}($docArg)")
}
case _ =>
Patch.empty
}
}
// ---
case t @ Term.Apply.After_4_6_0(Term.Select(
c, Term.Name("remove")), Term.ArgClause(args, _)) if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/collections/GenericCollection")) => {
val orderedArgs = Seq("selector", "writeConcern", "firstMatchOnly")
val argMap = scala.collection.mutable.Map.empty[String, Term]
args.zipWithIndex.foreach {
case (Term.Assign(Term.Name(nme), a), _) =>
argMap.put(nme, a)
case (a, idx) =>
argMap.put(orderedArgs(idx), a)
}
val delete = argMap.get("writeConcern") match {
case Some(wc) =>
s"${c.syntax}.delete(writeConcern = ${wc.syntax})"
case _ =>
s"${c.syntax}.delete"
}
val oneArgs = Seq.newBuilder[Term]
argMap.get("selector").foreach { q =>
oneArgs += q"q = ${q}"
}
argMap.get("firstMatchOnly").foreach { a =>
oneArgs += q"limit = { if (${a}) Some(1) else None }"
}
Patch.replaceTree(
t, s"${delete}.one(${oneArgs.result().map(_.syntax).mkString(", ")})")
}
// ---
case t @ Term.Select(Term.Name(_), n @ Term.Name("aggregateWith1")) if (
t.symbol.info.exists(_.toString startsWith "reactivemongo/api/collections/GenericCollection")) =>
Patch.replaceTree(n, "aggregateWith")
case t @ Term.Select(op @ Term.Name(_), Term.Name("makePipe")) if (t.symbol.info.exists(_.toString startsWith "reactivemongo/api/commands/AggregationFramework")) => Patch.replaceTree(t, op.syntax)
case t @ Term.Apply.After_4_6_0(a @ Term.Apply.After_4_6_0(
Term.ApplyType.After_4_6_0(Term.Select(
_, _), _), _), Term.ArgClause(List(Term.Block(
List(Term.Function.After_4_6_0(_, pipeline)))), _)) if (
t.symbol.info.exists(_.toString startsWith "reactivemongo/api/collections/GenericCollection#aggregateWith")) => {
val pn = Term.fresh("pipeline")
transformer(doc)(a) + Patch.addLeft(pipeline, s"{ val ${pn} = { ") +
transformer(doc)(pipeline) + Patch.addRight(
pipeline, s" }; ${pn}._1 +: ${pn}._2 }")
}
case t @ Term.Apply.After_4_6_0(s, Term.ArgClause(args, _)) if (
t.symbol.info.exists(_.toString startsWith "reactivemongo/api/collections/GenericCollection#aggregatorContext")) => {
var first = Option.empty[Term]
var other = Option.empty[Term]
val as = args.filter { a =>
a.symbol.info.forall { i =>
val s = i.toString
if (s.indexOf("PipelineOperator") != -1) {
first = Some(a)
false
} else if (s.indexOf("List") != -1) {
other = Some(a)
false
} else {
true
}
}
}
first match {
case Some(firstOp) => {
val pipeline = other match {
case Some(ops) =>
s"${firstOp.syntax} +: ${ops.syntax}"
case _ =>
s"List(${firstOp.syntax})"
}
val after = if (as.isEmpty) "" else {
", " + as.map(_.syntax).mkString(", ")
}
Patch.replaceTree(
t, s"${s.syntax}(pipeline = ${pipeline}${after})")
}
case _ =>
Patch.empty
}
}
// ---
case t @ Term.Select(
Term.Select(c, Term.Name("BatchCommands")),
Term.Name("AggregationFramework")
) if (t.symbol.info.exists(_.toString startsWith "reactivemongo/api/collections/bson/BSONBatchCommands")) =>
Patch.replaceTree(t, s"${c.syntax}.AggregationFramework")
case t @ Term.Select(c, Term.Name("aggregationFramework")) =>
Patch.replaceTree(t, s"${c.syntax}.AggregationFramework")
// ---
case t @ Term.Name(QueryBuilderNaming(newName)) if (t.symbol.info.exists(_.toString startsWith "reactivemongo/api/collections/GenericQueryBuilder")) =>
Patch.replaceTree(t, newName)
case t @ Term.Apply.After_4_6_0(
Term.Select(c, Term.Name("find")),
Term.ArgClause(List(a, b), _)) if (t.symbol.info.exists { i =>
val sym = i.toString
sym.startsWith("reactivemongo/api/collections/GenericCollection") &&
sym.indexOf("projection: J") != -1
}) => {
val (selector, projection): (Term, Term) = a match {
case Term.Assign(n @ Term.Name("projection"), arg) =>
b -> Term.Assign(n, q"Some(${arg})")
case _ => a -> (b match {
case Term.Assign(n @ Term.Name("projection"), arg) =>
Term.Assign(n, q"Some(${arg})")
case _ =>
q"Some($b)"
})
}
Patch.replaceTree(t, s"${c.syntax}.find($selector, $projection)")
}
// ---
case t @ Term.Apply.After_4_6_0(
Term.Select(c, Term.Name("rename")), Term.ArgClause(args, _)) if (
t.symbol.info.exists {
_.toString startsWith "reactivemongo/api/CollectionMetaCommands"
}) => {
val tn = Term.fresh("coll")
Patch.replaceTree(t, s"""{ val ${tn.syntax} = ${c.syntax}; ${tn.syntax}.db.connection.database("admin").flatMap(_.renameCollection(${tn.syntax}.db.name, ${tn.syntax}.name, ${args.map(_.syntax).mkString(", ")})) }""")
}
// ---
case t @ Term.Select(d,
Term.Name("connect" | "connection")) if (t.symbol.info.exists { x =>
val sym = x.toString
sym.startsWith("reactivemongo/api/MongoDriver") || sym.startsWith(
"reactivemongo/api/AsyncDriver")
}) =>
Patch.replaceTree(t, s"${d.syntax}.connect")
// ---
case t @ Term.Select(c, Term.Name(m)) if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/MongoConnection")) =>
m match {
case "askClose" =>
Patch.replaceTree(t, s"${c.syntax}.close")
case "parseURI" =>
Patch.replaceTree(t, s"${c.syntax}.fromString")
case _ =>
Patch.empty
}
// ---
case t @ Type.Name("CollStatsResult") if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/commands/")) =>
Patch.replaceTree(t, "CollectionStats")
// ---
case t @ Type.Name("DefaultDB" | "GenericDB") if (
t.symbol.info.exists(_.toString startsWith "reactivemongo/api/")) =>
Patch.replaceTree(t, "DB")
case t @ Type.Name("MongoDriver") if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/")) =>
Patch.replaceTree(t, "AsyncDriver")
case t @ Type.Name(n @ (
"MultiBulkWriteResult" | "UpdateWriteResult")) if (
t.symbol.toString startsWith "reactivemongo/api/commands/") =>
Patch.replaceTree(t, s"Any /* ${n} ~> anyCollection.MultiBulkWriteResult */")
}
})
private def playUpgrade(implicit doc: SemanticDocument) = {
object GridFSServe {
def unapply(t: Tree): Option[Tree] = find(t, false)
@annotation.tailrec
private def find(t: Tree, ctrl: Boolean): Option[Tree] = t match {
case x @ Term.Apply.After_4_6_0(fn, _) if (x.symbol.info.exists(
_.toString startsWith (
"play/modules/reactivemongo/MongoController"))) =>
find(fn, true)
case t @ Term.ApplyType.After_4_6_0(Term.Name("serve"), _) if ctrl =>
Some(t)
case a @ Term.ApplyType.After_4_6_0(Term.Name("serve"), _) =>
a.symbol.info.map(_.signature).flatMap {
case ClassSignature(_, parents, _, _) => parents.collectFirst {
case TypeRef(_, sym, _) if (sym.toString.startsWith(
"play/modules/reactivemongo/MongoController")) => a
}
case _ => Option.empty[Tree]
}
case _ => None
}
}
Fix(
`import` = {
case Importer(
x @ Term.Name("MongoController"), importees
) if (x.symbol.toString startsWith "play/modules/reactivemongo/") => {
val patches = Seq.newBuilder[Patch]
importees.foreach {
case i @ Importee.Name(Name.Indeterminate("JsGridFS")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(x, List(importee"GridFS")))).atomic
case i @ Importee.Name(Name.Indeterminate("readFileReads")) =>
patches += Patch.removeImportee(i)
case _ =>
}
Patch.fromIterable(patches.result())
}
case Importer(
Term.Select(
Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("play")),
Term.Name("json")
),
Term.Name("collection")
),
importees) => {
val patches = Seq.newBuilder[Patch]
importees.foreach {
case i @ Importee.Name(Name.Indeterminate("JSONCollection")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(
q"reactivemongo.api.bson.collection",
List(importee"BSONCollection")))).atomic
case i =>
patches += Patch.removeImportee(i)
}
Patch.fromIterable(patches.result())
}
case Importer(
Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("play")),
Term.Name("json")
), importees) => {
val patches = Seq.newBuilder[Patch]
importees.foreach {
case i @ Importee.Name(Name.Indeterminate("JSONSerializationPack")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(
q"reactivemongo.api.bson.collection",
List(importee"BSONSerializationPack")))).atomic
case i @ Importee.Name(Name.Indeterminate("BSONFormats")) =>
patches += (Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(
q"reactivemongo.play.json.compat",
List(importee"_")))).atomic
case _ =>
}
Patch.fromIterable(patches.result())
}
},
refactor = {
case GridFSServe(a) =>
Patch.replaceTree(a, "serve[reactivemongo.api.bson.BSONValue]")
// ---
case t @ Init.After_4_6_0(Type.Name("MongoController"), _, _) if (t.symbol.info.exists(_.toString startsWith "play/modules/reactivemongo/MongoController")) =>
(Patch.addGlobalImport(Importer(
q"reactivemongo.play.json.compat", List(importee"_"))) + Patch.
addGlobalImport(Importer(
q"reactivemongo.play.json.compat.json2bson", List(importee"_")))).
atomic
case t @ Term.Name("CONTENT_DISPOSITION_INLINE") if (t.symbol.info.exists(_.toString startsWith "play/modules/reactivemongo/MongoController#CONTENT_DISPOSITION_INLINE")) =>
Patch.replaceTree(t, """"inline"""")
case n @ Type.Name("JSONCollection") if (n.symbol.info.exists(
_.toString startsWith "reactivemongo/play/json/collection/")) =>
(Patch.replaceTree(n, "BSONCollection") + Patch.addGlobalImport(
Importer( // In case JSONCollection was coming from _ import
q"reactivemongo.api.bson.collection",
List(importee"BSONCollection")))).atomic
case t @ Term.Select(Term.Select(Term.Select(Term.Select(
Term.Name("reactivemongo"), Term.Name("play")),
Term.Name("json")), Term.Name("collection")),
Term.Name("JSONCollection")) =>
Patch.replaceTree(t, "reactivemongo.api.bson.collection.BSONCollection")
case n @ Type.Singleton(Term.Name("JSONSerializationPack")) =>
(Patch.replaceTree(
n, "BSONSerializationPack.type") + Patch.addGlobalImport(
Importer( // In case JSONSerializationPack was coming from _ import
q"reactivemongo.api.bson.collection",
List(importee"BSONSerializationPack")))).atomic
case t @ Term.Select(Term.Select(Term.Select(
Term.Name("reactivemongo"), Term.Name("play")),
Term.Name("json")), Term.Name("JSONSerializationPack")) =>
Patch.replaceTree(
t, "reactivemongo.api.bson.collection.BSONSerializationPack")
case n @ Type.Name("JsGridFS") =>
Patch.replaceTree(n, "GridFS")
case p @ Term.Apply.After_4_6_0(
Term.Apply.After_4_6_0(Term.Name("gridFSBodyParser"),
Term.ArgClause(gfs :: _, _)),
Term.ArgClause(_ :: _ :: mat :: Nil, _)) => {
if (gfs.symbol.info.map(
_.signature.toString).exists(_ startsWith "Future[")) {
Patch.replaceTree(p, s"""gridFSBodyParser($gfs)($mat)""")
} else {
Patch.replaceTree(
p, s"gridFSBodyParser(Future.successful($gfs))($mat)")
}
}
case p @ Term.Apply.After_4_6_0(
Term.Apply.After_4_6_0(Term.Name("gridFSBodyParser"),
Term.ArgClause(gfs :: _ :: _, _)),
Term.ArgClause(_ :: _ :: mat :: _ :: Nil, _)) =>
Patch.replaceTree(
p, s"gridFSBodyParser(Future.successful($gfs))($mat)")
// ---
case t @ Term.Select(Term.Name("BSONFormats"), Term.Name(n)) if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/play/json")) => {
if (n == "toJSON") {
Patch.replaceTree(t, "ValueConverters.fromValue")
} else if (n == "toBSON") {
Patch.replaceTree(t, "ValueConverters.toValue")
} else {
Patch.empty
}
}
case t @ Term.Select(Term.Select(Term.Select(Term.Select(Term.Name(
"reactivemongo"), Term.Name("play")),
Term.Name("json")), Term.Name("BSONFormats")), Term.Name(n)) => {
if (n == "toJSON") {
Patch.replaceTree(t, "reactivemongo.play.json.compat.fromValue")
} else if (n == "toBSON") {
Patch.replaceTree(t, "reactivemongo.play.json.compat.toValue")
} else {
Patch.empty
}
}
})
}
private class AfterWriteExtractor(implicit doc: SemanticDocument) {
def unapply(tree: Tree): Option[Term] = {
if (!tree.symbol.info.exists(_.toString startsWith "reactivemongo/bson/BSONWriter")) {
None
} else tree match {
case Term.Select(w, Term.Name("afterWrite")) =>
Some(w)
case Term.ApplyType.After_4_6_0(
Term.Select(w, Term.Name("afterWrite")), Type.ArgClause(List(_))) =>
Some(w)
case _ =>
None
}
}
}
private def bsonUpgrade(implicit doc: SemanticDocument) = {
val apiPkg = Term.Select(Term.Select(
Term.Name("reactivemongo"), Term.Name("api")), Term.Name("bson"))
val AfterWrite = new AfterWriteExtractor
Fix(
`import` = {
case i @ Importer(Term.Name("Macros"), is) if (
i.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/Macros")) => {
val m = Term.Select(apiPkg, Term.Name("Macros"))
Patch.fromIterable(is.map {
case i @ Importee.Name(Name.Indeterminate("Annotations")) =>
Patch.removeImportee(i)
case i =>
(Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(m, List(i)))).atomic
})
}
case i @ Importer(Term.Select(Term.Name("Macros"), m), is) if (
i.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/Macros")) => {
val pkg = Term.Select(Term.Select(apiPkg, Term.Name("Macros")), m)
Patch.fromIterable(is.map { i =>
(Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(pkg, List(i)))).atomic
})
}
case i @ Importer(n @ Term.Name("Annotations"), is) if (
i.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/Macros.Annotations")) => {
val m = Term.Select(Term.Select(apiPkg, Term.Name("Macros")), n)
Patch.fromIterable(is.map { i =>
(Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(m, List(i)))).atomic
})
}
// ---
case Importer(Term.Select(
Term.Name("reactivemongo"), Term.Name("bson")), is) => {
Patch.fromIterable(is.map {
case i @ Importee.Name(Name.Indeterminate("DefaultBSONHandlers")) =>
Patch.removeImportee(i)
case i =>
(Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(apiPkg, List(i)))).atomic
})
}
case Importer(Term.Select(Term.Select(
Term.Name("reactivemongo"), Term.Name("bson")),
Term.Name("DefaultBSONHandlers")), is) => {
Patch.fromIterable(is.map { i =>
(Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(apiPkg, List(i)))).atomic
})
}
case Importer(Term.Select(Term.Select(
Term.Name("reactivemongo"), Term.Name("bson")), x), is) => {
val pkg = Term.Select(apiPkg, x)
Patch.fromIterable(is.map { i =>
(Patch.removeImportee(i) + Patch.addGlobalImport(
Importer(pkg, List(i)))).atomic
})
}
},
refactor = {
case t @ Term.Select(companion @ Term.Select(Term.Select(Term.Select(
Term.Name("reactivemongo"), Term.Name("api")),
Term.Name("commands")), Term.Name("WriteResult")
), Term.Name("lastError")) =>
Patch.replaceTree(t, s"${companion.syntax}.Exception.unapply")
case t @ Term.Select(Term.Name(
"WriteResult"), Term.Name("lastError")) if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/commands/WriteResult")) =>
Patch.replaceTree(t, s"WriteResult.Exception.unapply")
// ---
case Term.Apply.After_4_6_0(aw @ AfterWrite(w),
Term.ArgClause(List(body @ Term.PartialFunction(_)), _)) if (
w.symbol.info.exists(
_.toString.indexOf("BSONDocumentWriter") == -1)) => {
val basePatch = Patch.replaceTree(aw, s"${w.syntax}.afterWrite")
val bodyPatch = transformer(doc)(body)
if (bodyPatch.nonEmpty) {
(basePatch + bodyPatch).atomic
} else {
basePatch
}
}
case Term.Apply.After_4_6_0(aw @ AfterWrite(w),
Term.ArgClause(List(Term.Block(
List(Term.Function.After_4_6_0(
Term.ParamClause(List(p @ Term.Param(_, _, _, _)), _),
body)))), _)) if (
w.symbol.info.exists(
_.toString.indexOf("BSONDocumentWriter") == -1)) => {
val basePatch = (Patch.replaceTree(
aw, s"${w.syntax}.afterWrite") + Patch.replaceTree(
p, s"case ${p.syntax}"))
val bodyPatch = transformer(doc)(body)
if (bodyPatch.nonEmpty) {
(basePatch + bodyPatch).atomic
} else {
basePatch.atomic
}
}
// ---
case t @ Term.Select(v, Term.Name("as")) if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSON")) =>
Patch.replaceTree(t, s"${v.syntax}.asOpt")
// ---
case t @ Term.Select(r, Term.Name("read")) if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSON")) =>
Patch.replaceTree(t, s"${r.syntax}.readTry")
case t @ Term.Select(r, Term.Name("write")) if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSON")) =>
Patch.replaceTree(t, s"${r.syntax}.writeTry")
// ---
case Term.Apply.After_4_6_0(n @ (Term.Name("BSONHandler") | Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("bson")),
Term.Name("BSONHandler"))), Term.ArgClause(List(r, w), _)) => {
val safe = r.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSONReader#read")
val factory: String = n match {
case Term.Name("BSONHandler") => "BSONHandler"
case _ => "reactivemongo.api.bson.BSONHandler"
}
if (safe) {
(transformer(doc)(r) + transformer(doc)(w) + Patch.replaceTree(
n, s"${factory}.from")).atomic
} else {
Patch.replaceTree(n, s"${factory}")
}
}
case Term.Apply.After_4_6_0(
t @ Term.ApplyType.After_4_6_0(
n @ (Term.Name("BSONHandler") | Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("bson")),
Term.Name("BSONHandler"))), Type.ArgClause(List(_, tpe))),
Term.ArgClause(List(r, w), _)) => {
val safe = r.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSONReader#read")
val factory: String = n match {
case Term.Name("BSONHandler") => "BSONHandler"
case _ => "reactivemongo.api.bson.BSONHandler"
}
if (safe) {
(transformer(doc)(r) + transformer(doc)(w) + Patch.replaceTree(
t, s"${factory}.from[${tpe}]")).atomic
} else {
Patch.replaceTree(t, s"${factory}[${tpe}]")
}
}
case Term.Apply.After_4_6_0(
t @ Term.ApplyType.After_4_6_0(n @ (Term.Name(
"BSONDocumentHandler") | Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("bson")),
Term.Name("BSONDocumentHandler"))),
Type.ArgClause(List(tpe))), Term.ArgClause(List(r, w), _)) => {
val safe = r.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSONReader#read")
val factory: String = n match {
case Term.Name("BSONDocumentHandler") => "BSONDocumentHandler"
case _ => "reactivemongo.api.bson.BSONDocumentHandler"
}
if (safe) {
(transformer(doc)(r) + transformer(doc)(w) + Patch.replaceTree(
t, s"${factory}.from[${tpe}]")).atomic
} else {
Patch.replaceTree(t, s"${factory}[${tpe}]")
}
}
case Term.Apply.After_4_6_0(n @ (Term.Name(
"BSONDocumentHandler") | Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("bson")),
Term.Name("BSONDocumentHandler"))),
Term.ArgClause(List(r, w), _)) => {
val safe = r.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSONReader#read")
val factory: String = n match {
case Term.Name("BSONDocumentHandler") => "BSONDocumentHandler"
case _ => "reactivemongo.api.bson.BSONDocumentHandler"
}
if (safe) {
(transformer(doc)(r) + transformer(doc)(w) + Patch.replaceTree(
n, s"${factory}.from")).atomic
} else {
Patch.replaceTree(n, factory)
}
}
// ---
case t @ Term.ApplyType.After_4_6_0(
Term.Name("BSONReader"), Type.ArgClause(List(_, tpe))) =>
Patch.replaceTree(t, s"BSONReader[${tpe.syntax}]")
case t @ Term.ApplyType.After_4_6_0(Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("bson")),
Term.Name("BSONReader")), Type.ArgClause(List(_, tpe))) =>
Patch.replaceTree(
t, s"reactivemongo.api.bson.BSONReader[${tpe.syntax}]")
// ---
case t @ Term.ApplyType.After_4_6_0(
Term.Name("BSONWriter"), Type.ArgClause(List(tpe, _))) =>
Patch.replaceTree(t, s"BSONWriter[${tpe.syntax}]")
case t @ Term.ApplyType.After_4_6_0(Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("bson")),
Term.Name("BSONWriter")), Type.ArgClause(List(tpe, _))) =>
Patch.replaceTree(
t, s"reactivemongo.api.bson.BSONWriter[${tpe.syntax}]")
// ---
case t @ Type.Apply.After_4_6_0(Type.Select(Term.Select(
Term.Name("reactivemongo"), Term.Name("bson")),
n @ Type.Name("BSONHandler" | "BSONReader")),
Type.ArgClause(List(_, tpe))) =>
Patch.replaceTree(t, s"reactivemongo.api.bson.$n[${tpe.syntax}]")
case t @ Type.Apply.After_4_6_0(Type.Name(
n @ ("BSONHandler" | "BSONReader")),
Type.ArgClause(List(_, tpe))) if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSON")) =>
Patch.replaceTree(t, s"${n}[${tpe.syntax}]")
// ---
case t @ Type.Apply.After_4_6_0(Type.Select(Term.Select(
Term.Name("reactivemongo"), Term.Name("bson")),
Type.Name(n @ "BSONWriter")), Type.ArgClause(List(tpe, _))) =>
Patch.replaceTree(t, s"reactivemongo.api.bson.${n}[${tpe.syntax}]")
case t @ Type.Apply.After_4_6_0(n @ Type.Name("BSONWriter"),
Type.ArgClause(List(tpe, _))) if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSON")) =>
Patch.replaceTree(t, s"${n}[${tpe.syntax}]")
// ---
case t @ Term.Select(Term.Name("DefaultBSONHandlers"), _) if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/DefaultBSONHandlers")) =>
migrationRequired(t, "DefaultBSONHandlers is no longer public; Rather use implicit resolution.")
case t @ Term.Name("DefaultBSONHandlers") if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/DefaultBSONHandlers")) =>
migrationRequired(t, "DefaultBSONHandlers is no longer public; Rather use implicit resolution.")
// ---
case t @ Type.Select(Term.Select(
Term.Name("reactivemongo"), Term.Name("bson")), Type.Name(n)
) if (!Seq("BSONReader", "BSONHandler", "BSONWriter").contains(n)) =>
Patch.replaceTree(t, s"reactivemongo.api.bson.$n")
case t @ Term.Select(
Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("api")),
Term.Name("collections")
), Term.Name("bson")) =>
Patch.replaceTree(t, s"reactivemongo.api.bson.collection")
case Term.Apply.After_4_6_0(Term.Select(
Term.Apply.After_4_6_0(
Term.ApplyType.After_4_6_0(
Term.Select(d, getAs @ Term.Name("getAs")),
Type.ArgClause(
List(Type.Name("BSONNumberLike" | "BSONBooleanLike")))
),
Term.ArgClause(List(_), _)
),
map @ Term.Name("map")
),
Term.ArgClause(List(body), _)
) if (d.symbol.info.exists { i =>
val t = i.toString
val s = i.signature.toString
s == "BSONDocument" || s == "BSONArray" ||
t.startsWith("reactivemongo/bson/BSONDocument") ||
t.startsWith("reactivemongo/bson/BSONArray")
}) => {
val bd = body match {
case Term.AnonymousFunction(
Term.Select(_: Term.Placeholder, expr)) =>
Patch.addRight(expr, ".toOption")
case Term.Block(List(Term.Function.After_4_6_0(
Term.ParamClause(List(_), _), b))) =>
Patch.addRight(b, ".toOption")
case _ =>
Patch.empty
}
val pd = transformer(doc)(d)
(pd + Patch.replaceTree(getAs, "getAsOpt") +
Patch.replaceTree(map, "flatMap") + bd).atomic
}
case Term.ApplyType.After_4_6_0(Term.Select(
d, getAs @ Term.Name("getAs")), Type.ArgClause(List(t))) if (
t != "BSONNumberLike" && t != "BSONBooleanLike" &&
getAs.symbol.info.exists { i =>
val s = i.toString
s.startsWith("reactivemongo/bson/BSONDocument") || s.
startsWith("reactivemongo/bson/BSONArray")
}) => {
val pd = transformer(doc)(d)
if (pd.nonEmpty) {
(Patch.replaceTree(getAs, "getAsOpt") + pd).atomic
} else {
Patch.replaceTree(getAs, "getAsOpt")
}
}
case u @ Term.Apply.After_4_6_0(
Term.Select(x, Term.Name("getUnflattenedTry")),
Term.ArgClause(List(f), _)
) if (x.symbol.info.exists(_.signature.toString == "BSONDocument")) =>
Patch.replaceTree(u, s"${x.syntax}.getAsUnflattenedTry[reactivemongo.api.bson.BSONValue]($f)")
case t @ Term.ApplyInfix.After_4_6_0(
d, Term.Name(":~"), Type.ArgClause(Nil),
arg @ Term.ArgClause(List(_), _)) if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSONDocument")) =>
Patch.replaceTree(t, s"(${d.syntax} ++ ${arg.syntax})")
case t @ Term.ApplyInfix.After_4_6_0(
arg, Term.Name("~:"), Type.ArgClause(Nil),
Term.ArgClause(List(d @ Term.Name(_)), _)) if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSONDocument")) =>
Patch.replaceTree(t, s"(BSONDocument(${arg.syntax}) ++ ${d.syntax})")
case t @ Term.ApplyInfix.After_4_6_0(
arg, Term.Name("~:"), Type.ArgClause(Nil),
d @ Term.ArgClause(List(_), _)) if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/bson/BSONDocument")) =>
Patch.replaceTree(t, s"(BSONDocument(${arg.syntax}) ++ ${d.syntax})")
})
}
private def gridfsUpgrade(implicit doc: SemanticDocument) = Fix(
`import` = {
case Importer(
Term.Select(
Term.Select(Term.Name("reactivemongo"), Term.Name("api")),
Term.Name("gridfs")
), importees) => {
val patches = Seq.newBuilder[Patch]
importees.foreach {
case i @ Importee.Name(Name.Indeterminate(
"DefaultFileToSave" | "DefaultReadFile" |
"ComputedMetadata" | "BasicMetadata" | "CustomMetadata")) =>
patches += Patch.removeImportee(i)
case _ =>
}
Patch.fromIterable(patches.result())
}
case Importer(
Term.Select(
Term.Select(Term.Name("play"), Term.Name("modules")),
Term.Name("reactivemongo")
), importees) => {
val patches = Seq.newBuilder[Patch]
importees.foreach {
case i @ Importee.Name(Name.Indeterminate("JSONFileToSave")) =>
patches += Patch.removeImportee(i)
case _ =>
}
Patch.fromIterable(patches.result())
}
},
refactor = {
// Extractors
object GridFSTermName {
def unapply(t: Term): Boolean = t match {
case g @ Term.Name("GridFS") =>
g.symbol.owner.toString == "reactivemongo/api/gridfs/"
case _ =>
false
}
}
// ---
object ReadFileTypeLike {
def unapply(t: Type): Option[String] = t match {
case Type.Name("BasicMetadata" |
"ComputedMetadata" | "CustomMetadata" | "ReadFile") if (
t.symbol.owner.toString == "reactivemongo/api/gridfs/") =>
Some("ReadFile")
case Type.Select(Term.Select(Term.Select(Term.Name(
"reactivemongo"), Term.Name("api")),
Term.Name("gridfs")), tn @ Type.Name(_)) =>
unapply(tn).map { n =>
s"reactivemongo.api.gridfs.${n}"
}
case _ =>
None
}
}
{
case t @ Term.Apply.After_4_6_0(
call, Term.ArgClause(List(a, b, _, c), _)) if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/gridfs/GridFS#find")) => {
val basePatch = Patch.replaceTree(
t, s"(${a.syntax}, ${b.syntax}, ${c.syntax})")
val callPatch = transformer(doc)(call)
if (callPatch.nonEmpty) {
(callPatch + basePatch).atomic
} else {
basePatch
}
}
case t @ Term.Apply.After_4_6_0(Term.ApplyType.After_4_6_0(Term.Select(
fs, Term.Name("find")), Type.ArgClause(List(_, _))),
Term.ArgClause(List(id), _)) if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/gridfs/GridFS#find")) =>
Patch.replaceTree(t, s"${fs.syntax}.find[reactivemongo.api.bson.BSONDocument, reactivemongo.api.bson.BSONValue](${id.syntax})")
// ---
case Term.Select(t @ Term.Select(
gfs, Term.Name("files")), Term.Name("update")) if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/gridfs/GridFS#files")) =>
Patch.replaceTree(t, gfs.syntax)
// ---
case gridfsRes @ Term.Apply.After_4_6_0(
Term.ApplyType.After_4_6_0(GridFSTermName(), Type.ArgClause(List(_))),
Term.ArgClause(List(Term.Name(db)), _)) =>
Patch.replaceTree(gridfsRes, s"${db}.gridfs")
case gridfsRes @ Term.Apply.After_4_6_0(
Term.ApplyType.After_4_6_0(
GridFSTermName(),
Type.ArgClause(
List(Type.Singleton(Term.Name("BSONSerializationPack"))))
),
Term.ArgClause(List(Term.Name(db), prefix), _)
) =>
Patch.replaceTree(gridfsRes, s"${db}.gridfs($prefix)")
// ---
case readFileTpe @ Type.Apply.After_4_6_0(ReadFileTypeLike(rf),
Type.ArgClause(List(_, idTpe))) => {
val it: String = {
if (idTpe.symbol.info.exists(
_.toString startsWith "play/api/libs/json/JsString")) {
"reactivemongo.api.bson.BSONString"
} else {
idTpe.syntax
}
}
Patch.replaceTree(
readFileTpe,
s"${rf}[${it}, reactivemongo.api.bson.BSONDocument]")
}
case save @ Term.Apply.After_4_6_0(
Term.Apply.After_4_6_0(
Term.Select(g, Term.Name("save" | "saveWithMD5")),
Term.ArgClause(
List(Term.Name(_), Term.Name(file), Term.Name(chunkSize)), _)
),
Term.ArgClause(
List(Term.Name(_), Term.Name(ec), Term.Name(_), Term.Name(_)), _)
) if (save.symbol.owner.
toString == "reactivemongo/api/gridfs/GridFS#") =>
Patch.addRight(save, s" // Consider: ${g}.writeFromInputStream(${file}, _streamNotEnumerator, $chunkSize)($ec)")
case iteratee @ Term.Apply.After_4_6_0(
Term.Apply.After_4_6_0(
Term.Select(g, Term.Name("iteratee" | "iterateeWithMD5")),
Term.ArgClause(List(Term.Name(_), Term.Name(_)), _)
),
Term.ArgClause(
List(Term.Name(_), Term.Name(_), Term.Name(_), Term.Name(_)), _)
) if (iteratee.symbol.owner.
toString == "reactivemongo/api/gridfs/GridFS#") =>
Patch.addRight(iteratee, s" // Consider: ${g}.readToOutputStream or GridFS support in streaming modules")
case gridfsRm @ Term.Apply.After_4_6_0(
Term.Select(Term.Name(gt), Term.Name("remove")),
Term.ArgClause(List(Term.Name(ref)), _)
) if (gridfsRm.symbol.owner.
toString == "reactivemongo/api/gridfs/GridFS#") =>
Patch.replaceTree(gridfsRm, s"${gt}.remove(${ref}.id)")
case t @ Type.Name("DefaultFileToSave") if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/gridfs/")) =>
Patch.replaceTree(t, "Unit /* Consider: reactivemongo.api.gridfs.GridFS.fileToSave */")
case t @ Type.Apply.After_4_6_0(Type.Name(
"BasicMetadata" | "CustomMetadata"), _) if (t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/gridfs/")) =>
Patch.replaceTree(t, "Unit /* Consider: reactivemongo.api.gridfs.ReadFile */")
case t @ Type.Name("DefaultReadFile" | "ComputedMetadata") if (
t.symbol.info.exists(
_.toString startsWith "reactivemongo/api/gridfs/")) =>
Patch.replaceTree(t, "Unit /* Consider: reactivemongo.api.gridfs.ReadFile */")
case t @ Type.Name("JSONFileToSave") if (t.symbol.info.exists(
_.toString startsWith "play/modules/reactivemongo/")) =>
Patch.replaceTree(t, "Unit /* Consider: reactivemongo.api.gridfs.ReadFile */")
}
})
private def testSym(t: Tree)(f: String => Boolean)(
implicit
doc: SemanticDocument): Boolean =
t.symbol.info.exists { i => f(i.toString) }
@inline private def migrationRequired(t: Tree, msg: String): Patch = Patch.replaceTree(t, s"""reactivemongo.api.bson.migrationRequired("${msg}") /* ${t.syntax} */""")
// ---
private case class Fix(
refactor: PartialFunction[Tree, PatchDirective],
`import`: PartialFunction[Importer, Patch] = PartialFunction.empty[Importer, Patch])
private case class PatchDirective(
patch: Patch,
recurse: Boolean)
private object PatchDirective {
import scala.language.implicitConversions
implicit def default(p: Patch): PatchDirective = PatchDirective(p, true)
implicit def full(spec: (Patch, Boolean)): PatchDirective =
PatchDirective(spec._1, spec._2)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy