I was wondering if scala had an equivalent to java's #SuppressWarnings that can be applied to a function or whatever to ignore any deprecation warnings[1] that function emits?
1: Relevant warning in my case is: method stop in class Thread is deprecated: see corresponding Javadoc for more information. I am aware of the problems with stop however there are still some cases where due to legacy code we have to use it.
No, and an enhancement request [1] for such a feature was closed as wontfix.
I agree it would be useful. I expect that the Scala core team aren't against the idea, but they have finite resources and many higher priorities.
update: this feature was eventually implemented in scala 2.13.2 release on 2020-04-22, see this answer
[1] https://issues.scala-lang.org/browse/SI-1781
EDIT: You should use #nowarn
There is a simple compiler plugin for this: silencer (a bit shameless plug)
Scala 2.13.2 provides #nowarn annotation developed on the basis of ghik's silencer, for example
import scala.annotation.nowarn
def t = { 0: #nowarn; 1 }
raises no warnings, whilst
def t = { 0; 1 }
gives
warning: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses
def t = { 0; 1 }
^
Here is how to suppress all warnings in sbt:
import sbt._
import Keys._
import KeyRanks.DTask
import xsbti.{Reporter, Problem, Position, Severity}
private lazy val compilerReporter = TaskKey[xsbti.Reporter](
"compilerReporter",
"Experimental hook to listen (or send) compilation failure messages.",
DTask
)
val ignoreWarnings = Seq(
compilerReporter in (Compile, compile) :=
new xsbti.Reporter {
private val buffer = collection.mutable.ArrayBuffer.empty[Problem]
def reset(): Unit = buffer.clear()
def hasErrors: Boolean = buffer.exists(_.severity == Severity.Error)
def hasWarnings: Boolean = buffer.exists(_.severity == Severity.Warn)
def printSummary(): Unit = {
print("\033c")
if (problems.nonEmpty) {
problems.foreach{ p =>
println("=====================================================")
println(p.position)
println(p.message)
println()
println()
}
}
}
def problems: Array[Problem] = buffer.toArray
def log(problem: Problem): Unit = {
if (problem.severity == Severity.Error) {
buffer.append(problem)
}
}
def log(pos: Position, msg: String, sev: Severity): Unit = {
log(new Problem {
def category: String = "foo"
def severity: Severity = sev
def message: String = msg
def position: Position = pos
})
}
def comment(pos: xsbti.Position, msg: String): Unit = ()
}
)
Related
I'm trying to get started with writing a compiler plugin for scala 3. At this stage, it's primarily based on https://github.com/liufengyun/scala3-plugin-example/blob/master/plugin/src/main/scala/Phases.scala (and the accompanying youtube video explaining how it works).
It's been an interesting process so far, and I'm getting a bit of a feel for some aspects of the compiler.
As a first step, I'm simply trying to wrap a method body into a block, print whatever the returned object was going to be, and then return the object.
This differs from the original plugin mainly in that there was a single side-effecting method call added to each method - this is also assigning a local variable, (which I think is probably the cause of the problem), and moving the method body into a block.
I've produced as minimal of a working example as I could in a fork here: https://github.com/robmwalsh/scala3-plugin-example
The plugin compiles fine, seems to run as part of compilation as expected, and then blows up at runtime. I'm not entirely sure if this is me doing something wrong (not unlikely) or a bug in the compiler (less likely, but a distinct possibility!).
Can anybody please shed some light on why this isn't working? I don't know what flags should be set when creating a new Symbol, so that's one possibility, but there's heaps of stuff that sorta seemed to work so I rolled with it.
Here's where I'm at (the interesting bits):
...
override def prepareForUnit(tree: Tree)(using ctx: Context): Context =
//find the printLn method
val predef = requiredModule("scala.Predef")
printlnSym = predef.requiredMethod("println", List(defn.AnyType))
ctx
override def transformDefDef(tree: DefDef)(using ctx: Context): Tree =
val sym = tree.symbol
// ignore abstract and synthetic methods
if tree.rhs.isEmpty|| sym.isOneOf(Synthetic | Deferred | Private | Accessor)
then return tree
try {
println("\n\n\n\n")
println("========================== tree ==========================")
println(tree.show)
// val body = {tree.rhs}
val body = ValDef(
newSymbol(
tree.symbol, termName("body"), tree.symbol.flags, tree.rhs.tpe),
Block(Nil, tree.rhs)
)
// println(body)
val bodyRef = ref(body.symbol)
val printRes = ref(printlnSym).appliedTo(bodyRef)
// shove it all together in a block
val rhs1 = tpd.Block(body :: printRes :: Nil, bodyRef)
//replace RHS with new
val newDefDef = cpy.DefDef(tree)(rhs = rhs1)
println("====================== transformed ======================")
println(newDefDef.show)
newDefDef
} catch {
case e =>
println("====================== error ===========================")
println(e)
println(e.printStackTrace)
tree
}
...
test program for compiler plugin
object Test extends App:
def foo: String = "forty two"
def bar(x: String): Int = x.length
def baz(x: String, y: Int): String = x + y
baz(foo, bar(foo))
output during compile using plugin (exactly what I wanted! I got very excited at this point)
========================== tree ==========================
def foo: String = "forty two"
====================== transformed ======================
def foo: String =
{
val body: ("forty two" : String) =
{
"forty two"
}
println(body)
body
}
========================== tree ==========================
def bar(x: String): Int = x.length()
====================== transformed ======================
def bar(x: String): Int =
{
val body: Int =
{
x.length()
}
println(body)
body
}
========================== tree ==========================
def baz(x: String, y: Int): String = x.+(y)
====================== transformed ======================
def baz(x: String, y: Int): String =
{
val body: String =
{
x.+(y)
}
println(body)
body
}
output during runtime :'( (this changes depending on the code it's running on, but always the same theme)
Exception in thread "main" java.lang.VerifyError: Bad local variable type
Exception Details:
Location:
testing/Test$.body$2()I #0: aload_1
Reason:
Type top (current frame, locals[1]) is not assignable to reference type
Current Frame:
bci: #0
flags: { }
locals: { 'testing/Test$' }
stack: { }
Bytecode:
0000000: 2bb6 007d ac
at testing.Test.main(Example.scala)
Edit: I'm using scala 3.1.2
I was using the existing flags when creating my new symbol. Instead, I needed to use the Local flag, which I suppose makes sense.
I'm creating a DSL where users have a given when then, where they pass a string with the action they want.
All those actions are controlled using some regular expressions.
Now using shapeless or macros I would like to control what users compones using the DSL, and in case the string does not fit the string as we expect make the code don't compile.
Si for instance
Given("a good action") -> fine
When("wrong string action") -> compilation error
Can anybody please point me to a good blog or documentation of how to achieve this?
Regards
After the solution of Gabriele now I´m facing a design issue. This is my vaidator class
object DSLValidator {
import scala.language.experimental.macros
def Given(message: String): Any = macro checkActionImpl
def When(message: String): Any = macro checkActionImpl
def Then(message: String): Any = macro checkActionImpl
def And(message: String): Any = macro checkActionImpl
def checkActionImpl(c: blackbox.Context)(message: c.Tree): c.Tree = {
import c.universe._
def isValidAction(s: String): Boolean = checkMessage(s)
message match {
case _tree#Literal(Constant(s: String)) if isValidAction(s) => _tree
case _ => c.abort(c.enclosingPosition, "Invalid action for DSL. Check the allowed actions in RegexActions")
}
}
val PARAMS = "(.*)=(.*)"
val PAYLOAD_VALUE_REGEX = s"^Payload $PARAMS".r
def checkMessage(action: String): Boolean = {
action match {
case PAYLOAD_VALUE_REGEX(c, c1) => true
case "Make a request to server" => true
case _ => false
}
}
}
And if I invoke the Given/When/Then from another class with wrong arguments the compiler complain as we expect.
#Test
def TestDSL(): Unit = {
DSLValidator.Given("Make a request to wrong")--> not compiling
DSLValidator.When("Payload code=works") --> compiling
println("End")
}
But now thinking the whole idea of was not only to check the values on compilation time but also execute the logic of the Given/When/then
And I realize than when I use macros in a function there´s nothing else that we can do so.
def Given(message: String): Any = {
macro checkActionImpl
//My bussiness logic
}
is not compiling, so my question is, where can I put now the business logic and where the invocation of the macro function to continue working.
Also using a static class as bridge does not work and wont compile since the string message does not fit.
object Macros {
def Given(message: String) = {
DSLValidator.Given(message)
//Logic here
println("Given")
}
}
Macros.Given("Make a request to wrong")
Any idea?
Regards
I think shapeless cannot help you in this case, so a macro would be the way to go.
Here's a skeleton for a macro you can start from:
import scala.language.experimental.macros
def Given(message: String): /* the return type of Given */ = macro givenImpl
def givenImpl(c: Context)(message: c.Tree): c.Tree = {
import c.universe._
def isValid(s: String): Boolean = ??? // your validation logic here
message match {
case case t # Literal(Constant(s: String)) if isValid(s) => t
case _ => c.abort(c.enclosingPosition, "Invalid message")
}
}
In order to fully understand this I can recommend you to read:
http://docs.scala-lang.org/overviews/macros/overview.html
https://blog.scalac.io/2016/02/25/def-hello-macro-world.html
To give you a minimal example:
object Main extends JSApp
{
val someThing: String = determineSomething("test")
def main(): Unit =
{
println(someThing)
}
}
Now, two possibilities here:
private def determineSomething(s: String): String = "succeeded"
If the project is executed like this, well, I get a log entry saying
succeeded
But when I use the more functional syntax:
private val determineSomething: (s: String) => "succeeded"
I get
TypeError: this.determineSomething$1 is null
I am curious as to the why this happens as in the (JVM) repl, both ways work perfectly fine.
I think what you want is something like this:
object Main extends JSApp {
private val determineSomething: String => String = (s: String) => "succeeded"
val someThing: String = determineSomething("test")
def main(): Unit = {
println(someThing)
}
}
The declaration of determineSomething needs to come before the declaration of something, otherwise the former will be uninitialized when the compiler attempts to initialize the latter.
I'm trying to use scala compiler Y warnings, but I don't think I'm doing it right. In the example below, nums is unused, hence I'd expect -Ywarn-value-discard to print a warning for that.
There are two if conditions, one nested inside the other. The child if's condition is exact opposite of parents', hence anything inside it is dead code. But -Ywarn-dead-code doesn't warn against that.
Could anyone please suggest what I might be doing wrong? Any pointers would be appreciated.
object DeadCodeWarn{
def main( args: Array[ String ] ) {
val nums = 1 to 100
//There should be a warning for squares
//as its a non-unit expression thats not
//used in any computation. Y-warn-value-discard
val squares = nums.map( x => x * x )
if( nums.length == 100 ){
println( "should be printed" )
if( nums.length !=100 )
{
//-Ywarn-dead-code
println( "Dead code, so should be warned against" )
}
}
}
}
$scalac -Xlint -Ywarn-all DeadCodeWarn.scala succeeds silently.
-Ywarn-value-discard
The value isn't discarded, it's assigned to the val!
The problem is that the val is unused
With code:
package foo
case class Foo(x: Int) {
def foo: Int = {
val y = 2;
x
}
}
I get with scalac 2.11.5 and -Ywarn-unused:
[warn] /.../foo.scala:5: local val in method foo is never used
[warn] val y = 2;
-Ywarn-dead-code
It's easy to write the counter-example(s) where the common logic doesn't work:
// Evil mutable class
class Arrayish {
private var x: Int = 100
def length(): Int = {
val res = x
x += 1
res
}
}
def test(nums: Arrayish): Unit =
if (nums.length == 100) {
println( "should be printed" )
if (nums.length != 100) {
println("Dead code, so should be warned against")
}
}
Dead code is run:
scala> test(new Arrayish())
should be printed
Dead code, so should be warned against
Or
class NonEqual {
def ==(x: Int): Boolean = true
def !=(x: Int): Boolean = true
}
class NaNLength {
val length: NonEqual = new NonEqual
}
def test2(nums: NaNLength): Unit =
if (nums.length == 100) {
println("should be printed")
if (nums.length != 100) {
println("Dead code, so should be warned against")
}
}
Dead code is run as well:
scala> test2(new NaNLength)
should be printed
Dead code, so should be warned against
Scalac compiler isn't smart enough to distinguish well behaving and not-so-well behaving cases.
If you are going to submit the bug / feature-request, mention example like:
def test3(nums: Array[Int]): Unit = {
if (true) {
println("should be printed")
if (false) {
println("Dead code, so should be warned against")
}
}
}
def test4(nums: Array[Int]): Unit = {
val hundred = nums.length == 100
if (hundred) {
println("should be printed")
if (!hundred) {
println("Dead code, so should be warned against")
}
}
}
Seems that scalac dead code reporter isn't as polished as e.g. Java. I hope scalac does optimise these examples properly though, it shouldn't be too complicated.
After some toying around with the code, it appears that this is simply not a case where dead code is caught. I will see about continuing to peruse the compiler code itself, however upon review it seems that dead code is eliminated. Upon elimination is when it seems to emit the warning. So, if you review something as simple as
def foo = {return 1; 1}
You will see that the final code is simply to return 1 and not even emit the second 1. However, if you take a look at the final code of this nested if you will see that the second if is not eliminated.
Go ahead and submit a bug, although I'm not sure what kind of priority this would have.
trying to run the sample code in the Apress book called "Beginning Scala". I even downloaded the code from their website to make sure I didn't goof. Getting the following message:
/root/sum.scala:19: error: missing arguments for method collect in trait Iterator;
follow this method with `_' if you want to treat it as a partially applied function
val lines = input.getLines.collect
^
one error found
and here is the source code i used (running Scala version 2.8.1.final (Java HotSpot(TM) Server VM, Java 1.6.0_22 on Fedora 13)
import scala.io._
def toInt(in: String): Option[Int] =
try {
Some(Integer.parseInt(in.trim))
} catch {
case e: NumberFormatException => None
}
def sum(in: Seq[String]) = {
val ints = in.flatMap(s => toInt(s))
ints.foldLeft(0)((a, b) => a + b)
}
println("Enter some numbers and press ctrl-D (Unix/Mac) ctrl-C (Windows)")
val input = Source.fromInputStream(System.in)
val lines = input.getLines.collect
println("Sum "+sum(lines))
looks like this is the relevant change:
The Iterator.collect() method in 2.7.7 returns a Seq. In 2.8, it is used to perform a conditional map using a PartialFunction. You can use input.getLines.toSeq instead.
Ah, I remember this:
EDIT: replaced with more in depth answer
The code was written against Scala
2.7.3 and 2.8 introduces some breaking changes.
Here's an update to the code that
works under Scala 2.8.0:
import scala.io._
object Sum {
def main(args: Array[String]): Unit = {
println("Enter some numbers and press ctrl-D (Unix/Mac) ctrl-Z (Windows)")
val input = Source.fromInputStream(System.in)
val lines = input.getLines.toList
println("Sum " + sum(lines))
}
def toInt(s: String): Option[Int] = {
try {
Some(Integer.parseInt(s))
} catch {
case e: NumberFormatException => None
}
}
def sum(in: Seq[String]): Int = {
val ints = in.flatMap(toInt(_))
ints.foldLeft(0)((a, b) => a + b)
}
}
Source: http://scala-programming-language.1934581.n4.nabble.com/Beginning-Scala-book-problem-td2966867.html