Skip to content

Commit

Permalink
Scala 3 OldTime
Browse files Browse the repository at this point in the history
  • Loading branch information
xuwei-k committed Mar 23, 2022
1 parent 2f1e9d2 commit 6e67928
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 13 deletions.
36 changes: 26 additions & 10 deletions core/src/main/scala-2/org/wartremover/contrib/warts/OldTime.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.wartremover
package contrib.warts

import scala.collection.mutable

/**
* Forbids use of
* - java.util.{ Date, Calendar, GregorianCalendar, TimeZone }
Expand All @@ -22,6 +24,8 @@ object OldTime extends WartTraverser {
"java.text.SimpleDateFormat"
)

private[this] final case class LineInFile(path: String, line: Int)

def apply(u: WartUniverse): u.Traverser = {
import u.universe._
import u.universe.Flag._
Expand All @@ -33,52 +37,64 @@ object OldTime extends WartTraverser {
def isJavaTimeImport(tree: Tree, selectors: List[ImportSelector]): Boolean =
selectors.map(tree.symbol.fullName + "." + _.name).exists(javaTime.contains)

val linesWithError = mutable.Set.empty[LineInFile]

def addError(pos: Position, message: String): Unit = {
error(u)(pos, message)
linesWithError.add(LineInFile(pos.source.path, pos.line))
}

def errorAlreadyExists(pos: Position): Boolean = {
linesWithError.contains(LineInFile(pos.source.path, pos.line))
}

new u.Traverser {
override def traverse(tree: Tree): Unit = tree match {
// Ignore trees marked by SuppressWarnings
case t if hasWartAnnotation(u)(t) =>
case _ if errorAlreadyExists(tree.pos) =>

// forbid use of any type from org.joda
case TypeTree() if isJodaTime(tree.symbol) =>
error(u)(tree.pos, jodaError)
addError(tree.pos, jodaError)

// forbid use of any type that's part of the old java time API
case TypeTree() if isJavaTime(tree.symbol) =>
error(u)(tree.pos, javaError)
addError(tree.pos, javaError)

case tt @ TypeTree() =>
tt.tpe match {
// forbid org.joda.time types in type bounds
case TypeBounds(a, b) if isJodaTime(a.typeSymbol) || isJodaTime(b.typeSymbol) =>
error(u)(tree.pos, jodaError)
addError(tree.pos, jodaError)
// forbid old java time API types in type bounds
case TypeBounds(a, b) if isJavaTime(a.typeSymbol) || isJavaTime(b.typeSymbol) =>
error(u)(tree.pos, javaError)
addError(tree.pos, javaError)
// forbid org.joda.time types as type arguments
case _ if tt.tpe.typeArgs.exists(t => isJodaTime(t.typeSymbol)) =>
error(u)(tree.pos, jodaError)
addError(tree.pos, jodaError)
// forbid old java time API types as type arguments
case _ if tt.tpe.typeArgs.exists(t => isJavaTime(t.typeSymbol)) =>
error(u)(tree.pos, javaError)
addError(tree.pos, javaError)
case _ =>
super.traverse(tree)
}

// forbid use of org.joda.time members
case Select(qual, name) if qual.toString.startsWith("org.joda.time") =>
error(u)(tree.pos, jodaError)
addError(tree.pos, jodaError)

// forbid use of old java time API members
case Select(qual, name) if javaTime.contains(qual.toString + "." + name) =>
error(u)(tree.pos, javaError)
addError(tree.pos, javaError)

// forbid org.joda.time types in type applications
case TypeApply(fun, args) if args.exists(t => isJodaTime(t.symbol)) =>
error(u)(tree.pos, jodaError)
addError(tree.pos, jodaError)

// forbid old java time API types in type applications
case TypeApply(fun, args) if args.exists(t => isJavaTime(t.symbol)) =>
error(u)(tree.pos, javaError)
addError(tree.pos, javaError)

case _ =>
super.traverse(tree)
Expand Down
54 changes: 54 additions & 0 deletions core/src/main/scala-3/org/wartremover/contrib/warts/OldTime.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.wartremover.contrib.warts

import org.wartremover.WartTraverser
import org.wartremover.WartUniverse
import scala.collection.mutable

object OldTime extends WartTraverser {
private[this] val oldJavaTime: Set[String] = Set(
"java.util.Date",
"java.util.Calendar",
"java.util.GregorianCalendar",
"java.util.TimeZone",
"java.text.DateFormat",
"java.text.SimpleDateFormat"
)

private[this] final case class LineInFile(content: Option[String], startLine: Int)

private[this] def oldJavaMessage = "The old Java time API is disabled. Use Java 8 java.time._ API instead."
private[this] def jodaMessage = "JodaTime is disabled. Use Java 8 java.time._ API instead."

def apply(u: WartUniverse): u.Traverser = {
val linesWithError = mutable.Set.empty[LineInFile]
new u.Traverser(this) {
import q.reflect.*

def addError(pos: Position, message: String): Unit = {
error(pos, message)
linesWithError.add(LineInFile(pos.sourceFile.content, pos.startLine))
}

def errorAlreadyExists(pos: Position): Boolean = {
linesWithError.contains(LineInFile(pos.sourceFile.content, pos.startLine))
}

override def traverseTree(tree: Tree)(owner: Symbol): Unit = {
tree match {
case _ if hasWartAnnotation(tree) =>
case _ if errorAlreadyExists(tree.pos) =>
case t: TypeTree if t.symbol.fullName.startsWith("org.joda.time") =>
addError(t.pos, jodaMessage)
case t: TypeTree if oldJavaTime(t.symbol.fullName) =>
addError(t.pos, oldJavaMessage)
case t: New if t.tpt.symbol.fullName.startsWith("org.joda.time") =>
addError(t.pos, jodaMessage)
case t: New if oldJavaTime(t.tpt.symbol.fullName) =>
addError(t.pos, oldJavaMessage)
case _ =>
super.traverseTree(tree)(owner)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class OldTimeTest extends AnyFunSuite with ResultAssertions {
import java.util._
val x = new Date()
}
assertErrors(result)(javaError, 2)
assertError(result)(javaError)
}
test("disable creating instances of java.util.Date (1)") {
val result = WartTestTraverser(OldTime) {
Expand Down Expand Up @@ -115,7 +115,7 @@ class OldTimeTest extends AnyFunSuite with ResultAssertions {
val result = WartTestTraverser(OldTime) {
val x = List.empty[java.util.Date]
}
assertErrors(result)(javaError, 2)
assertError(result)(javaError)
}
test("disable using org.joda.time.LocalDate as a type parameter (1)") {
val result = WartTestTraverser(OldTime) {
Expand All @@ -127,6 +127,6 @@ class OldTimeTest extends AnyFunSuite with ResultAssertions {
val result = WartTestTraverser(OldTime) {
val x = List.empty[org.joda.time.LocalDate]
}
assertErrors(result)(jodaError, 2)
assertError(result)(jodaError)
}
}

0 comments on commit 6e67928

Please sign in to comment.