Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/scala-3/com/sksamuel/scapegoat/Inspection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ abstract class Inspection(
val explanation: String
) extends InspectionBase {

val self: Inspection = this
implicit val self: Inspection = this

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,54 @@
package com.sksamuel.scapegoat

import dotty.tools.dotc.ast.Trees
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.tpd.*
import dotty.tools.dotc.core.Constants
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Names
import dotty.tools.dotc.core.Symbols
import dotty.tools.dotc.core.Symbols.requiredClass
import dotty.tools.dotc.util.NoSource

abstract class InspectionTraverser extends TreeTraverser {
abstract class InspectionTraverser(using inspection: Inspection) extends TreeTraverser {

override protected def traverseChildren(tree: tpd.Tree)(using Context): Unit = {
if (!isSuppressed(tree)) {
super.traverseChildren(tree)
}
}

private def isSuppressed(t: tpd.Tree)(using Context): Boolean = {
val symbol = t.symbol
val annotation = symbol.getAnnotation(requiredClass("java.lang.SuppressWarnings"))
val arg = annotation.flatMap(_.argument(0)).map(extractArg)
val inspectionName = inspection.getClass.getSimpleName
arg match {
case Some(
Apply(Apply(TypeApply(Select(Ident(array), _), _), List(Typed(SeqLiteral(args, _), _))), _)
) =>
args.exists {
case Literal(value) if value.tag == Constants.StringTag =>
value.stringValue == "all" || value.stringValue == inspectionName
case _ => false

Check warning on line 33 in src/main/scala-3/com/sksamuel/scapegoat/InspectionTraverser.scala

View check run for this annotation

Codecov / codecov/patch

src/main/scala-3/com/sksamuel/scapegoat/InspectionTraverser.scala#L33

Added line #L33 was not covered by tests
}
case _ => false
}
}

// Scala 3.3 doesn't insert NamedArg, skip it while trying to match
private def extractArg(t: tpd.Tree): tpd.Tree = t match {
case NamedArg(_, tree) => tree
case _ => t

Check warning on line 42 in src/main/scala-3/com/sksamuel/scapegoat/InspectionTraverser.scala

View check run for this annotation

Codecov / codecov/patch

src/main/scala-3/com/sksamuel/scapegoat/InspectionTraverser.scala#L42

Added line #L42 was not covered by tests
}

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

}

object InspectionTraverser {
val array = Names.termName("Array")

Check warning on line 53 in src/main/scala-3/com/sksamuel/scapegoat/InspectionTraverser.scala

View check run for this annotation

Codecov / codecov/patch

src/main/scala-3/com/sksamuel/scapegoat/InspectionTraverser.scala#L53

Added line #L53 was not covered by tests
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.sksamuel.scapegoat

import com.sksamuel.scapegoat.inspections.option.OptionGet

class InspectionTraverserTest extends InspectionTest(classOf[OptionGet]) {
"InspectionTraverser" - {
"should ignore all inspection based on SuppressWarnings on class" in {
val code = """
@SuppressWarnings(Array("all"))
class Test {
val o = Option("sammy")
o.get
}""".stripMargin

val feedback = runner.compileCodeSnippet(code)
feedback.errors.assertable shouldEqual Seq.empty
}

"should ignore specific inspection based on SuppressWarnings on class" in {
val code = """
@SuppressWarnings(Array("OptionGet"))
class Test {
val o = Option("sammy")
o.get
}""".stripMargin

val feedback = runner.compileCodeSnippet(code)
feedback.errors.assertable shouldEqual Seq.empty
}

"should ignore specific inspection based on SuppressWarnings on class (Different warning)" in {
val code = """
@SuppressWarnings(Array("AvoidRequire"))
class Test {
val o = Option("sammy")
o.get
}""".stripMargin

val feedback = runner.compileCodeSnippet(code)
feedback.errors.assertable shouldEqual Seq(
warning(4, Levels.Error, Some("o.get"))
)
}

"should ignore all inspection based on SuppressWarnings on method" in {
val code = """
class Test {
@SuppressWarnings(Array("all"))
def func(): String = {
// ignored violation
val o = Option("sammy")
o.get
}
// violation
val o2 = Option("sammy")
o2.get
func()
}""".stripMargin

val feedback = runner.compileCodeSnippet(code)
feedback.errors.assertable shouldEqual Seq(
warning(11, Levels.Error, Some("o2.get"))
)
}
}
}