diff --git a/README.md b/README.md
index 64a830aa..df6691ca 100644
--- a/README.md
+++ b/README.md
@@ -147,6 +147,13 @@ And if you prefer a prettier report, here is a screen shot of the type of HTML r
For instructions on suppressing warnings by file, by inspection or by line see [the sbt-scapegoat README](https://github.com/sksamuel/sbt-scapegoat).
+To suppress warnings globally for the project, use `disabledInspections` or `overrideLevels` flags:
+
+```
+-P:scapegoat:disabledInspections:FinalModifierOnCaseClass
+-P:scapegoat:overrideLevels:PreferSeqEmpty=ignore:AsInstanceOf=ignore
+```
+
### Inspections
There are currently 121 inspections. An overview list is given, followed by a more detailed description of each inspection after the list (todo: finish rest of detailed descriptions)
diff --git a/src/main/scala/com/sksamuel/scapegoat/Configuration.scala b/src/main/scala/com/sksamuel/scapegoat/Configuration.scala
index 77ff679c..653a3a29 100644
--- a/src/main/scala/com/sksamuel/scapegoat/Configuration.scala
+++ b/src/main/scala/com/sksamuel/scapegoat/Configuration.scala
@@ -74,7 +74,11 @@ object Configuration {
val sourcePrefix = fromProperty("sourcePrefix", defaultValue = "src/main/scala/")(x => x)
val minimalLevel = fromProperty[Level]("minimalLevel", defaultValue = Levels.Info) { value =>
Levels.fromName(value)
+ } match {
+ case Levels.Ignore => throw new IllegalArgumentException(s"Minimal level cannot be set to 'ignore'")
+ case l => l
}
+
val dataDir = fromProperty[Option[File]](
"dataDir",
defaultValue = None
diff --git a/src/main/scala/com/sksamuel/scapegoat/Feedback.scala b/src/main/scala/com/sksamuel/scapegoat/Feedback.scala
index a2d01775..497ba24a 100644
--- a/src/main/scala/com/sksamuel/scapegoat/Feedback.scala
+++ b/src/main/scala/com/sksamuel/scapegoat/Feedback.scala
@@ -68,6 +68,7 @@ class Feedback(
case Levels.Error => reporter.error(pos, report)
case Levels.Warning => reporter.warning(pos, report)
case Levels.Info => reporter.echo(pos, report)
+ case Levels.Ignore => ()
}
}
}
@@ -93,9 +94,10 @@ final case class Warning(
) {
def hasMinimalLevelOf(minimalLevel: Level): Boolean = {
minimalLevel match {
- case Levels.Info => true
- case Levels.Warning => this.level == Levels.Warning || this.level == Levels.Error
- case Levels.Error => this.level == Levels.Error
+ case Levels.Ignore => throw new IllegalArgumentException("Ignore cannot be minimal level")
+ case Levels.Info => this.level.higherOrEqualTo(Levels.Info)
+ case Levels.Warning => this.level.higherOrEqualTo(Levels.Warning)
+ case Levels.Error => this.level.higherOrEqualTo(Levels.Error)
}
}
}
diff --git a/src/main/scala/com/sksamuel/scapegoat/Level.scala b/src/main/scala/com/sksamuel/scapegoat/Level.scala
index 9996d7bc..bf2b9674 100644
--- a/src/main/scala/com/sksamuel/scapegoat/Level.scala
+++ b/src/main/scala/com/sksamuel/scapegoat/Level.scala
@@ -4,7 +4,11 @@ package com.sksamuel.scapegoat
* @author
* Stephen Samuel
*/
-sealed trait Level
+sealed trait Level {
+ protected def weight: Short
+
+ def higherOrEqualTo(other: Level): Boolean = this.weight >= other.weight
+}
object Levels {
@@ -13,7 +17,9 @@ object Levels {
*
* An example is use of nulls. Use of nulls can lead to NullPointerExceptions and should be avoided.
*/
- case object Error extends Level
+ case object Error extends Level {
+ override protected def weight: Short = 30
+ }
/**
* Warnings are reserved for code that has bad semantics. This by itself does not necessarily mean the code
@@ -26,7 +32,9 @@ object Levels {
* Another example is a constant if. You can do things like if (true) { } if you want, but since the block
* will always evaluate, the if statement perhaps indicates a mistake.
*/
- case object Warning extends Level
+ case object Warning extends Level {
+ override protected def weight: Short = 20
+ }
/**
* Infos are used for code which is semantically fine, but there exists a more idomatic way of writing it.
@@ -43,13 +51,20 @@ object Levels {
*
* def foo = a
*/
- case object Info extends Level
+ case object Info extends Level {
+ override protected def weight: Short = 10
+ }
+
+ case object Ignore extends Level {
+ override protected def weight: Short = 0
+ }
def fromName(name: String): Level =
name.toLowerCase() match {
case "error" => Error
case "warning" => Warning
case "info" => Info
+ case "ignore" => Ignore
case _ => throw new IllegalArgumentException(s"Unrecognised level '$name'")
}
}
diff --git a/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala b/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala
index 24efdf0f..9a4b10f3 100644
--- a/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala
+++ b/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala
@@ -116,6 +116,7 @@ object HtmlReportWriter extends ReportWriter {
case Levels.Info => Info
case Levels.Warning => Warning
case Levels.Error => Error
+ case Levels.Ignore => ()
}
} {decorateCode(warning.text)}
{warning.inspection}
diff --git a/src/test/scala/com/sksamuel/scapegoat/ConfigurationTest.scala b/src/test/scala/com/sksamuel/scapegoat/ConfigurationTest.scala
index 65d7a4b9..49c4c606 100644
--- a/src/test/scala/com/sksamuel/scapegoat/ConfigurationTest.scala
+++ b/src/test/scala/com/sksamuel/scapegoat/ConfigurationTest.scala
@@ -17,5 +17,9 @@ class ConfigurationTest extends AnyFreeSpec with Matchers {
}
}
}
+
+ "throw an exception on 'ignore' as minimal level" in {
+ the[IllegalArgumentException] thrownBy Configuration.fromPluginOptions(List("minimalLevel:ignore"))
+ }
}
}
diff --git a/src/test/scala/com/sksamuel/scapegoat/FeedbackTest.scala b/src/test/scala/com/sksamuel/scapegoat/FeedbackTest.scala
index 7661c66a..b25ceb4a 100644
--- a/src/test/scala/com/sksamuel/scapegoat/FeedbackTest.scala
+++ b/src/test/scala/com/sksamuel/scapegoat/FeedbackTest.scala
@@ -132,7 +132,13 @@ class FeedbackTest extends AnyFreeSpec with Matchers with OneInstancePerTest wit
"This is description.",
"This is explanation."
)
- val inspections = Seq(inspectionError, inspectionWarning, inspectionInfo)
+ val inspectionIgnored = new DummyInspection(
+ "My default is Ignore",
+ Levels.Ignore,
+ "This is description.",
+ "This is explanation."
+ )
+ val inspections = Seq(inspectionError, inspectionWarning, inspectionInfo, inspectionIgnored)
val reporter = new StoreReporter(new Settings())
val feedback =
new Feedback(reporter, testConfiguration(consoleOutput = true, defaultSourcePrefix, Levels.Info))
@@ -159,7 +165,13 @@ class FeedbackTest extends AnyFreeSpec with Matchers with OneInstancePerTest wit
"This is description.",
"This is explanation."
)
- val inspections = Seq(inspectionError, inspectionWarning, inspectionInfo)
+ val inspectionIgnored = new DummyInspection(
+ "My default is Ignore",
+ Levels.Ignore,
+ "This is description.",
+ "This is explanation."
+ )
+ val inspections = Seq(inspectionError, inspectionWarning, inspectionInfo, inspectionIgnored)
val reporter = new StoreReporter(new Settings())
val feedback =
new Feedback(
@@ -191,7 +203,13 @@ class FeedbackTest extends AnyFreeSpec with Matchers with OneInstancePerTest wit
"This is description.",
"This is explanation."
)
- val inspections = Seq(inspectionError, inspectionWarning, inspectionInfo)
+ val inspectionIgnored = new DummyInspection(
+ "My default is Ignore",
+ Levels.Ignore,
+ "This is description.",
+ "This is explanation."
+ )
+ val inspections = Seq(inspectionError, inspectionWarning, inspectionInfo, inspectionIgnored)
val reporter = new StoreReporter(new Settings())
val feedback =
new Feedback(reporter, testConfiguration(consoleOutput = false, defaultSourcePrefix, Levels.Error))
diff --git a/src/test/scala/com/sksamuel/scapegoat/LevelsTest.scala b/src/test/scala/com/sksamuel/scapegoat/LevelsTest.scala
new file mode 100644
index 00000000..e44de3cc
--- /dev/null
+++ b/src/test/scala/com/sksamuel/scapegoat/LevelsTest.scala
@@ -0,0 +1,69 @@
+package com.sksamuel.scapegoat
+
+import org.scalatest.freespec.AnyFreeSpec
+import org.scalatest.matchers.should.Matchers
+
+class LevelsTest extends AnyFreeSpec with Matchers {
+ "Levels" - {
+ "#fromName" - {
+ "should return correct object" - {
+ "for 'error'" in {
+ Levels.fromName("error") should be(Levels.Error)
+ }
+
+ "for 'warning'" in {
+ Levels.fromName("warning") should be(Levels.Warning)
+ }
+
+ "for 'info'" in {
+ Levels.fromName("info") should be(Levels.Info)
+ }
+
+ "for 'ignore'" in {
+ Levels.fromName("ignore") should be(Levels.Ignore)
+ }
+ }
+
+ "throw an exception when uunknown level is provided" in {
+ the[IllegalArgumentException] thrownBy Levels.fromName(
+ "UNKNOWN"
+ ) should have message "Unrecognised level 'UNKNOWN'"
+ }
+ }
+ }
+
+ "Level" - {
+ "#higherOrEqual" - {
+ "should be true for levels with higher weight" - {
+ val levels = Seq(Levels.Ignore, Levels.Info, Levels.Warning, Levels.Error)
+
+ "for ignore" in {
+ levels.map(other =>
+ Levels.Ignore.higherOrEqualTo(other)
+ ) should contain theSameElementsInOrderAs Seq(true, false, false, false)
+ }
+
+ "for info" in {
+ levels.map(other => Levels.Info.higherOrEqualTo(other)) should contain theSameElementsInOrderAs Seq(
+ true,
+ true,
+ false,
+ false
+ )
+ }
+
+ "for warning" in {
+ levels.map(other =>
+ Levels.Warning.higherOrEqualTo(other)
+ ) should contain theSameElementsInOrderAs Seq(true, true, true, false)
+ }
+
+ "for error" in {
+ levels.map(other =>
+ Levels.Error.higherOrEqualTo(other)
+ ) should contain theSameElementsInOrderAs Seq(true, true, true, true)
+ }
+ }
+ }
+ }
+}