Skip to content

Commit d63e5f1

Browse files
authored
feat: Support Suppression of warnings on Scala 3 (#946)
* feat: Support Suppression of all warnings * feat: Support suppressing specific inspections
1 parent b172c47 commit d63e5f1

File tree

3 files changed

+110
-2
lines changed

3 files changed

+110
-2
lines changed

src/main/scala-3/com/sksamuel/scapegoat/Inspection.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ abstract class Inspection(
1111
val explanation: String
1212
) extends InspectionBase {
1313

14-
val self: Inspection = this
14+
implicit val self: Inspection = this
1515

1616
def inspect(feedback: Feedback[SourcePosition], tree: tpd.Tree)(using Context): Unit
1717

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,54 @@
11
package com.sksamuel.scapegoat
22

3+
import dotty.tools.dotc.ast.Trees
4+
import dotty.tools.dotc.ast.tpd
35
import dotty.tools.dotc.ast.tpd.*
6+
import dotty.tools.dotc.core.Constants
47
import dotty.tools.dotc.core.Contexts.Context
8+
import dotty.tools.dotc.core.Names
9+
import dotty.tools.dotc.core.Symbols
10+
import dotty.tools.dotc.core.Symbols.requiredClass
511
import dotty.tools.dotc.util.NoSource
612

7-
abstract class InspectionTraverser extends TreeTraverser {
13+
abstract class InspectionTraverser(using inspection: Inspection) extends TreeTraverser {
14+
15+
override protected def traverseChildren(tree: tpd.Tree)(using Context): Unit = {
16+
if (!isSuppressed(tree)) {
17+
super.traverseChildren(tree)
18+
}
19+
}
20+
21+
private def isSuppressed(t: tpd.Tree)(using Context): Boolean = {
22+
val symbol = t.symbol
23+
val annotation = symbol.getAnnotation(requiredClass("java.lang.SuppressWarnings"))
24+
val arg = annotation.flatMap(_.argument(0)).map(extractArg)
25+
val inspectionName = inspection.getClass.getSimpleName
26+
arg match {
27+
case Some(
28+
Apply(Apply(TypeApply(Select(Ident(array), _), _), List(Typed(SeqLiteral(args, _), _))), _)
29+
) =>
30+
args.exists {
31+
case Literal(value) if value.tag == Constants.StringTag =>
32+
value.stringValue == "all" || value.stringValue == inspectionName
33+
case _ => false
34+
}
35+
case _ => false
36+
}
37+
}
38+
39+
// Scala 3.3 doesn't insert NamedArg, skip it while trying to match
40+
private def extractArg(t: tpd.Tree): tpd.Tree = t match {
41+
case NamedArg(_, tree) => tree
42+
case _ => t
43+
}
844

945
extension (tree: Tree)(using Context)
1046
def asSnippet: Option[String] = tree.source match
1147
case NoSource => None
1248
case _ => Some(tree.source.content().slice(tree.sourcePos.start, tree.sourcePos.end).mkString)
1349

1450
}
51+
52+
object InspectionTraverser {
53+
val array = Names.termName("Array")
54+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.sksamuel.scapegoat
2+
3+
import com.sksamuel.scapegoat.inspections.option.OptionGet
4+
5+
class InspectionTraverserTest extends InspectionTest(classOf[OptionGet]) {
6+
"InspectionTraverser" - {
7+
"should ignore all inspection based on SuppressWarnings on class" in {
8+
val code = """
9+
@SuppressWarnings(Array("all"))
10+
class Test {
11+
val o = Option("sammy")
12+
o.get
13+
}""".stripMargin
14+
15+
val feedback = runner.compileCodeSnippet(code)
16+
feedback.errors.assertable shouldEqual Seq.empty
17+
}
18+
19+
"should ignore specific inspection based on SuppressWarnings on class" in {
20+
val code = """
21+
@SuppressWarnings(Array("OptionGet"))
22+
class Test {
23+
val o = Option("sammy")
24+
o.get
25+
}""".stripMargin
26+
27+
val feedback = runner.compileCodeSnippet(code)
28+
feedback.errors.assertable shouldEqual Seq.empty
29+
}
30+
31+
"should ignore specific inspection based on SuppressWarnings on class (Different warning)" in {
32+
val code = """
33+
@SuppressWarnings(Array("AvoidRequire"))
34+
class Test {
35+
val o = Option("sammy")
36+
o.get
37+
}""".stripMargin
38+
39+
val feedback = runner.compileCodeSnippet(code)
40+
feedback.errors.assertable shouldEqual Seq(
41+
warning(4, Levels.Error, Some("o.get"))
42+
)
43+
}
44+
45+
"should ignore all inspection based on SuppressWarnings on method" in {
46+
val code = """
47+
class Test {
48+
@SuppressWarnings(Array("all"))
49+
def func(): String = {
50+
// ignored violation
51+
val o = Option("sammy")
52+
o.get
53+
}
54+
55+
// violation
56+
val o2 = Option("sammy")
57+
o2.get
58+
59+
func()
60+
}""".stripMargin
61+
62+
val feedback = runner.compileCodeSnippet(code)
63+
feedback.errors.assertable shouldEqual Seq(
64+
warning(11, Levels.Error, Some("o2.get"))
65+
)
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)