typer
to see what's going on.
Nothing
and complains.Nothing
node as an inferred type, so wartremover assumes it was provided by the user, and does not raise any warnings.
@nigredo-tori oh right. Cool. Yeah, I'm trying to avoid giving the compiler the full definition of Right and Left. If I do I can give it the correct types from the start as you mentioned. I don't have to because the compiler infers the correct types to return from withTypes
which is how the callers of this method see it and nothing within the withTypes
method uses the incorrectly inferred types. Not sure if that makes sense? So I guess what I'm saying is that even if the compiler infers Nothing for a Left or Right, if that expression has an explicit Either type defined (either via a type ascription or return type) that doesn't have Nothing in it, then wartremover can relax because the expected type will be chosen.
example:
val l: Either[String, Int] = Left("error")
//I don't expect WR to complain because the type ascription sets the correct type for all intents and purposes for the use of l.
@ssanj, here, again, the typer infers Nothing
...
if that expression has an explicit Either type defined (either via a type ascription or return type) that doesn't have Nothing in it, then wartremover can relax because the expected type will be chosen.
It would be nice, but that means the wart would have to analyze the parent nodes, and to somehow guess that the types are explicit enough (whatever this means), which sounds kind of arbitrary and needlessly complicated (read "fragile"). It gets more difficult the more possibilities we consider, and before long we're basically reimplementing the typer...
It seems like the only sane way to disable the warning for such cases is to disable it for all or some contravariant positions, which arguably defeats the point.
As an aside, you might want to challenge yourself by using the combinators that don't need covariance. E.g. with the mouse
library (complementing cats
), your initial example turns to this:
def withTypes(value: Int): Either[String, Int] =
(value > 10).either(s"less than 10: $value", value)
which does not include any type arguments that can be Nothing
.
[error] Modules were resolved with conflicting cross-version suffixes in {file:/Users/andrew/tmp/wartremover/}sbt-plugin:
[error] org.scala-lang.modules:scala-xml _2.13.0-M3, _2.12
java.lang.RuntimeException: Conflicting cross-version suffixes in: org.scala-lang.modules:scala-xml
at scala.sys.package$.error(package.scala:27)
at sbt.ConflictWarning$.processCrossVersioned(ConflictWarning.scala:46)
at sbt.ConflictWarning$.apply(ConflictWarning.scala:32)
lazy val wartremoverSettings =
Seq(
wartremoverErrors in (Compile, compile) ++= Warts.unsafe,
wartremoverExcluded += sourceManaged.value
)
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthProvider.scala:81:11: [wartremover:Var] var is disabled
[error] var _done__ = false
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:118:19: [wartremover:Any] Inferred type containing Any
[error] __fieldsMap.getOrElse(__fields.get(0), "").asInstanceOf[_root_.scala.Predef.String],
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:119:19: [wartremover:Any] Inferred type containing Any
[error] __fieldsMap.getOrElse(__fields.get(1), "").asInstanceOf[_root_.scala.Predef.String]
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:118:50: [wartremover:AsInstanceOf] asInstanceOf is disabled
[error] __fieldsMap.getOrElse(__fields.get(0), "").asInstanceOf[_root_.scala.Predef.String],
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:119:50: [wartremover:AsInstanceOf] asInstanceOf is disabled
[error] __fieldsMap.getOrElse(__fields.get(1), "").asInstanceOf[_root_.scala.Predef.String]
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:34:5: [wartremover:DefaultArguments] Function has default arguments
[error] providerId: _root_.scala.Predef.String = "",
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:71:24: [wartremover:NonUnitStatements] Statements must return Unit
[error] while (!_done__) {
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:93:35: [wartremover:Null] null is disabled
[error] if (__t != "") __t else null
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:97:35: [wartremover:Null] null is disabled
[error] if (__t != "") __t else null
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:126:62: [wartremover:OptionPartial] Option#get is disabled - use Option#fold instead
[error] __fieldsMap.get(scalaDescriptor.findFieldByNumber(1).get).map(_.as[_root_.scala.Predef.String]).getOrElse(""),
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:127:62: [wartremover:OptionPartial] Option#get is disabled - use Option#fold instead
[error] __fieldsMap.get(scalaDescriptor.findFieldByNumber(2).get).map(_.as[_root_.scala.Predef.String]).getOrElse("")
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:129:15: [wartremover:Throw] throw is disabled
[error] case _ => throw new RuntimeException("Expected PMessage")
[error] ^
[error] C:\cygwin64\home\r892107\sourcecode\topgun\target\scala-2.12\src_managed\main\com\google\api\AuthRequirement.scala:133:113: [wartremover:Throw] throw is disabled
[error] def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = throw new MatchError(__number)
[error]
To exclude a file or directory from all checks, use wartremoverExcluded in your build.sbt file:
wartremoverExcluded += baseDirectory.value / "src" / "main" / "scala" / "SomeFile.scala"
wartremoverExcluded += sourceManaged.value
for {
...
(a,b) = foo
} yield ()
var
inside traits desugar into private fields with a setter method
StringPlusAny
StringPlusAny
, and that's not it