Built-in Warts
Here is a list of built-in warts under the
org.wartremover.warts package.
ArrayEquals
Unlike other collections == on arrays and iterators checks reference equality:
List(1) == List(1) //true
Array(1) == Array(1) //false, won't compile: == is disabled
Any
Any is the top type; it is the supertype of every other type. The
Scala compiler loves to infer Any as a generic type, but that is
almost always incorrect. Explicit type arguments should be used
instead.
// Won't compile: Inferred type containing Any
val any = List(1, true, "three")
AnyVal
See Any.
// Won't compile: Inferred type containing AnyVal
val xs = List(1, true)
AsInstanceOf
asInstanceOf is unsafe in isolation and violates parametricity when guarded by isInstanceOf. Refactor so that the desired type is proven statically.
// Won't compile: asInstanceOf is disabled
x.asInstanceOf[String]
DefaultArguments
Scala allows methods to have default arguments, which make it hard to use methods as functions.
// Won't compile: Function has default arguments
def x(y: Int = 0)
EitherProjectionPartial
scala.util.Either.LeftProjection and scala.util.Either.RightProjection
have a get method which will throw if the value doesn’t match the
projection.  The program should be refactored to use scala.util.Either.LeftProjection#toOption
and scala.util.Either.RightProjection#toOption to explicitly handle both
the Some and None cases.
Enumeration
Scala’s Enumeration can cause performance problems due to its reliance on reflection. Additionally, the lack of exhaustive match checks and partial methods can lead to runtime errors. Instead of Enumeration, a sealed abstract class extended by case objects should be used instead.
Equals
Scala’s Any type provides an == method which is not type-safe. Using this method allows obviously incorrect code like 5 == "5" to compile. A better version which forbids equality checks across types (which always fail) is easily defined:
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
implicit final class AnyOps[A](self: A) {
   def ===(other: A): Boolean = self == other
}
equals, eq, ne are disabled as well.
ExplicitImplicitTypes
Scala has trouble correctly resolving implicits when some of them lack explicit result types. To avoid this, all implicits should have explicit type ascriptions.
FinalCaseClass
Scala’s case classes provide a useful implementation of logicless data types. Extending a case class can break this functionality in surprising ways. This can be avoided by always making them final or sealed.
// Won't compile: case classes must be final
case class Foo()
FinalVal
Value of a final val is inlined and can cause inconsistency during incremental compilation (see sbt/sbt/issues/1543 ).
file 1:
object c {
  // Won't compile: final val is disabled
  final val v = 1
}
file 2:
println(c.v)
ImplicitConversion
Implicit conversions weaken type safety and always can be replaced by explicit conversions.
// Won't compile: implicit conversion is disabled
implicit def int2Array(i: Int) = Array.fill(i)("madness")
ImplicitParameter
Implicit parameters as configuration often lead to confusing interfaces and can result in surprising inconsistencies.
// Won't compile: Implicit parameters are disabled
def f()(implicit s: String) = ()
// Still compiles
def f[A: Ordering](a: A, other: A) = ...
def f(a: A, other: A)(implicit ordering: Ordering[A]) = ...
IsInstanceOf
isInstanceOf violates parametricity. Refactor so that the type is established statically.
// Won't compile: isInstanceOf is disabled
x.isInstanceOf[String]
JavaConversions
The standard library provides implicits conversions to and from Java types in scala.collection.JavaConversions. This can make code difficult to understand and read about. The explicit conversions provided by scala.collection.JavaConverters should be used instead.
// Won't compile: scala.collection.JavaConversions is disabled
import scala.collection.JavaConversions._
val scalaMap: Map[String, String] = Map()
val javaMap: java.util.Map[String, String] = scalaMap
JavaSerializable
java.io.Serializable is a common subtype to many structures, especially those
imported from Java.  For example, String is a subtype of java.io.Serializable
but not scala.Serializable.  The Scala compiler loves to infer
java.io.Serializable as a common supertype, but that is almost always
incorrect. Explicit type arguments should be used instead.
// Won't compile: Inferred type containing java.io.Serializable
object O extends Serializable
val mistake = List("foo", "bar", O /* forgot O.toString */)
LeakingSealed
Descendants of a sealed type must be final or sealed. Otherwise this type can be extended in another file through its descendant.
file 1:
// Won't compile: Descendants of a sealed type must be final or sealed
sealed trait t
class c extends t
file 2:
class d extends c
MutableDataStructures
The standard library provides mutable collections. Mutation breaks equational reasoning.
// Won't compile: scala.collection.mutable package is disabled
import scala.collection.mutable.ListBuffer
val mutList = ListBuffer()
NonUnitStatements
Scala allows statements to return any type. Statements should only
return Unit (this ensures that they’re really intended to be
statements).
// Won't compile: Statements must return Unit
10
false
When interfacing with APIs that violate this principle, one can define a function in the project
@specialized def discard[A](evaluateForSideEffectOnly: A): Unit = {
  val _: A = evaluateForSideEffectOnly
  () //Return unit to prevent warning due to discarding value
}
discard{ badMutateFun(foo) } // Use like this
Nothing
Nothing is a special bottom type; it is a subtype of every other
type. The Scala compiler loves to infer Nothing as a generic type but
that is almost always incorrect. Explicit type arguments should be
used instead.
// Won't compile: Inferred type containing Nothing
val nothing = ???
val nothingList = List.empty
Null
null is a special value that inhabits all reference types. It breaks
type safety.
// Won't compile: null is disabled
val s: String = null
var s2: String = _
Option2Iterable
Scala inserts an implicit conversion from Option to Iterable. This can hide bugs and creates surprising situations like Some(1) zip Some(2) returning an Iterable[(Int, Int)].
OptionPartial
scala.Option has a get method which will throw if the value is
None. The program should be refactored to use scala.Option#fold to
explicitly handle both the Some and None cases.
Overloading
Method overloading may lead to confusion and usually can be avoided.
// Won't compile: Overloading is disabled
class c {
  def equals(x: Int) = {}
}
Product
Product is a type common to many structures; it is the supertype of
case classes and tuples. The Scala compiler loves to infer Product as
a generic type, but that is almost always incorrect. Explicit type
arguments should be used instead.
// Won't compile: Inferred type containing Product
val any = List((1, 2, 3), (1, 2))
PublicInference
Type inference of public members can expose extra type information, that can break encapsulation.
class c {
  // Won't compile: Public member must have an explicit type ascription
  def f() = new c with t
  val name = "abc" // Compiles: fields initialized by string, char or boolean literals are ignored
}
class c2 extends c {
  override def f() = ... // Compiles: overridden members are ignored
}
Recursion
General recursion can result in non-termination. There are various techniques, like fixed-point combinators, that allow you to extract recursion from your code. Recursion can also cause problems with stack usage. This can often be fixed with a @tailrec annotation (which uses constant stack) or by using a trampoline (which moves stack usage to the heap).
// Won't compile: Potentially-diverging recursion.
def diverging(i: Int): Int = if (i == 0) 0 else diverging(i + 1)
This particular instance can be silenced by adding @tailrec before def, making the stack safety explicit.
Return
return breaks referential transparency. Refactor to terminate computations in a safe way.
// Won't compile: return is disabled
def foo(n:Int): Int = return n + 1
def foo(ns: List[Int]): Any = ns.map(n => return n + 1)
Serializable
Serializable is a type common to many structures. The Scala compiler
loves to infer Serializable as a generic type, but that is almost
always incorrect. Explicit type arguments should be used instead.
// Won't compile: Inferred type containing Serializable
val any = List((1, 2, 3), (1, 2))
StringPlusAny
Scala’s String interface provides a + method that converts the operand to a String via its toString method. As mentioned in the documentation for the ToString wart, this method is unreliable and brittle.
// Won't compile: Implicit conversion to string is disabled
"foo" + {}
{} + "bar"
Throw
throw implies partiality. Encode exceptions/errors as return
values instead using Either.
ToString
Scala creates a toString method automatically for all classes. Since toString is based on the class name, any rename can potentially introduce bugs. This is especially pernicious for case objects. toString should be explicitly overridden wherever used.
case object Foo { override val toString = "Foo" }
IterableOps
scala.collection.Iterable has:
- head,
- tail,
- init,
- last,
- reduce,
- reduceLeft,
- reduceRight,
- max,
- maxBy,
- minand
- minBymethods,
all of which will throw if the collection is empty. The program should be refactored to use:
- headOption,
- drop(1),
- dropRight(1),
- lastOption,
- reduceOptionor- fold,
- reduceLeftOptionor- foldLeftand
- reduceRightOptionor- foldRightrespectively,
to explicitly handle empty collections.
TripleQuestionMark
??? throws NotImplementedError. Encode exceptions/errors as return values instead using Either.
// Won't compile: ??? is disabled
def foo: Int = ???
TryPartial
scala.util.Try has a get method which will throw if the value is a
Failure. The program should be refactored to use scala.util.Try#map and scala.util.Try#getOrElse to
explicitly handle both the Success and Failure cases.
Unsafe
Checks for the following warts:
- Any
- AsInstanceOf
- DefaultArguments
- EitherProjectionPartial
- IsInstanceOf
- IterableOps
- NonUnitStatements
- Null
- OptionPartial
- Product
- Return
- Serializable
- StringPlusAny
- Throw
- TripleQuestionMark
- TryPartial
- Var
This list is built from Unsafe.scala
Var
Mutation breaks equational reasoning.
// Won't compile: var is disabled
var x = 100
While
while loop usually indicates low-level code. If performance is not an issue, it can be replaced.
// Won't compile: while is disabled
while(i < 10) {
  i += 1
  ...
}