A selection of additional warts for wartremover managed by the community.
Add the following to your project/plugins.sbt:
addSbtPlugin("org.wartremover" % "sbt-wartremover-contrib" % "2.3.4")// In build.sbt
wartremoverErrors += ContribWart.OldTime // Or whichever warts you want to addTo use wartremover-contrib with other build managers, please refer to here.
Here is a list of warts under the org.wartremover.contrib.warts package.
apply slightly reduces amount of code, but makes code much less readable, and in conjunction with parenless methods can lead to bugs.
list.toSet(true)
A short name can make code more meaningful:
list.toSet.contains(true).
class C {
def apply(...) = ... // Won't compile: apply is disabled
}
object C {
def apply() = new C // Compiles: object's apply is enabled
}Tuples are described not by their semantic meaning, but by their types alone, which requires users of your API to either create that meaning themselves using unapply or to use the ugly _1, _2, ... accessors.
Public API should refrain from exposing tuples and should instead consider using custom case classes to add semantic meaning.
// Won't compile:
// | Avoid using tuples in public interfaces, as they only supply type information.
// | Consider using a custom case class to add semantic meaning.
def badFoo(customerTotal: (String, Long)) = {
// Code
}// Custom case class with added semantic meaning
final case class CustomerAccount(customerId: String, accountTotal: Long)
// Will compile
def goodFoo(customerTotal: CustomerAccount) = {
// Code
}Though override may be optional, it is safer to add it every time.
Consider the following code:
trait T {
def f1(): Unit
}
class C extends T {
def f1() = ...
def f2() = ...
}Renaming T.f1 to T.f2 leads to dead code and unexpected behavior in C.
It is advised to use this rule with UnsafeInheritance to avoid default implementation override:
trait T {
def f1(): Unit = ...
}Sometimes an additional power of Monad is not needed, and
Applicative is enough. This issues a warning in such cases
(not an error, since using a Monad instance might still be a conscious decision)
scala> for {
| x <- List(1,2,3)
| y <- List(2,3,4)
| } yield x * y
<console>:19: warning: No need for Monad here (Applicative should suffice).
> "If the extra power provided by Monad isn’t needed, it’s usually a good idea to use Applicative instead."
Typeclassopedia (https://wiki.haskell.org/Typeclassopedia)
Apart from a cleaner code, using Applicatives instead of Monads can in general case result in a more parallel code.
For more context, please refer to the aforementioned Typeclassopedia, http://comonad.com/reader/2012/abstracting-with-applicatives/, or http://www.serpentine.com/blog/2008/02/06/the-basics-of-applicative-functors-put-to-practical-work/
x <- List(1,2,3)
^
res0: List[Int] = List(2, 3, 4, 4, 6, 8, 6, 9, 12)
scala> for {
| x <- List(1,2,3)
| y <- x to 3
| } yield x * y
res1: List[Int] = List(1, 2, 3, 4, 6, 9)Forbids use of deprecated time APIs in favor of the Java 8 Time API.
Disabled types:
java.util.Datejava.util.Calendarjava.util.GregorianCalendarjava.util.TimeZonejava.text.DateFormatjava.text.SimpleDateFormatorg.joda.time._
Reports an error/warning when a sealed case class is seen. As per FinalCaseClass wart, case classes
should always be final. And, in Scala combining final and sealed together is not allowed.
Some.apply may break typing in two ways: First, when it is used with a null value, creating an instance of Some(null) instead of the (usually) expected None, and; Second, when it causes type inference to infer Some[T] instead of the (usually) expected Option[T]. Use Option.apply instead, to cover both cases.
def someOfNull(foo: String) = {
// Won't compile: Some.apply is disabled - use Option.apply instead
val expectedSafeFoo: Option[String] = Some(foo) // If foo == null, Some(null)
val actualSafeFoo: Option[String] = Option(foo) // If foo == null, None
}def typeInference() = {
// Won't compile: Some.apply is disabled - use Option.apply instead
val maybeFoo = Some("bar") // maybeFoo has type Some[String], not Option[String]...
// ...so the following code would not have compiled
maybeFoo match {
case Some(value) => // ...
case None => // ...
}
}Symbolic names don't affect program correctness directly, however this language feature makes it harder to reason about the code, and that leads to bugs.
As a general rule, symbolic names have two valid use-cases: domain-specific languages, logically mathematical operations. Otherwise they can be replaced by normal readable names.
A name is considered symbolic if the number of characters that aren't letters or underscore is greater than 2.
// Won't compile: Symbolic name is disabled
def :+:(): Unit = {}The mapValues and filterKeys methods of maps implicitly turn a strictly evaluated collection into a lazily evaluated one.
This has been the subject of many debates and will be fixed in the new collections library in Scala 2.13, but until then should be avoided.
You should instead consider using the explicit call to the view or toStream methods.
val map: Map[Int, Int] = ???
// Won't compile
val positivesLazyMap = map.filterKeys(_ > 0)
// Won't compile
val incrementedLazyMap = map.mapValues(_ + 1)Overriding method implementation can break parent's contract.
trait T {
// Won't compile: Method must be final or abstract
def positive = 1
}
class C extends T {
override def positive = -1
}The andThen method receives a side-effecting callback, whose return value are discarded.
This is confusing, because it is different from Function types' andThen which compose two instances of functions (e.g. f andThen g).
The flatMap method, which can chain the result to other Future, may be more appropriate than andThen.
val f: Future[Int] = fooAsync()
// Won't compile
f.andThen {
case i if i > 100 => Future.successful(0)
case _ => Future.succesful(42)
}
// Will compile
f.andThen {
case i if i > 100 => println("side-effect")
case _ => println("other side-effect")
}Reports an error when a case class is being inherited. The reason behind this is similar to
another wart FinalCaseClass, however CaseClassInheritance prohibits the code
where one class inherits another case class, no matter the case class is defined as final or not.
case class Car()
// Won't compile: Case class should not be inherited: Car
class RedCar() extends Car()