From 91f697afa2c9882ce1aa821d3e94f5c0b731b38a Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Thu, 5 Mar 2020 23:36:54 +0000 Subject: [PATCH 01/18] WIP Add description to inspections. --- .../com/sksamuel/scapegoat/Feedback.scala | 17 ++++++++-------- .../com/sksamuel/scapegoat/Inspection.scala | 20 ++++++++++--------- .../inspections/collections/ArrayEquals.scala | 9 ++++++--- .../collections/AvoidSizeEqualsZero.scala | 10 +++++++--- .../collections/AvoidSizeNotEqualsZero.scala | 11 ++++++---- .../CollectionIndexOnNonIndexedSeq.scala | 8 ++++++-- .../CollectionNamingConfusion.scala | 15 ++++++++------ .../collections/CollectionNegativeIndex.scala | 12 ++++++++--- .../CollectionPromotionToAny.scala | 9 +++++++-- 9 files changed, 71 insertions(+), 40 deletions(-) diff --git a/src/main/scala/com/sksamuel/scapegoat/Feedback.scala b/src/main/scala/com/sksamuel/scapegoat/Feedback.scala index d59292c8..c235de79 100644 --- a/src/main/scala/com/sksamuel/scapegoat/Feedback.scala +++ b/src/main/scala/com/sksamuel/scapegoat/Feedback.scala @@ -20,10 +20,10 @@ class Feedback(consoleOutput: Boolean, reporter: Reporter, sourcePrefix: String, def warns = warnings(Levels.Warning) def warnings(level: Level): Seq[Warning] = warnings.filter(_.level == level) - def warn(pos: Position, inspection: Inspection, snippet: Option[String] = None): Unit = { + def warn(pos: Position, inspection: Inspection, adhocDescription: Option[String] = None): Unit = { val level = inspection.defaultLevel val text = inspection.text - val snippetText = inspection.explanation.orElse(snippet) + val description = adhocDescription.getOrElse(inspection.description) val adjustedLevel = (levelOverridesByInspectionSimpleName.get("all"), levelOverridesByInspectionSimpleName.get(inspection.getClass.getSimpleName)) match { case (Some(l), _) => l case (None, Some(l)) => l @@ -32,11 +32,11 @@ class Feedback(consoleOutput: Boolean, reporter: Reporter, sourcePrefix: String, val sourceFileFull = pos.source.file.path val sourceFileNormalized = normalizeSourceFile(sourceFileFull) - val warning = Warning(text, pos.line, adjustedLevel, sourceFileFull, sourceFileNormalized, snippetText, inspection.getClass.getCanonicalName) + val warning = Warning(text, pos.line, adjustedLevel, sourceFileFull, sourceFileNormalized, description, inspection.getClass.getCanonicalName) warningsBuffer.append(warning) if (shouldPrint(warning)) { println(s"[${warning.level.toString.toLowerCase}] $sourceFileNormalized:${warning.line}: $text") - snippetText.foreach(s => println(s" $s")) + description.foreach(s => println(s" $s")) println() } @@ -55,14 +55,15 @@ class Feedback(consoleOutput: Boolean, reporter: Reporter, sourcePrefix: String, } } -case class Warning(text: String, +case class Warning( + text: String, line: Int, level: Level, sourceFileFull: String, sourceFileNormalized: String, - snippet: Option[String], - inspection: String) { - + description: String, + inspection: String +) { def hasMinimalLevelOf(minimalLevel: Level): Boolean = { minimalLevel match { case Levels.Info => true diff --git a/src/main/scala/com/sksamuel/scapegoat/Inspection.scala b/src/main/scala/com/sksamuel/scapegoat/Inspection.scala index 6b6ab9a9..481ec415 100644 --- a/src/main/scala/com/sksamuel/scapegoat/Inspection.scala +++ b/src/main/scala/com/sksamuel/scapegoat/Inspection.scala @@ -4,13 +4,15 @@ import scala.reflect.internal.util.Position import scala.tools.nsc.Global /** @author Stephen Samuel */ -abstract class Inspection(val text: String, val defaultLevel: Level, val explanation: Option[String] = None) { +abstract class Inspection( + val text: String, + val defaultLevel: Level, + val description: String, + val explanation: String +) { val self = this - def this(text: String, defaultLevel: Level, explanation: String) = - this(text, defaultLevel, Option(explanation)) - def inspector(context: InspectionContext): Inspector } @@ -39,13 +41,13 @@ abstract class Inspector(val context: InspectionContext) { } case class InspectionContext(global: Global, feedback: Feedback) { - - def warn(pos: Position, inspection: Inspection, snippet: String): Unit = { - feedback.warn(pos, inspection, Some(snippet)) + + def warn(pos: Position, inspection: Inspection): Unit = { + feedback.warn(pos, inspection, None) } - def warn(pos: Position, inspection: Inspection): Unit = { - feedback.warn(pos, inspection) + def warn(pos: Position, inspection: Inspection, adhocDescription: String): Unit = { + feedback.warn(pos, inspection, Some(adhocDescription)) } trait Traverser extends global.Traverser { diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ArrayEquals.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ArrayEquals.scala index e6b135c8..5930a4fd 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ArrayEquals.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ArrayEquals.scala @@ -4,8 +4,11 @@ import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ class ArrayEquals extends Inspection( - "Array equals", Levels.Info, - "Array equals is not an equality check. Use a.deep == b.deep or convert to another collection type") { + text = "Array equals", + defaultLevel = Levels.Info, + description = "Checks for comparison of arrays using == which will always return false.", + explanation = "Array equals is not an equality check. Use a.deep == b.deep or convert to another collection type." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -34,4 +37,4 @@ class ArrayEquals extends Inspection( } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala index 6f49c64f..813882b0 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class AvoidSizeEqualsZero extends Inspection("Avoid Traversable.size == 0, use Traversable.isEmpty instead", Levels.Warning) { +class AvoidSizeEqualsZero extends Inspection( + text = "Avoid Traversable.size == 0", + defaultLevel = Levels.Warning, + description = "Checks for use of Traversable.size.", + explanation = "Traversable.size can be slow for some data structure, prefer Traversable.isEmpty, which is O(1)." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,8 +21,7 @@ class AvoidSizeEqualsZero extends Inspection("Avoid Traversable.size == 0, use T override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(Select(q, Size | Length), TermName("$eq$eq")), List(Literal(Constant(0)))) if isTraversable(q) => - context.warn(tree.pos, self, - "Traversable.size is slow for some implementations. Prefer .isEmpty which is O(1): " + tree.toString().take(100)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala index 9b5094a6..98f6042b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class AvoidSizeNotEqualsZero extends Inspection("Avoid Traversable.size != 0, use Traversable.nonEmpty instead", Levels.Warning) { +class AvoidSizeNotEqualsZero extends Inspection( + text = "Avoid Traversable.size != 0", + defaultLevel = Levels.Warning, + description = "Checks for use of Traversable.size.", + explanation = "Traversable.size can be slow for some data structures, prefer Traversable.nonEmpty, which is O(1)." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,9 +21,7 @@ class AvoidSizeNotEqualsZero extends Inspection("Avoid Traversable.size != 0, us override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(Select(_, Length | Size), TermName("$bang$eq")), List(Literal(Constant(0)))) => - context.warn(tree.pos, self, - "Traversable.size is slow for some implementations. Prefer .nonEmpty which is O(1): " + tree - .toString().take(100)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionIndexOnNonIndexedSeq.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionIndexOnNonIndexedSeq.scala index 2cb5ed52..fdb8b9e1 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionIndexOnNonIndexedSeq.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionIndexOnNonIndexedSeq.scala @@ -4,7 +4,11 @@ import com.sksamuel.scapegoat._ /** @author Josh Rosen */ class CollectionIndexOnNonIndexedSeq extends Inspection( - "Seq.apply() on a non-IndexedSeq may cause performance problems", Levels.Warning) { + text = "Use of apply method on a non-indexed Seq", + defaultLevel = Levels.Warning, + description = "Checks for indexing on a Seq which is not an IndexedSeq.", + explanation = "Using an index to access elements of an IndexedSeq may cause performance problems." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -19,7 +23,7 @@ class CollectionIndexOnNonIndexedSeq extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(lhs, TermName("apply")), List(idx)) if isSeq(lhs) && !isIndexedSeq(lhs) && !isLiteral(idx)=> - context.warn(tree.pos, self, tree.toString().take(100)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNamingConfusion.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNamingConfusion.scala index ced160a1..bbd8b188 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNamingConfusion.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNamingConfusion.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class CollectionNamingConfusion extends Inspection("Collection naming Confusion", Levels.Info) { +class CollectionNamingConfusion extends Inspection( + text = "Collection naming confusion", + defaultLevel = Levels.Info, + description = "Checks for variables that are confusingly named.", + explanation = "E.g. an instance of a Set is confusingly referred to by a variable called/containing list, or the other way around." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -19,15 +24,13 @@ class CollectionNamingConfusion extends Inspection("Collection naming Confusion" tree match { case ValDef(_, TermName(name), tpt, _) if isSet(tpt) && isNamedList(name) => context.warn(tree.pos, self, - "An instance of Set is confusingly referred to by a variable called/containing list: " + - tree.toString().take(300)) + "An instance of a Set is confusingly referred to by a variable called/containing list.") case ValDef(_, TermName(name), tpt, _) if isList(tpt) && isNamedSet(name) => context.warn(tree.pos, self, - "An instance of List is confusingly referred to by a variable called/containing set: " + - tree.toString().take(300)) + "An instance of a List is confusingly referred to by a variable called/containing set.") case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNegativeIndex.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNegativeIndex.scala index c51decc7..8aae30ff 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNegativeIndex.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNegativeIndex.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class CollectionNegativeIndex extends Inspection("Collection index out of bounds", Levels.Warning) { +class CollectionNegativeIndex extends Inspection( + text = "Collection index out of bounds", + defaultLevel = Levels.Warning, + description = "Checks for negative access on a sequence, e.g. list.get(-1).", + explanation = "Trying to access Seq elements using a negative index will result in an IndexOutOfBoundsException." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -12,8 +17,9 @@ class CollectionNegativeIndex extends Inspection("Collection index out of bounds override def inspect(tree: Tree): Unit = { tree match { - case Apply(Select(lhs, TermName("apply")), List(Literal(Constant(x: Int)))) if isList(lhs) && x < 0 => - context.warn(tree.pos, self, tree.toString().take(100)) + case Apply(Select(lhs, TermName("apply")), List(Literal(Constant(x: Int)))) + if isList(lhs) && x < 0 => + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionPromotionToAny.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionPromotionToAny.scala index da3d8f70..386489a5 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionPromotionToAny.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionPromotionToAny.scala @@ -6,7 +6,12 @@ import com.sksamuel.scapegoat._ * @author Stephen Samuel * This inspection was inspired by http://p5wscala.wordpress.com/scalaprocessing-gotchas/#t2 */ -class CollectionPromotionToAny extends Inspection("Collection promotion to any", Levels.Warning) { +class CollectionPromotionToAny extends Inspection( + text = "Collection promotion to Any", + defaultLevel = Levels.Warning, + description = "Checks for collection operations that promote the collection to Any.", + explanation = "The :+ (append) operator on collections accepts any argument you give it, which means that you can end up with e.g. Seq[Any] if your types don't match." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -34,7 +39,7 @@ class CollectionPromotionToAny extends Inspection("Collection promotion to any", tree match { case TypeApply(Select(l, TermName("$colon$plus")), a :: _) => if (!isAnySeq(l) && isAny(a)) - context.warn(tree.pos, self, tree.toString().take(100)) + context.warn(tree.pos, self) case _ => continue(tree) } } From d9108ccfc9ad85af44b3c98b4dd95f9b7ed9e86e Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Sat, 7 Mar 2020 20:28:59 +0000 Subject: [PATCH 02/18] Add description to the remaining collection inspections. --- .../collections/ComparisonToEmptyList.scala | 15 ++++++---- .../collections/ComparisonToEmptySet.scala | 13 ++++---- .../collections/DuplicateMapKey.scala | 14 ++++----- .../collections/DuplicateSetValue.scala | 16 +++++----- .../collections/ExistsIncompatibleType.scala | 6 ---- .../ExistsSimplifiableToContains.scala | 9 ++++-- .../collections/FilterDotHead.scala | 12 +++++--- .../collections/FilterDotHeadOption.scala | 12 +++++--- .../collections/FilterDotIsEmpty.scala | 12 +++++--- .../collections/FilterDotSize.scala | 10 +++++-- .../collections/FilterDotSizeComparison.scala | 30 ------------------- .../collections/FilterOptionAndGet.scala | 11 ++++--- ...indAndNotEqualsNoneReplaceWithExists.scala | 11 ++++--- .../collections/FindDotIsDefined.scala | 12 +++++--- .../collections/JavaConversionsUse.scala | 10 +++++-- .../inspections/collections/ListAppend.scala | 10 +++++-- .../inspections/collections/ListSize.scala | 8 +++-- .../inspections/collections/ListTail.scala | 20 ------------- .../collections/MapGetAndGetOrElse.scala | 10 +++++-- .../collections/NegationIsEmpty.scala | 9 ++++-- .../collections/NegationNonEmpty.scala | 9 ++++-- .../collections/NegativeSeqPad.scala | 11 +++++-- .../collections/PredefIterableIsMutable.scala | 17 ++++++----- .../collections/PredefSeqIsMutable.scala | 15 +++++----- .../PredefTraversableIsMutable.scala | 9 ++++-- .../collections/PreferMapEmpty.scala | 13 ++++---- .../collections/PreferSeqEmpty.scala | 17 +++++------ .../collections/PreferSetEmpty.scala | 13 ++++---- .../inspections/collections/ReverseFunc.scala | 18 +++++------ .../collections/ReverseTailReverse.scala | 20 +++++++------ .../collections/ReverseTakeReverse.scala | 28 +++++++++-------- .../collections/SwapSortFilter.scala | 18 +++++------ .../collections/UnsafeContains.scala | 14 ++++++--- .../UnsafeTraversableMethods.scala | 9 ++++-- 34 files changed, 250 insertions(+), 211 deletions(-) delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsIncompatibleType.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSizeComparison.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListTail.scala diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptyList.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptyList.scala index 2087a048..00b4742c 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptyList.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptyList.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class ComparisonToEmptyList extends Inspection("Comparison to empty list", Levels.Info) { +class ComparisonToEmptyList extends Inspection( + text = "Comparison to empty list", + defaultLevel = Levels.Info, + description = "Checks for code like a == List() or a == Nil.", + explanation = "Prefer use of isEmpty instead of comparison to an empty List." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -25,10 +30,8 @@ class ComparisonToEmptyList extends Inspection("Comparison to empty list", Level } } - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self, - "Prefer use of isEmpty instead of comparison to an empty List: " + tree.toString().take(200)) - } + private def warn(tree: Tree): Unit = + context.warn(tree.pos, self) } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptySet.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptySet.scala index fa9d3559..f71c1e8c 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptySet.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptySet.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class ComparisonToEmptySet extends Inspection("Comparison to empty set", Levels.Info) { +class ComparisonToEmptySet extends Inspection( + text = "Comparison to empty set", + defaultLevel = Levels.Info, + description = "Checks for code like a == Set() or a == Set.empty.", + explanation = "Prefer use of isEmpty instead of comparison to an empty Set." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -25,10 +30,8 @@ class ComparisonToEmptySet extends Inspection("Comparison to empty set", Levels. } } - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self, - "Prefer use of isEmpty instead of comparison to an empty Set: " + tree.toString().take(200)) - } + private def warn(tree: Tree): Unit = + context.warn(tree.pos, self) } } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateMapKey.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateMapKey.scala index ca700900..7f711bfe 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateMapKey.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateMapKey.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class DuplicateMapKey extends Inspection("Duplicated map key", Levels.Warning) { +class DuplicateMapKey extends Inspection( + text = "Duplicated map key", + defaultLevel = Levels.Warning, + description = "Checks for duplicate key names in Map literals.", + explanation = "A map key is overwritten by a later entry." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -22,14 +27,9 @@ class DuplicateMapKey extends Inspection("Duplicated map key", Levels.Warning) { keys.toSet.size < keys.size } - private def warn(tree: Tree) = { - context.warn(tree.pos, self, - "A map key is overwritten by a later entry: " + tree.toString().take(100)) - } - override def inspect(tree: Tree): Unit = { tree match { - case Apply(TypeApply(Select(Select(_, TermName("Map")), TermName("apply")), _), args) if isDuplicateKeys(args) => warn(tree) + case Apply(TypeApply(Select(Select(_, TermName("Map")), TermName("apply")), _), args) if isDuplicateKeys(args) => context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateSetValue.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateSetValue.scala index 911fed0d..113394e2 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateSetValue.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateSetValue.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class DuplicateSetValue extends Inspection("Duplicated set value", Levels.Warning) { +class DuplicateSetValue extends Inspection( + text = "Duplicated set value", + defaultLevel = Levels.Warning, + description = "Checks for duplicate values in set literals.", + explanation = "A set value is overwritten by a later entry." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -18,14 +23,11 @@ class DuplicateSetValue extends Inspection("Duplicated set value", Levels.Warnin values.size < trees.size } - private def warn(tree: Tree) = { - context.warn(tree.pos, self, - "A set value is overwritten by a later entry: " + tree.toString().take(100)) - } - override def inspect(tree: Tree): Unit = { tree match { - case Apply(TypeApply(Select(Select(_, TermName("Set")), TermName("apply")), _), args) if hasDuplicates(args) => warn(tree) + case Apply(TypeApply(Select(Select(_, TermName("Set")), TermName("apply")), _), args) + if hasDuplicates(args) => + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsIncompatibleType.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsIncompatibleType.scala deleted file mode 100644 index 54d7a3b3..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsIncompatibleType.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections.collections - -/** @author Stephen Samuel */ -class ExistsIncompatibleType { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsSimplifiableToContains.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsSimplifiableToContains.scala index cb737eae..960f6ede 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsSimplifiableToContains.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsSimplifiableToContains.scala @@ -7,7 +7,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} * * Inspired by Intellij */ -class ExistsSimplifiableToContains extends Inspection("Exists simplifiable to contains", Levels.Info) { +class ExistsSimplifiableToContains extends Inspection( + text = "Exists simplifiable to contains", + defaultLevel = Levels.Info, + description = "Checks if exists() can be simplified to contains().", + explanation = "exists(x => x == y) can be replaced with contains(y)." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -31,7 +36,7 @@ class ExistsSimplifiableToContains extends Inspection("Exists simplifiable to co tree match { case Apply(Select(lhs, TermName("exists")), List(Function(_, Apply(Select(_, Equals), List(x))))) if isContainsTraversable(lhs) && doesElementTypeMatch(lhs, x) => - context.warn(tree.pos, self, "exists(x => x == y) can be replaced with contains(y): " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHead.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHead.scala index ffd8d548..5d17447d 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHead.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHead.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class FilterDotHead extends Inspection("filter().head can throw an exception; use find()", Levels.Info) { +class FilterDotHead extends Inspection( + text = "filter().head can throw an exception", + defaultLevel = Levels.Info, + description = "Checks for use of filter().head.", + explanation = "filter().head can throw an exception if the collection is empty - it can be replaced with find() match {...}." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,11 +21,10 @@ class FilterDotHead extends Inspection("filter().head can throw an exception; us override def inspect(tree: Tree): Unit = { tree match { case Select(Apply(Select(_, Filter), _), Head) => - context.warn(tree.pos, self, - ".filter(x => Bool).head can be replaced with find(x => Bool) and a match: " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHeadOption.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHeadOption.scala index 95088180..326bbe4a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHeadOption.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHeadOption.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class FilterDotHeadOption extends Inspection("filter().headOption instead of find()", Levels.Info) { +class FilterDotHeadOption extends Inspection( + text = "filter().headOption instead of find()", + defaultLevel = Levels.Info, + description = "Checks for use of filter().headOption.", + explanation = "filter() scans the entire collection, which is unnecessary if you only want to get the first element that satisfies the predicate - filter().headOption can be replaced with find() to potentially avoid scanning the entire collection." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,11 +18,10 @@ class FilterDotHeadOption extends Inspection("filter().headOption instead of fin override def inspect(tree: Tree): Unit = { tree match { case Select(Apply(Select(_, TermName("filter")), _), TermName("headOption")) => - context.warn(tree.pos, self, - ".filter(x => Bool).headOption can be replaced with find(x => Bool): " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotIsEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotIsEmpty.scala index f7b25e09..f8142217 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotIsEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotIsEmpty.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class FilterDotIsEmpty extends Inspection("filter().isEmpty instead of !exists()", Levels.Info) { +class FilterDotIsEmpty extends Inspection( + text = "filter().isEmpty instead of !exists()", + defaultLevel = Levels.Info, + description = "Checks for use of filter().isEmpty.", + explanation = "filter() scans the entire collection, which can potentially be avoided if the element exists in the collection - filter().isEmpty can be replaced with !exists()." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,11 +18,10 @@ class FilterDotIsEmpty extends Inspection("filter().isEmpty instead of !exists() override def inspect(tree: Tree): Unit = { tree match { case Select(Apply(Select(_, TermName("filter")), _), TermName("isEmpty")) => - context.warn(tree.pos, self, - ".filter(x => Bool).isEmpty can be replaced with !exists(x => Bool): " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSize.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSize.scala index 3c2b916d..620da6f7 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSize.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSize.scala @@ -7,7 +7,12 @@ import com.sksamuel.scapegoat._ * * Inspired by IntelliJ */ -class FilterDotSize extends Inspection("filter().size() instead of count()", Levels.Info) { +class FilterDotSize extends Inspection( + text = "filter().size() instead of count()", + defaultLevel = Levels.Info, + description = "Checks if filter().size can be simplified to count().", + explanation = "filter().size can be replaced with count(), which is more concise." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -17,8 +22,7 @@ class FilterDotSize extends Inspection("filter().size() instead of count()", Lev override def inspect(tree: Tree): Unit = { tree match { case Select(Apply(Select(_, TermName("filter")), _), TermName("size")) => - context.warn(tree.pos, self, - ".filter(x => Bool).size can be replaced with count(x => Bool): " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSizeComparison.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSizeComparison.scala deleted file mode 100644 index 2d8f6e68..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSizeComparison.scala +++ /dev/null @@ -1,30 +0,0 @@ -package com.sksamuel.scapegoat.inspections.collections - -import com.sksamuel.scapegoat._ - -/** - * @author Stephen Samuel - * - * Inspired by IntelliJ - * - * Checks for filter.size > 0, filter.size == 0, etc - */ -class FilterDotSizeComparison extends Inspection("TODO", Levels.Info) { - - def inspector(context: InspectionContext): Inspector = new Inspector(context) { - override def postTyperTraverser = Some apply new context.Traverser { - - import context.global._ - - override def inspect(tree: Tree): Unit = { - tree match { - // todo - case Select(Apply(Select(_, TermName("filter")), _), TermName("isEmpty")) => - context.warn(tree.pos, self, - "TODO" + tree.toString().take(500)) - case _ => continue(tree) - } - } - } - } -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterOptionAndGet.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterOptionAndGet.scala index b1f8d88c..4bc7cc0c 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterOptionAndGet.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterOptionAndGet.scala @@ -4,7 +4,11 @@ import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ class FilterOptionAndGet extends Inspection( - "filter(_.isDefined).map(_.get) instead of flatten", Levels.Info) { + text = "filter(_.isDefined).map(_.get) instead of flatten", + defaultLevel = Levels.Info, + description = "Checks whether the expression can be rewritten using flatten.", + explanation = "filter(_.isDefined).map(_.get) can be replaced with flatten." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,11 +20,10 @@ class FilterOptionAndGet extends Inspection( case Apply(TypeApply( Select(Apply(Select(_, TermName("filter")), List(Function(_, Select(_, TermName("isDefined"))))), TermName("map")), _), List(Function(_, Select(_, TermName("get"))))) => - context.warn(tree.pos, self, - ".filter(_.isDefined).map(_.get) can be replaced with flatten: " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindAndNotEqualsNoneReplaceWithExists.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindAndNotEqualsNoneReplaceWithExists.scala index bc46cfa8..748a9736 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindAndNotEqualsNoneReplaceWithExists.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindAndNotEqualsNoneReplaceWithExists.scala @@ -3,7 +3,11 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} class FindAndNotEqualsNoneReplaceWithExists extends Inspection( - "find(x => ) != None instead of exists(x =>)", Levels.Info) { + text = "find(x => ) != None instead of exists(x =>)", + defaultLevel = Levels.Info, + description = "Checks whether find() can be replaced with exists().", + explanation = "find() != None can be replaced with exists(), which is more concise." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -14,11 +18,10 @@ class FindAndNotEqualsNoneReplaceWithExists extends Inspection( tree match { case Apply(Select(Apply(Select(_, TermName("find")), _), TermName("$bang$eq")), List(Select(_, TermName("None")))) => - context.warn(tree.pos, self, - ".find(x => ) != None can be replaced with exists(x =>): " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindDotIsDefined.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindDotIsDefined.scala index 144db750..6015ebfc 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindDotIsDefined.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindDotIsDefined.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class FindDotIsDefined extends Inspection("use exists() not find().isDefined()", Levels.Info) { +class FindDotIsDefined extends Inspection( + text = "find().isDefined() instead of exists()", + defaultLevel = Levels.Info, + description = "Checks whether find() can be replaced with exists().", + explanation = "find().isDefined can be replaced with exists(), which is more concise." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,11 +18,10 @@ class FindDotIsDefined extends Inspection("use exists() not find().isDefined()", override def inspect(tree: Tree): Unit = { tree match { case Select(Apply(Select(_, TermName("find")), _), TermName("isDefined")) => - context.warn(tree.pos, self, - ".find(x => Bool).isDefined can be replaced with exists(x => Bool): " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/JavaConversionsUse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/JavaConversionsUse.scala index ce93d363..01937af2 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/JavaConversionsUse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/JavaConversionsUse.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class JavaConversionsUse extends Inspection("Java conversions", Levels.Warning, - "Use of java conversions can lead to unusual behaviour. It is recommended to use JavaConverters") { +class JavaConversionsUse extends Inspection( + text = "Java conversions", + defaultLevel = Levels.Warning, + description = "Checks for use of Java conversions.", + explanation = "Use of java conversions can lead to unusual behaviour. It is recommended to use JavaConverters." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -20,4 +24,4 @@ class JavaConversionsUse extends Inspection("Java conversions", Levels.Warning, } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListAppend.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListAppend.scala index 5407b37a..2cd18dcc 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListAppend.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListAppend.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class ListAppend extends Inspection("List append is slow", Levels.Info, - "List append is O(n). For large lists, consider using cons (::) or another data structure such as ListBuffer or Vector and converting to a List once built.") { +class ListAppend extends Inspection( + text = "List append is slow", + defaultLevel = Levels.Info, + description = "Checks for when elements are appended to a list.", + explanation = "List append is O(n). For large lists, consider using cons (::) or another data structure such as ListBuffer, Vector or a cats.data.Chain (which has constant prepend and append)." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -22,4 +26,4 @@ class ListAppend extends Inspection("List append is slow", Levels.Info, } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListSize.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListSize.scala index b5ec950e..ced1797f 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListSize.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListSize.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class ListSize extends Inspection("List.size is O(n)", Levels.Info, - "List.size is O(n). Consider using a different data type with O(1) size lookup such as Vector or Array.") { +class ListSize extends Inspection( + text = "List.size is O(n)", + defaultLevel = Levels.Info, + description = "Checks for use of List.size.", + explanation = "List.size is O(n). Consider using a different data type with O(1) size lookup such as Vector or an Array." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListTail.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListTail.scala deleted file mode 100644 index f64038a8..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ListTail.scala +++ /dev/null @@ -1,20 +0,0 @@ -package com.sksamuel.scapegoat.inspections.collections - -import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} - -/** @author Stephen Samuel */ -class ListTail extends Inspection("TODO", Levels.Info) { - - def inspector(context: InspectionContext): Inspector = new Inspector(context) { - override def postTyperTraverser = Some apply new context.Traverser { - - import context.global._ - - override def inspect(tree: Tree): Unit = { - tree match { - case _ => continue(tree) - } - } - } - } -} \ No newline at end of file diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala index 27960e6d..94dc413a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala @@ -8,7 +8,12 @@ import com.sksamuel.scapegoat._ * Inspired by Intellij inspection that does: * myMap.get(key).getOrElse(defaultValue) –> myMap.getOrElse(key, defaultValue) */ -class MapGetAndGetOrElse extends Inspection("Use of .get.getOrElse instead of .getOrElse", Levels.Error) { +class MapGetAndGetOrElse extends Inspection( + text = "Use of Map.get(key).getOrElse(value) instead of Map.getOrElse(key, value)", + defaultLevel = Levels.Error, + description = "Checks whether Map.get().getOrElse() can be simplified to Map.getOrElse().", + explanation = "Map.get(key).getOrElse(value) can be replaced with Map.getOrElse(key, value), which is more concise." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -19,8 +24,7 @@ class MapGetAndGetOrElse extends Inspection("Use of .get.getOrElse instead of .g tree match { case Apply(TypeApply(Select(Apply(Select(left, TermName("get")), List(key)), TermName("getOrElse")), _), List(defaultValue)) if isMap(left) => - context.warn(tree.pos, self, - s"Use of .get($key).getOrElse($defaultValue) instead of getOrElse($key, $defaultValue): " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala index 144a85fb..56c0fe1b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class NegationIsEmpty extends Inspection("!isEmpty can be replaced with nonEmpty", Levels.Info) { +class NegationIsEmpty extends Inspection( + text = "!isEmpty can be replaced with nonEmpty", + defaultLevel = Levels.Info, + description = "Checks whether !isEmpty can be replaced with nonEmpty.", + explanation = "!Traversable.isEmpty can be replaced with Traversable.nonEmpty to make it easier to reason about." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,7 +21,7 @@ class NegationIsEmpty extends Inspection("!isEmpty can be replaced with nonEmpty override def inspect(tree: Tree): Unit = { tree match { case Select(Select(lhs, IsEmpty), Bang) if isTraversable(lhs) => - context.warn(tree.pos, self, tree.toString().take(100)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala index e69d0802..30ceb947 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class NegationNonEmpty extends Inspection("!nonEmpty can be replaced with isEmpty", Levels.Info) { +class NegationNonEmpty extends Inspection( + text = "!nonEmpty can be replaced with isEmpty", + defaultLevel = Levels.Info, + description = "Checks whether !nonEmpty can be replaced with isEmpty.", + explanation = "!Traversable.nonEmpty can be replaced with Traversable.isEmpty to make it easier to reason about." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,7 +21,7 @@ class NegationNonEmpty extends Inspection("!nonEmpty can be replaced with isEmpt override def inspect(tree: Tree): Unit = { tree match { case Select(Select(lhs, IsEmpty), Bang) if isTraversable(lhs) => - context.warn(tree.pos, self, tree.toString().take(100)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegativeSeqPad.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegativeSeqPad.scala index 50f88ce1..3f749e7a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegativeSeqPad.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegativeSeqPad.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class NegativeSeqPad extends Inspection("Negative seq padTo", Levels.Error) { +class NegativeSeqPad extends Inspection( + text = "Negative seq padTo", + defaultLevel = Levels.Error, + description = "Checks for use of padTo with negative length.", + explanation = "Seq.padTo with a negative length will not have any effect." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,10 +18,10 @@ class NegativeSeqPad extends Inspection("Negative seq padTo", Levels.Error) { override def inspect(tree: Tree): Unit = { tree match { case Apply(TypeApply(Select(_, TermName("padTo")), _), Literal(Constant(_)) :: _) => - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefIterableIsMutable.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefIterableIsMutable.scala index 99df2b79..f916ce12 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefIterableIsMutable.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefIterableIsMutable.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class PredefIterableIsMutable extends Inspection("Default Iterable is mutable", Levels.Info, - "Iterable aliases scala.collection.mutable.Iterable. Did you intend to use an immutable Iterable?") { +class PredefIterableIsMutable extends Inspection( + text = "Default Iterable is mutable", + defaultLevel = Levels.Info, + description = "Checks for use of mutable Iterable.", + explanation = "Iterable aliases scala.collection.mutable.Iterable. Did you intend to use an immutable Iterable?" +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -14,14 +18,11 @@ class PredefIterableIsMutable extends Inspection("Default Iterable is mutable", override def inspect(tree: Tree): Unit = { tree match { case DefDef(_, _, _, _, _, _) if tree.symbol.isAccessor => - case TypeTree() if tree.tpe.erasure.toString() == "Iterable[Any]" => warn(tree) + case TypeTree() if tree.tpe.erasure.toString() == "Iterable[Any]" => + context.warn(tree.pos, self) case _ => continue(tree) } } - - def warn(tree: Tree): Unit = { - context.warn(tree.pos, self) - } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefSeqIsMutable.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefSeqIsMutable.scala index 4ce1b74a..e1bb558b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefSeqIsMutable.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefSeqIsMutable.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class PredefSeqIsMutable extends Inspection("Predef.Seq is mutable", Levels.Info, - "Predef.Seq aliases scala.collection.mutable.Seq. Did you intend to use an immutable Seq?") { +class PredefSeqIsMutable extends Inspection( + text = "Predef.Seq is mutable", + defaultLevel = Levels.Info, + description = "Checks for use of mutable Seq.", + explanation = "Predef.Seq aliases scala.collection.mutable.Seq. Did you intend to use an immutable Seq?" +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = if (isScala213) None else Some( @@ -15,14 +19,11 @@ class PredefSeqIsMutable extends Inspection("Predef.Seq is mutable", Levels.Info override def inspect(tree: Tree): Unit = { tree match { case DefDef(_, _, _, _, _, _) if tree.symbol.isAccessor => - case TypeTree() if tree.tpe.erasure.toString() == "Seq[Any]" => warn(tree) + case TypeTree() if tree.tpe.erasure.toString() == "Seq[Any]" => + context.warn(tree.pos, self) case _ => continue(tree) } } - - def warn(tree: Tree): Unit = { - context.warn(tree.pos, self) - } } ) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefTraversableIsMutable.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefTraversableIsMutable.scala index 40364254..121d80b8 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefTraversableIsMutable.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PredefTraversableIsMutable.scala @@ -4,8 +4,11 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ class PredefTraversableIsMutable extends Inspection( - "Default Traversable is mutable", Levels.Info, - "Traversable aliases scala.collection.mutable.Traversable. Did you intend to use an immutable Traversable?") { + text = "Traversable is mutable", + defaultLevel = Levels.Info, + description = "", + explanation = "Traversable aliases scala.collection.mutable.Traversable. Did you intend to use an immutable Traversable?" +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -25,4 +28,4 @@ class PredefTraversableIsMutable extends Inspection( } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferMapEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferMapEmpty.scala index f305d494..26523dac 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferMapEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferMapEmpty.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ -class PreferMapEmpty extends Inspection("Prefer Map.empty", Levels.Info) { +class PreferMapEmpty extends Inspection( + text = "Prefer Map.empty", + defaultLevel = Levels.Info, + description = "Checks for use of Map().", + explanation = "Map[K,V]() allocates an intermediate object. Consider Map.empty which returns a singleton instance without creating a new object." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,12 +21,10 @@ class PreferMapEmpty extends Inspection("Prefer Map.empty", Levels.Info) { tree match { case a@Apply(TypeApply(Select(Select(_, MapTerm), ApplyTerm), _), List()) if a.tpe.toString.startsWith("scala.collection.immutable.") => - context.warn(tree.pos, self, - "Map[K,V]() allocates an intermediate object. Consider Map.empty which returns a singleton instance without creating a new object." + - tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSeqEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSeqEmpty.scala index ade7871f..99822df3 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSeqEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSeqEmpty.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class PreferSeqEmpty extends Inspection("Prefer Seq.empty", Levels.Info) { +class PreferSeqEmpty extends Inspection( + text = "Prefer Seq.empty", + defaultLevel = Levels.Info, + description = "Checks for use of Seq().", + explanation = "Seq[T]() allocates an intermediate object. Consider Seq.empty which returns a singleton instance without creating a new object." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -17,16 +22,10 @@ class PreferSeqEmpty extends Inspection("Prefer Seq.empty", Levels.Info) { tree match { case a@Apply(TypeApply(Select(Select(_, SeqTerm), ApplyTerm), _), List()) if (!a.tpe.toString.startsWith("scala.collection.mutable.")) => - warn(tree) + context.warn(tree.pos, self) case _ => continue(tree) } } - - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self, - "Seq[T]() allocates an intermediate object. Consider Seq.empty wich returns a singleton instance without creating a new object. " + - tree.toString().take(500)) - } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSetEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSetEmpty.scala index b12cd739..7dabf45b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSetEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSetEmpty.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class PreferSetEmpty extends Inspection("Prefer Set.empty", Levels.Info) { +class PreferSetEmpty extends Inspection( + text = "Prefer Set.empty", + defaultLevel = Levels.Info, + description = "Checks for use of Set().", + explanation = "Set[T]() allocates an intermediate object. Consider Set.empty which returns a singleton instance without creating a new object." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -17,12 +22,10 @@ class PreferSetEmpty extends Inspection("Prefer Set.empty", Levels.Info) { tree match { case a@Apply(TypeApply(Select(Select(_, SetTerm), ApplyTerm), _), List()) if a.tpe.toString.startsWith("scala.collection.immutable.") => - context.warn(tree.pos, self, - "Set[T]() allocates an intermediate object. Consider Set.empty which returns a singleton instance without creating a new object. " + - tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseFunc.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseFunc.scala index 3524656c..7a167c92 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseFunc.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseFunc.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ -class ReverseFunc extends Inspection("Unnecessary reverse", Levels.Info) { +class ReverseFunc extends Inspection( + text = "Unnecessary reverse", + defaultLevel = Levels.Info, + description = "Checks for use of reverse followed by head/headOption/iterator/map.", + explanation = "reverse followed by head, headOption, iterator, or map can be replaced, respectively, with last, lastOption, reverseIterator, or reverseMap." +) { object FuncReplace { @@ -25,18 +30,13 @@ class ReverseFunc extends Inspection("Unnecessary reverse", Levels.Info) { tree match { case Select(Select(c, TermName("reverse")), TermName(FuncReplace(func, replace))) if c.tpe <:< typeOf[Traversable[Any]] => - warn(func, replace, tree) + context.warn(tree.pos, self) case Select(Apply(arrayOps1, List(Select(Apply(arrayOps2, List(_)), TermName("reverse")))), TermName(FuncReplace(func, replace))) if arrayOps1.toString.contains("ArrayOps") && arrayOps2.toString.contains("ArrayOps") => - warn(func, replace, tree) + context.warn(tree.pos, self) case _ => continue(tree) } } - - private def warn(func: String, replace: String, tree: Tree) = - context.warn(tree.pos, self, - s".reverse.$func can be replaced with $replace: " + tree.toString().take(500)) - } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTailReverse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTailReverse.scala index 950fc592..05a9d07d 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTailReverse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTailReverse.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ -class ReverseTailReverse extends Inspection("reverse.tail.reverse instead of init", Levels.Info) { +class ReverseTailReverse extends Inspection( + text = "reverse.tail.reverse instead of init", + defaultLevel = Levels.Info, + description = "Checks for use of reverse.tail.reverse.", + explanation = "reverse.tail.reverse can be replaced with init, which is more concise." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -12,18 +17,15 @@ class ReverseTailReverse extends Inspection("reverse.tail.reverse instead of ini override def inspect(tree: Tree): Unit = { tree match { case Select(Select(Select(c, TermName("reverse")), TermName("tail")), TermName("reverse")) if isTraversable(c) => - warn(tree) - case Select(Apply(arrayOps0, List(Select(Apply(arrayOps1, List(Select(Apply(arrayOps2, List(_)), TermName("reverse")))), TermName("tail")))), TermName("reverse")) if (arrayOps0.toString.contains("ArrayOps")) + context.warn(tree.pos, self) + case Select(Apply(arrayOps0, List(Select(Apply(arrayOps1, List(Select(Apply(arrayOps2, List(_)), TermName("reverse")))), TermName("tail")))), TermName("reverse")) + if (arrayOps0.toString.contains("ArrayOps")) && arrayOps1.toString.contains("ArrayOps") && arrayOps2.toString.contains("ArrayOps") => - warn(tree) + context.warn(tree.pos, self) case _ => continue(tree) } } - - private def warn(tree: Tree) = - context.warn(tree.pos, self, - ".reverse.tail.reverse can be replaced with init: " + tree.toString().take(500)) } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTakeReverse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTakeReverse.scala index 66503fbe..da2c53c2 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTakeReverse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTakeReverse.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ -class ReverseTakeReverse extends Inspection("reverse.take(...).reverse instead of takeRight", Levels.Info) { +class ReverseTakeReverse extends Inspection( + text = "reverse.take().reverse instead of takeRight", + defaultLevel = Levels.Info, + description = "Checks for use of reverse.take().reverse.", + explanation = "reverse.take().reverse can be replaced with takeRight, which is more concise." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -11,20 +16,17 @@ class ReverseTakeReverse extends Inspection("reverse.take(...).reverse instead o override def inspect(tree: Tree): Unit = { tree match { - case Select(Apply(Select(Select(c, TermName("reverse")), TermName("take")), _), TermName("reverse")) if isTraversable(c) => - warn(tree) - case Select(Apply(arrayOps0, List(Apply(Select(Apply(arrayOps1, List(Select(Apply(arrayOps2, List(_)), TermName("reverse")))), TermName("take")), _))), TermName("reverse")) if (arrayOps0.toString.contains("ArrayOps")) - && (arrayOps1.toString.contains("ArrayOps")) - && (arrayOps2.toString.contains("ArrayOps")) => - warn(tree) + case Select(Apply(Select(Select(c, TermName("reverse")), TermName("take")), _), TermName("reverse")) + if isTraversable(c) => + context.warn(tree.pos, self) + case Select(Apply(arrayOps0, List(Apply(Select(Apply(arrayOps1, List(Select(Apply(arrayOps2, List(_)), TermName("reverse")))), TermName("take")), _))), TermName("reverse")) + if (arrayOps0.toString.contains("ArrayOps")) + && (arrayOps1.toString.contains("ArrayOps")) + && (arrayOps2.toString.contains("ArrayOps")) => + context.warn(tree.pos, self) case _ => continue(tree) } } - - private def warn(tree: Tree) = { - context.warn(tree.pos, self, - ".reverse.take(...).reverse can be replaced with takeRight: " + tree.toString().take(500)) - } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/SwapSortFilter.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/SwapSortFilter.scala index 55cf5f93..ae91787b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/SwapSortFilter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/SwapSortFilter.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class SwapSortFilter extends Inspection("Swap sort filter", Levels.Info) { +class SwapSortFilter extends Inspection( + text = "Swap sort filter", + defaultLevel = Levels.Info, + description = "Checks for an inefficient use of filter.sort.", + explanation = "Filter first and then sort the remaining collection. Swap sort.filter for filter.sort for better performance." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,19 +18,14 @@ class SwapSortFilter extends Inspection("Swap sort filter", Levels.Info) { override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(Apply(TypeApply(Select(lhs, TermName("sorted")), _), _), TermName("filter")), _) if isSeq(lhs) => - warn(tree) + context.warn(tree.pos, self) case Apply(Select(Apply(Apply(TypeApply(Select(lhs, TermName("sortBy")), _), _), _), TermName("filter")), _) if isSeq(lhs) => - warn(tree) + context.warn(tree.pos, self) case Apply(Select(Apply(Select(lhs, TermName("sortWith")), _), TermName("filter")), _) if isSeq(lhs) => - warn(tree) + context.warn(tree.pos, self) case _ => continue(tree) } } - - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self, - "Swap sort.filter for filter.sort for better performance: " + tree.toString().take(500)) - } } } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeContains.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeContains.scala index 344ab367..3ac3460e 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeContains.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeContains.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class UnsafeContains extends Inspection("Unsafe contains", Levels.Error) { +class UnsafeContains extends Inspection( + text = "Unsafe contains", + defaultLevel = Levels.Error, + description = "Checks Seq.contains() and Option.contains() for unrelated types.", + explanation = "contains() accepts arguments af any type, which means you might be checking if your collection contains an element of an unrelated type." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -21,7 +26,7 @@ class UnsafeContains extends Inspection("Unsafe contains", Levels.Error) { private def isCompatibleType(container: Tree, value: Tree, typ: Symbol): Boolean = container.tpe baseType typ match { case TypeRef(_, _, elem :: Nil) if elem.isInstanceOf[Any] && elem <:< value.tpe => true case TypeRef(_, _, elem :: Nil) => value.tpe <:< elem - case _ => false + case _ => false } private def isCompatibleType(container: Tree, value: Tree): Boolean = { @@ -29,8 +34,9 @@ class UnsafeContains extends Inspection("Unsafe contains", Levels.Error) { } override def inspect(tree: Tree): Unit = tree match { - case Applied(Select(lhs, Contains), _, (arg :: Nil) :: Nil) if isSeqOrOption(lhs) && !isCompatibleType(lhs, arg) => - context.warn(tree.pos, self, tree.toString().take(300)) + case Applied(Select(lhs, Contains), _, (arg :: Nil) :: Nil) + if isSeqOrOption(lhs) && !isCompatibleType(lhs, arg) => + context.warn(tree.pos, self) case _ => continue(tree) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeTraversableMethods.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeTraversableMethods.scala index df8c1b07..dbeea9ef 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeTraversableMethods.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeTraversableMethods.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections.collections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} -class UnsafeTraversableMethods extends Inspection("Use of unsafe Traversable methods (head, tail, init, last, reduce, reduceLeft, reduceRight, max, maxBy, min, minBy)", Levels.Error) { +class UnsafeTraversableMethods extends Inspection( + text = "Use of unsafe Traversable methods.", + defaultLevel = Levels.Error, + description = "Checks for use of unsafe methods on Traversable.", + explanation = "The following methods on Traversable are considered to be unsafe (head, tail, init, last, reduce, reduceLeft, reduceRight, max, maxBy, min, minBy)." +) { private val unsafeMethods = Set( "head", @@ -27,7 +32,7 @@ class UnsafeTraversableMethods extends Inspection("Use of unsafe Traversable met tree match { case Select(left, TermName(method)) => if (isTraversable(left) && unsafeMethods.contains(method)) - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } From e9f04ea4443a0489e75be76a4f3143bda82fbcdd Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Sun, 8 Mar 2020 18:58:41 +0000 Subject: [PATCH 03/18] Add description to control flow, empty, exception, imports, inference and matching inspections. --- .../controlflow/RepeatedIfElseBody.scala | 16 ++++++++---- .../inspections/controlflow/WhileTrue.scala | 15 ++++++----- .../inspections/empty/EmptyFor.scala | 9 +++++-- .../inspections/empty/EmptyIfBlock.scala | 9 +++++-- .../inspections/empty/EmptyMethod.scala | 9 +++++-- .../empty/EmptySynchronizedBlock.scala | 11 +++++--- .../inspections/empty/EmptyTryBlock.scala | 11 +++++--- .../inspections/empty/EmptyWhileBlock.scala | 11 +++++--- .../ComparingFloatingPointTypes.scala | 6 ++++- .../equality/ComparingUnrelatedTypes.scala | 9 +++++-- .../equality/ComparisonWithSelf.scala | 12 ++++++--- .../exception/CatchException.scala | 11 +++++--- .../inspections/exception/CatchFatal.scala | 11 +++++--- .../inspections/exception/CatchNpe.scala | 8 ++++-- .../exception/CatchThrowable.scala | 11 +++++--- .../CaughtExceptionImmediatelyRethrown.scala | 5 ---- .../IncorrectlyNamedExceptions.scala | 15 ++++++----- .../exception/ProhibitedExceptionThrown.scala | 5 ---- .../exception/SwallowedException.scala | 15 ++++++----- .../exception/UnreachableCatch.scala | 10 ++++--- .../inspections/imports/DuplicateImport.scala | 9 +++++-- .../inspections/imports/WildcardImport.scala | 9 +++++-- .../inference/BoundedByFinalType.scala | 14 +++++----- .../inference/MethodReturningAny.scala | 10 ++++--- .../inference/PointlessTypeBounds.scala | 11 +++++--- .../ProductWithSerializableInferred.scala | 10 ++++--- .../inspections/matching/FruitlessMatch.scala | 5 ---- .../PartialFunctionInsteadOfMatch.scala | 26 ++++++++++++------- .../matching/RepeatedCaseBody.scala | 12 ++++++--- .../SuspiciousMatchOnClassObject.scala | 17 ++++++------ 30 files changed, 212 insertions(+), 120 deletions(-) delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/exception/CaughtExceptionImmediatelyRethrown.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/exception/ProhibitedExceptionThrown.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/matching/FruitlessMatch.scala diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/RepeatedIfElseBody.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/RepeatedIfElseBody.scala index 939adaeb..f7a0aaf6 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/RepeatedIfElseBody.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/RepeatedIfElseBody.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections.controlflow import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} -class RepeatedIfElseBody extends Inspection("Repeated body of if main and else branch", Levels.Warning) { +class RepeatedIfElseBody extends Inspection( + text = "Repeated body of if main and else branch", + defaultLevel = Levels.Warning, + description = "Checks for the main branch and the else branch of an if being the same.", + explanation = "The if statement could be refactored if both branches are the same or start with the same." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -23,12 +28,13 @@ class RepeatedIfElseBody extends Inspection("Repeated body of if main and else b override def inspect(tree: Tree): Unit = { tree match { case If(_, mainBranch, elseBranch) if isRepeated(mainBranch, elseBranch) => - context.warn(tree.pos, self, "Main and else branches of if are repeated: " + tree.toString().take(500)) - case If(_, mainBranch@Block(_, _), elseBranch@Block(_, _)) if twoBlocksStartWithTheSame(mainBranch, elseBranch) => - context.warn(tree.pos, self, "Main and else branches start with the same command: " + tree.toString().take(500)) + context.warn(tree.pos, self, "Main and else branches of if are repeated.") + case If(_, mainBranch@Block(_, _), elseBranch@Block(_, _)) + if twoBlocksStartWithTheSame(mainBranch, elseBranch) => + context.warn(tree.pos, self, "Main and else branches start with the same command.") case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/WhileTrue.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/WhileTrue.scala index ba3c113c..6984ab1d 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/WhileTrue.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/WhileTrue.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat._ /** * @author Stephen Samuel */ -class WhileTrue extends Inspection("While true loop", Levels.Warning) { +class WhileTrue extends Inspection( + text = "While true loop", + defaultLevel = Levels.Warning, + description = "Checks for code that uses a while(true) or do {...} while(true) block.", + explanation = "A (do) while true loop is unlikely to be meant for production." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -15,11 +20,9 @@ class WhileTrue extends Inspection("While true loop", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case LabelDef(name, _, If(cond, _, _)) if isWhile(name) && isConstantCondition(cond) => - context.warn(tree.pos, self, - "A while true loop is unlikely to be meant for production: " + tree.toString().take(500)) + context.warn(tree.pos, self) case LabelDef(name, _, Block(_, If(cond, _, _))) if isWhile(name) && isConstantCondition(cond) => - context.warn(tree.pos, self, - "A do while true loop is unlikely to be meant for production: " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } @@ -34,4 +37,4 @@ class WhileTrue extends Inspection("While true loop", Levels.Warning) { } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala index f3d7541a..1d61e3f4 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.empty import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class EmptyFor extends Inspection("Empty for loop", Levels.Warning) { +class EmptyFor extends Inspection( + text = "Empty for loop", + defaultLevel = Levels.Warning, + description = "Checks for empty for loops.", + explanation = "An empty for loop isn't a common practice and in most cases is considered as dead code.", +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -15,7 +20,7 @@ class EmptyFor extends Inspection("Empty for loop", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case Apply(TypeApply(Select(_, Foreach), _), List(Function(List(ValDef(_, _, _, EmptyTree)), Literal(Constant(()))))) => - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyIfBlock.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyIfBlock.scala index a026bed0..fe2c0f23 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyIfBlock.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyIfBlock.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.empty import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class EmptyIfBlock extends Inspection("Empty if statement", Levels.Warning) { +class EmptyIfBlock extends Inspection( + text = "Empty if expression", + defaultLevel = Levels.Warning, + description = "Checks for empty if blocks.", + explanation = "An empty if block is considered as dead code." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,7 +18,7 @@ class EmptyIfBlock extends Inspection("Empty if statement", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case If(_, Literal(Constant(())), _) => - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyMethod.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyMethod.scala index 7fd05a8e..8127392a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyMethod.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyMethod.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.empty import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class EmptyMethod extends Inspection("Empty method", Levels.Warning) { +class EmptyMethod extends Inspection( + text = "Empty method", + defaultLevel = Levels.Warning, + description = "Checks for empty method statements.", + explanation = "An empty method is considered as dead code." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -17,7 +22,7 @@ class EmptyMethod extends Inspection("Empty method", Levels.Warning) { case ClassDef(mods, _, _, _) if mods.isTrait => continue(tree) case DefDef(_, _, _, _, _, _) if tree.symbol != null && tree.symbol.enclClass.isTrait => case DefDef(_, _, _, _, _, Literal(Constant(()))) => - context.warn(tree.pos, self, "Empty method statement " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptySynchronizedBlock.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptySynchronizedBlock.scala index dd8350ef..2b9b2481 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptySynchronizedBlock.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptySynchronizedBlock.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.empty import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class EmptySynchronizedBlock extends Inspection("Empty synchronized block", Levels.Warning) { +class EmptySynchronizedBlock extends Inspection( + text = "Empty synchronized block", + defaultLevel = Levels.Warning, + description = "Checks for empty synchronized blocks.", + explanation = "An empty synchronized block is considered as dead code." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -15,10 +20,10 @@ class EmptySynchronizedBlock extends Inspection("Empty synchronized block", Leve override def inspect(tree: Tree): Unit = { tree match { case Apply(TypeApply(Select(_, Sync), _), List(Literal(Constant(())))) => - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyTryBlock.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyTryBlock.scala index 58a9d84b..2b383d49 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyTryBlock.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyTryBlock.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.empty import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class EmptyTryBlock extends Inspection("Empty try block", Levels.Warning) { +class EmptyTryBlock extends Inspection( + text = "Empty try block", + defaultLevel = Levels.Warning, + description = "Checks for empty try blocks.", + explanation = "An empty try block is considered as dead code." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,10 +18,10 @@ class EmptyTryBlock extends Inspection("Empty try block", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case Try(Literal(Constant(())), _, _) => - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyWhileBlock.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyWhileBlock.scala index 6ce4b791..33dddb0f 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyWhileBlock.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyWhileBlock.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.empty import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class EmptyWhileBlock extends Inspection("Empty while block", Levels.Warning) { +class EmptyWhileBlock extends Inspection( + text = "Empty while block", + defaultLevel = Levels.Warning, + description = "Checks for empty while blocks.", + explanation = "An empty while block is considered as dead code." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,10 +18,10 @@ class EmptyWhileBlock extends Inspection("Empty while block", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case LabelDef(_, _, If(_, Block(List(Literal(Constant(()))), _), _)) => - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingFloatingPointTypes.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingFloatingPointTypes.scala index 1617c8b3..d9961d32 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingFloatingPointTypes.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingFloatingPointTypes.scala @@ -4,7 +4,11 @@ import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ class ComparingFloatingPointTypes extends Inspection( - "Floating type comparison", Levels.Error) { + text = "Floating type comparison", + defaultLevel = Levels.Error, + description = "Checks for equality checks on floating point types.", + explanation = "Due to minor rounding errors, it is not advisable to compare floating-point numbers using the == operator. Either use a threshold based comparison, or switch to a BigDecimal." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingUnrelatedTypes.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingUnrelatedTypes.scala index d7d48334..cda5ea69 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingUnrelatedTypes.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingUnrelatedTypes.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.equality import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class ComparingUnrelatedTypes extends Inspection("Comparing unrelated types", Levels.Error) { +class ComparingUnrelatedTypes extends Inspection( + text = "Comparing unrelated types", + defaultLevel = Levels.Error, + description = "Checks for equality comparisons that cannot succeed.", + explanation = "In most case comparing unrelated types cannot succeed and it's usually an indication of a bug." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -59,7 +64,7 @@ class ComparingUnrelatedTypes extends Inspection("Comparing unrelated types", Le } if (!hasSpecificEq(lhs.tpe.deconst) && !related(lhs.tpe.widen, rhs.tpe.widen)) { - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) } case _ => continue(tree) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparisonWithSelf.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparisonWithSelf.scala index ff174164..f15ed6ee 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparisonWithSelf.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparisonWithSelf.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.equality import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class ComparisonWithSelf extends Inspection("Comparision with self", Levels.Warning, - "Comparision with self will always yield true") { +class ComparisonWithSelf extends Inspection( + text = "Comparision with self", + defaultLevel = Levels.Warning, + description = "Checks for equality checks with itself.", + explanation = "Comparison with self will always yield true." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -15,10 +19,10 @@ class ComparisonWithSelf extends Inspection("Comparision with self", Levels.Warn tree match { case Apply(Select(left, TermName("$eq$eq" | "$bang$eq")), List(right)) => if (left.toString() == right.toString()) - context.warn(tree.pos,self) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchException.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchException.scala index c666bb4a..343cd708 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchException.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchException.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.exception import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Marconi Lanna */ -class CatchException extends Inspection("Catch exception", Levels.Warning) { +class CatchException extends Inspection( + text = "Catch exception", + defaultLevel = Levels.Warning, + description = "Checks for try blocks that catch exception.", + explanation = "Did you intend to catch all exceptions? Consider catching a more specific exception class." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -23,9 +28,7 @@ class CatchException extends Inspection("Catch exception", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case Try(_, cases, _) if catchesException(cases) => - context.warn(tree.pos, self, - "Did you intend to catch all exceptions? Consider catching a more specific exception class: " + - tree.toString().take(300)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchFatal.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchFatal.scala index 6f7bb625..e654f684 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchFatal.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchFatal.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.util.control.ControlThrowable /** @author Marconi Lanna */ -class CatchFatal extends Inspection("Catch fatal exception", Levels.Warning) { +class CatchFatal extends Inspection( + text = "Catch fatal exception", + defaultLevel = Levels.Warning, + description = "Checks for try blocks that catch fatal exceptions: VirtualMachineError, ThreadDeath, InterruptedException, LinkageError, ControlThrowable.", + explanation = "Did you intend to catch a fatal exception? Consider using scala.util.control.NonFatal instead." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -33,9 +38,7 @@ class CatchFatal extends Inspection("Catch fatal exception", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case Try(_, cases, _) if catchesFatal(cases) => - context.warn(tree.pos, self, - "Did you intend to catch a fatal exception? Consider using scala.util.control.NonFatal: " + - tree.toString().take(300)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchNpe.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchNpe.scala index 794eb3a0..fdf031c0 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchNpe.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchNpe.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.exception import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class CatchNpe extends Inspection("Catching NPE", Levels.Error) { - +class CatchNpe extends Inspection( + text = "Catching NPE", + defaultLevel = Levels.Error, + description = "Checks for try blocks that catch null pointer exceptions.", + explanation = "Avoid using null at all cost and you shouldn't need to catch NullPointerExceptions. Prefer Option to indicate potentially missing values and use Try to materialize exceptions thrown by any external libraries." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchThrowable.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchThrowable.scala index b8b6e746..288846a3 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchThrowable.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchThrowable.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.exception import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class CatchThrowable extends Inspection("Catch throwable", Levels.Warning) { +class CatchThrowable extends Inspection( + text = "Catch throwable", + defaultLevel = Levels.Warning, + description = "Checks for try blocks that catch Throwable.", + explanation = "Did you intend to catch all throwables? Consider catching a more specific exception class." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -23,9 +28,7 @@ class CatchThrowable extends Inspection("Catch throwable", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case Try(_, cases, _) if catchesThrowable(cases) => - context.warn(tree.pos, self, - "Did you intend to catch all throwables? Consider catching a more specific exception class: " + - tree.toString().take(300)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CaughtExceptionImmediatelyRethrown.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CaughtExceptionImmediatelyRethrown.scala deleted file mode 100644 index a3f2ee37..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CaughtExceptionImmediatelyRethrown.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.sksamuel.scapegoat.inspections.exception - -class CaughtExceptionImmediatelyRethrown { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/IncorrectlyNamedExceptions.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/IncorrectlyNamedExceptions.scala index bb2ce0eb..d16daa2a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/IncorrectlyNamedExceptions.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/IncorrectlyNamedExceptions.scala @@ -7,7 +7,12 @@ import com.sksamuel.scapegoat._ * * Inspired by http://findbugs.sourceforge.net/bugDescriptions.html#NM_CLASS_NOT_EXCEPTION */ -class IncorrectlyNamedExceptions extends Inspection("Incorrectly Named Exceptions", Levels.Error) { +class IncorrectlyNamedExceptions extends Inspection( + text = "Incorrectly named exceptions", + defaultLevel = Levels.Error, + description = "Checks for exceptions that are not called *Exception and vice versa.", + explanation = "Class named exception does not derive from Exception / class derived from Exception is not named *Exception." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -36,11 +41,9 @@ class IncorrectlyNamedExceptions extends Inspection("Incorrectly Named Exception (isNamedException, isAnon, isException) match { case (true, _, false) => - context.warn(tree.pos, self, - "Class named exception does not derive from Exception" + tree.toString().take(500)) + context.warn(tree.pos, self) case (false, false, true) => - context.warn(tree.pos, self, - "Class derived from Exception is not named *Exception" + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => } case _ => @@ -49,4 +52,4 @@ class IncorrectlyNamedExceptions extends Inspection("Incorrectly Named Exception } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/ProhibitedExceptionThrown.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/ProhibitedExceptionThrown.scala deleted file mode 100644 index 1efbd3fc..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/ProhibitedExceptionThrown.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.sksamuel.scapegoat.inspections.exception - -class ProhibitedExceptionThrown { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/SwallowedException.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/SwallowedException.scala index 5e296e2f..c7eb359b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/SwallowedException.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/SwallowedException.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.exception import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class SwallowedException extends Inspection("Empty catch block", Levels.Warning) { +class SwallowedException extends Inspection( + text = "Empty catch block", + defaultLevel = Levels.Warning, + description = "Finds catch blocks that don't handle caught exceptions.", + explanation = "If you use a try/catch block to deal with an exception, you should handle all of the caught exceptions and if for some reason you're throwing another exception in the result, you should include the original exception as the cause." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -24,16 +29,12 @@ class SwallowedException extends Inspection("Empty catch block", Levels.Warning) private def checkCatches(defs: List[CaseDef]) = defs.foreach { case CaseDef(Bind(TermName("ignored") | TermName("ignore"), _), _, _) => - case cdef @ CaseDef(_, _, Literal(Constant(()))) if cdef.body.toString == "()" => - context.warn(cdef.pos, self, "Empty catch block " + cdef.toString().take(100)) - + context.warn(cdef.pos, self) case cdef @ CaseDef(Bind(caughtException, _), _, subtree) if containsMaskingThrow(caughtException, Seq(subtree)) => - context.warn(cdef.pos, self, - "Masking exception by not passing the original exception as cause: " + cdef.toString().take(100)) - + context.warn(cdef.pos, self) case _ => } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/UnreachableCatch.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/UnreachableCatch.scala index c392f450..06ee1a11 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/UnreachableCatch.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/UnreachableCatch.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.collection.mutable /** @author Stephen Samuel */ -class UnreachableCatch extends Inspection("Unreachable catch", Levels.Warning) { +class UnreachableCatch extends Inspection( + text = "Unreachable catch", + defaultLevel = Levels.Warning, + description = "Checks for catch clauses that cannot be reached.", + explanation = "One or more cases are unreachable." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -35,8 +40,7 @@ class UnreachableCatch extends Inspection("Unreachable catch", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case Try(_, cases, _) if isUnreachable(cases) => - context.warn(tree.pos, self, - "One or more cases are unreachable " + tree.toString().take(300)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/imports/DuplicateImport.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/imports/DuplicateImport.scala index f443615f..6f89aeef 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/imports/DuplicateImport.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/imports/DuplicateImport.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.collection.mutable /** @author Stephen Samuel */ -class DuplicateImport extends Inspection("Duplicated Import", Levels.Info) { +class DuplicateImport extends Inspection( + text = "Duplicate import", + defaultLevel = Levels.Info, + description = "Checks for duplicate import statements.", + explanation = "Duplicate imports should be removed." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { @@ -27,7 +32,7 @@ class DuplicateImport extends Inspection("Duplicated Import", Levels.Info) { selectors.foreach(selector => { val name = expr.toString + "." + selector.name if (imports.contains(name)) { - context.warn(tree.pos, self, name) + context.warn(tree.pos, self) } imports.add(name) }) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/imports/WildcardImport.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/imports/WildcardImport.scala index 2c5b60ee..39f42181 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/imports/WildcardImport.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/imports/WildcardImport.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.imports import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class WildcardImport extends Inspection("Wildcard import", Levels.Warning) { +class WildcardImport extends Inspection( + text = "Wildcard imports", + defaultLevel = Levels.Warning, + description = "Checks for wildcard imports.", + explanation = "Avoid using wildcard imports, unless you are importing more than a few entities. Wildcard imports make the code more difficult to maintain." +) { override def inspector(context: InspectionContext): Inspector = new Inspector(context) { @@ -16,7 +21,7 @@ class WildcardImport extends Inspection("Wildcard import", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case Import(_, selector) if isWildcard(selector) => - context.warn(tree.pos, self, "Wildcard import used: " + tree.toString()) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/BoundedByFinalType.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/BoundedByFinalType.scala index 7e37f2d5..db7a8251 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/BoundedByFinalType.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/BoundedByFinalType.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.inference import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class BoundedByFinalType extends Inspection("Bounded by final type", Levels.Warning) { +class BoundedByFinalType extends Inspection( + text = "Bounded by a final type", + defaultLevel = Levels.Warning, + description = "Checks for types with upper bounds of a final type.", + explanation = "Pointless type bound. Type parameter can only be a single value." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -18,10 +23,7 @@ class BoundedByFinalType extends Inspection("Bounded by final type", Levels.Warn case TypeDef(_, _, _, typeTree: TypeTree) => typeTree.original match { case TypeBoundsTree(lo, hi) if lo.tpe.isFinalType && hi.tpe.isFinalType => - context.warn(tree.pos, self, - "Pointless type bound. Type parameter can only be a single value: " + tree - .toString() - .take(300)) + context.warn(tree.pos, self) case _ => } case _ => continue(tree) @@ -29,4 +31,4 @@ class BoundedByFinalType extends Inspection("Bounded by final type", Levels.Warn } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/MethodReturningAny.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/MethodReturningAny.scala index a4cced21..ca525445 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/MethodReturningAny.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/MethodReturningAny.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.reflect.internal.Flags /** @author Stephen Samuel */ -class MethodReturningAny extends Inspection("Method Returning Any", Levels.Warning) { +class MethodReturningAny extends Inspection( + text = "Method returning Any", + defaultLevel = Levels.Warning, + description = "Checks for functions that are defined or inferred to return Any.", + explanation = "Method returns Any. Consider using a more specialized type." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -21,8 +26,7 @@ class MethodReturningAny extends Inspection("Method Returning Any", Levels.Warni /// ignore overridden methods as the parent will receive the warning case DefDef(mods, _, _, _, _, _) if mods.isOverride => case DefDef(_, _, _, _, tpt, _) if tpt.tpe =:= typeOf[Any] || tpt.tpe =:= typeOf[AnyRef] => - context.warn(tree.pos, self, - "Method returns Any. Consider using a more specialized type: " + tree.toString().take(300)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/PointlessTypeBounds.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/PointlessTypeBounds.scala index 43907c1e..c7257225 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/PointlessTypeBounds.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/PointlessTypeBounds.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.inference import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class PointlessTypeBounds extends Inspection("Pointless Type Bounds", Levels.Warning) { +class PointlessTypeBounds extends Inspection( + text = "Pointless type bounds", + defaultLevel = Levels.Warning, + description = "Finds type bounds of the form [A <: Any] or [A >: Nothing].", + explanation = "Type bound resolves to Nothing <: T <: Any. Did you mean to put in other bounds?" +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -15,9 +20,7 @@ class PointlessTypeBounds extends Inspection("Pointless Type Bounds", Levels.War case TypeDef(_, _, _, rhs) if rhs.tpe.bounds.isEmptyBounds && rhs.pos != null && (rhs.pos.lineContent.contains("<: Any") || rhs.pos.lineContent.contains(">: Nothing")) => - context.warn(tree.pos, self, - "Type bound resolves to Nothing <: T <: Any. Did you mean to put in other bounds: " + - tree.toString().take(300)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/ProductWithSerializableInferred.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/ProductWithSerializableInferred.scala index ca8b679a..cb0e9110 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/ProductWithSerializableInferred.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/ProductWithSerializableInferred.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.reflect.internal.Flags /** @author Stephen Samuel */ -class ProductWithSerializableInferred extends Inspection("Product with Serializable inferred", Levels.Warning) { +class ProductWithSerializableInferred extends Inspection( + text = "Product with Serializable inferred", + defaultLevel = Levels.Warning, + description = "Checks for values that have Product with Serializable as their inferred type.", + explanation = "It is unlikely that Product with Serializable was your target type. This is often an indication of mixing up incompatible types." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -29,8 +34,7 @@ class ProductWithSerializableInferred extends Inspection("Product with Serializa tree match { case ValDef(mods, _, _, _) if mods.hasFlag(Flags.SYNTHETIC) => case ValDef(_, _, tpt, _) if isProductWithSerializable(tpt.tpe) => - context.warn(tree.pos, self, - "It is unlikely that this was your target type: " + tree.toString().take(300)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/FruitlessMatch.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/FruitlessMatch.scala deleted file mode 100644 index 62880c4e..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/FruitlessMatch.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.sksamuel.scapegoat.inspections.matching - -class FruitlessMatch { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/PartialFunctionInsteadOfMatch.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/PartialFunctionInsteadOfMatch.scala index b6765ac8..04d6eaae 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/PartialFunctionInsteadOfMatch.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/PartialFunctionInsteadOfMatch.scala @@ -3,18 +3,18 @@ package com.sksamuel.scapegoat.inspections.matching import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class PartialFunctionInsteadOfMatch extends Inspection("Match instead of partial function", Levels.Info) { +class PartialFunctionInsteadOfMatch extends Inspection( + text = "Match instead of a partial function", + defaultLevel = Levels.Info, + description = "Warns when you could use a partial function directly instead of a match block.", + explanation = "A map match can be replaced with a partial function for greater readability." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { import context.global._ - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self, - "A map match can be replaced with a partial function for greater readability: " + tree.toString().take(500)) - } - private def isPFBind(name: TermName) = { val b = name.toString.matches("x0\\$\\d+") b @@ -24,13 +24,19 @@ class PartialFunctionInsteadOfMatch extends Inspection("Match instead of partial tree match { // _ match { case ...; case ... } // need to not warn on the partial function style, they use x0$1 - case Apply(_, List(Function(List(ValDef(_, name1, _, EmptyTree)), Match(name2, _)))) if name1.toString == name2.toString() => if (!isPFBind(name1)) warn(tree) - case Apply(TypeApply(_, _), List(Function(List(ValDef(_, name1, _, EmptyTree)), Match(name2, _)))) if name1.toString == name2.toString() => if (!isPFBind(name1)) warn(tree) - case TypeApply(_, List(Function(List(ValDef(_, name1, _, EmptyTree)), Match(name2, _)))) if name1.toString == name2.toString() => if (!isPFBind(name1)) warn(tree) + case Apply(_, List(Function(List(ValDef(_, name1, _, EmptyTree)), Match(name2, _)))) + if name1.toString == name2.toString() => + if (!isPFBind(name1)) context.warn(tree.pos, self) + case Apply(TypeApply(_, _), List(Function(List(ValDef(_, name1, _, EmptyTree)), Match(name2, _)))) + if name1.toString == name2.toString() => + if (!isPFBind(name1)) context.warn(tree.pos, self) + case TypeApply(_, List(Function(List(ValDef(_, name1, _, EmptyTree)), Match(name2, _)))) + if name1.toString == name2.toString() => + if (!isPFBind(name1)) context.warn(tree.pos, self) // a => a match { case ...; case ... } // case Apply(_, List(Function(List(ValDef(mods, x1, TypeTree(), EmptyTree)), Match(x2, _)))) // if x1.toString == x2.toString() => - // warn(tree) + // context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/RepeatedCaseBody.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/RepeatedCaseBody.scala index 81499be7..c8146a3b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/RepeatedCaseBody.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/RepeatedCaseBody.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.collection.mutable /** @author Stephen Samuel */ -class RepeatedCaseBody extends Inspection("Repeated case body", Levels.Warning) { +class RepeatedCaseBody extends Inspection( + text = "Repeated case body", + defaultLevel = Levels.Warning, + description = "Checks for case statements which have the same body.", + explanation = "Case body is repeated. Consider merging pattern clauses together." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -24,11 +29,10 @@ class RepeatedCaseBody extends Inspection("Repeated case body", Levels.Warning) override def inspect(tree: Tree): Unit = { tree match { case Match(_, cases) if isRepeated(cases) => - context.warn(tree.pos, self, - "Case body is repeated. Consider merging pattern clauses together: " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/SuspiciousMatchOnClassObject.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/SuspiciousMatchOnClassObject.scala index 371dc479..68cf42d5 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/SuspiciousMatchOnClassObject.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/SuspiciousMatchOnClassObject.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.matching import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class SuspiciousMatchOnClassObject extends Inspection("Suspicious match on class object", Levels.Warning) { +class SuspiciousMatchOnClassObject extends Inspection( + text = "Suspicious match on class object", + defaultLevel = Levels.Warning, + description = "Checks for code where matching is taking place on class literals.", + explanation = "Matching on an companion object of a case class is probably not what you intended." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -26,15 +31,11 @@ class SuspiciousMatchOnClassObject extends Inspection("Suspicious match on class pat.symbol.isModuleOrModuleClass && pat.tpe.typeSymbol.companionClass.isClass && !pat.tpe.typeSymbol.companionClass.isAbstractClass => - warn(c) - true + context.warn(c.pos, self) + true case _ => false } } - - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self, tree.toString().take(500)) - } } } -} \ No newline at end of file +} From 93856f6f6fb59c5d191b60b2c06c344f7898cc5b Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Sun, 8 Mar 2020 19:26:05 +0000 Subject: [PATCH 04/18] Add description to the math inspections. --- .../math/BigDecimalDoubleConstructor.scala | 30 ++++++++++--------- .../BigDecimalScaleWithoutRoundingMode.scala | 19 ++++++------ .../inspections/math/BrokenOddness.scala | 13 ++++---- .../inspections/math/DivideByOne.scala | 13 +++++--- .../math/IntDivisionAssignedToFloat.scala | 6 ---- .../scapegoat/inspections/math/ModOne.scala | 13 +++++--- .../inspections/math/NanComparison.scala | 24 ++++++++------- .../scapegoat/inspections/math/UseCbrt.scala | 11 ++++--- .../scapegoat/inspections/math/UseExpM1.scala | 12 ++++---- .../scapegoat/inspections/math/UseLog10.scala | 11 ++++--- .../scapegoat/inspections/math/UseLog1P.scala | 23 +++++++------- .../scapegoat/inspections/math/UseSqrt.scala | 14 +++++---- .../inspections/math/ZeroNumerator.scala | 8 +++-- 13 files changed, 112 insertions(+), 85 deletions(-) delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/math/IntDivisionAssignedToFloat.scala diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalDoubleConstructor.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalDoubleConstructor.scala index 9cb0137e..2c3ecb68 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalDoubleConstructor.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalDoubleConstructor.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.math import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class BigDecimalDoubleConstructor extends Inspection("Big decimal double constructor", Levels.Warning) { +class BigDecimalDoubleConstructor extends Inspection( + text = "Big decimal double constructor", + defaultLevel = Levels.Warning, + description = "Checks for use of BigDecimal(double) which can be unsafe.", + explanation = "The results of this constructor can be somewhat unpredictable. E.g. writing new BigDecimal(0.1) in Java creates a BigDecimal which is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,24 +18,21 @@ class BigDecimalDoubleConstructor extends Inspection("Big decimal double constru private def isBigDecimal(pack: Tree) = pack.toString == "scala.`package`.BigDecimal" || pack.toString == "java.math.BigDecimal" - private def isFloatingPointType(tree: Tree) = tree.tpe <:< FloatClass.tpe || tree.tpe <:< DoubleClass.tpe - - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self, - "The results of this constructor can be somewhat unpredictable. " + - "Eg, writing new BigDecimal(0.1) in Java creates a BigDecimal which is actually equal to 0.1000000000000000055511151231257827021181583404541015625. " + - "This is because 0.1 cannot be represented exactly as a double. " + tree.toString().take(100)) - } + + private def isFloatingPointType(tree: Tree) = + tree.tpe <:< FloatClass.tpe || tree.tpe <:< DoubleClass.tpe override def inspect(tree: Tree): Unit = { tree match { - case Apply(Select(pack, TermName("apply")), arg :: _) if isBigDecimal(pack) && isFloatingPointType(arg) => - warn(tree) - case Apply(Select(New(pack), nme.CONSTRUCTOR), arg :: _) if isBigDecimal(pack) && isFloatingPointType(arg) => - warn(tree) + case Apply(Select(pack, TermName("apply")), arg :: _) + if isBigDecimal(pack) && isFloatingPointType(arg) => + context.warn(tree.pos, self) + case Apply(Select(New(pack), nme.CONSTRUCTOR), arg :: _) + if isBigDecimal(pack) && isFloatingPointType(arg) => + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalScaleWithoutRoundingMode.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalScaleWithoutRoundingMode.scala index 9b8686b2..d163ad2b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalScaleWithoutRoundingMode.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalScaleWithoutRoundingMode.scala @@ -4,26 +4,27 @@ import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ class BigDecimalScaleWithoutRoundingMode extends Inspection( - "BigDecimal setScale() without rounding mode", Levels.Warning, - "When using setScale() on a BigDecimal without setting the rounding mode, this can throw an exception if rounding is required. Did you mean to call setScale(s, RoundingMode.XYZ)") { + text = "BigDecimal setScale() without rounding mode", + defaultLevel = Levels.Warning, + description = "Checks for use of setScale() on a BigDecimal without setting the rounding mode can throw an exception.", + explanation = "When using setScale() on a BigDecimal without setting the rounding mode, this can throw an exception if rounding is required. Did you mean to call setScale(s, RoundingMode.XYZ)?" +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { import context.global._ - private def isBigDecimal(t: Tree) = t.tpe <:< typeOf[BigDecimal] || t.tpe <:< typeOf[java.math.BigDecimal] - - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self) - } + private def isBigDecimal(t: Tree) = + t.tpe <:< typeOf[BigDecimal] || t.tpe <:< typeOf[java.math.BigDecimal] override def inspect(tree: Tree): Unit = { tree match { - case Apply(Select(lhs, TermName("setScale")), List(_)) if isBigDecimal(lhs) => warn(tree) + case Apply(Select(lhs, TermName("setScale")), List(_)) if isBigDecimal(lhs) => + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/BrokenOddness.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/BrokenOddness.scala index 93f54cc9..8dce6646 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/BrokenOddness.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/BrokenOddness.scala @@ -7,7 +7,12 @@ import com.sksamuel.scapegoat._ * * Inspired by http://codenarc.sourceforge.net/codenarc-rules-basic.html#BrokenOddnessCheck */ -class BrokenOddness extends Inspection("Broken odd check", Levels.Warning) { +class BrokenOddness extends Inspection( + text = "Broken odd check", + defaultLevel = Levels.Warning, + description = "Checks for potentially broken odd checks.", + explanation = "Code that attempts to check for oddness using x % 2 == 1 will fail on negative numbers. Consider using x % 2 != 0" +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -18,12 +23,10 @@ class BrokenOddness extends Inspection("Broken odd check", Levels.Warning) { tree match { case Apply(Select(Apply(Select(_, TermName("$percent")), List(Literal(Constant(2)))), TermName("$eq$eq")), List(Literal(Constant(1)))) => - context.warn(tree.pos, self, - "Potentially broken odd check. " + tree.toString().take(500) + "." + - "Code that attempts to check for oddness using x % 2 == 1 will fail on negative numbers. Consider using x % 2 != 0") + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/DivideByOne.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/DivideByOne.scala index 9220c7f8..f34e1603 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/DivideByOne.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/DivideByOne.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.math import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class DivideByOne extends Inspection("Divide by one", Levels.Warning, - "Divide by one will always return the original value") { +class DivideByOne extends Inspection( + text = "Divide by one", + defaultLevel = Levels.Warning, + description = "Checks for division by one.", + explanation = "Divide by one will always return the original value." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -25,8 +29,9 @@ class DivideByOne extends Inspection("Divide by one", Levels.Warning, override def inspect(tree: Tree): Unit = { tree match { - case Apply(Select(lhs, TermName("$div")), List(Literal(Constant(x)))) if isNumber(lhs) && isOne(x) => - context.warn(tree.pos, self) + case Apply(Select(lhs, TermName("$div")), List(Literal(Constant(x)))) + if isNumber(lhs) && isOne(x) => + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/IntDivisionAssignedToFloat.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/IntDivisionAssignedToFloat.scala deleted file mode 100644 index eda5f72b..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/IntDivisionAssignedToFloat.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections.math - -/** @author Stephen Samuel */ -class IntDivisionAssignedToFloat { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/ModOne.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/ModOne.scala index 107f7aea..a067002a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/ModOne.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/ModOne.scala @@ -7,7 +7,12 @@ import com.sksamuel.scapegoat._ * * Inspired by http://findbugs.sourceforge.net/bugDescriptions.html#INT_BAD_REM_BY_1 */ -class ModOne extends Inspection("Integer mod one", Levels.Warning) { +class ModOne extends Inspection( + text = "Integer mod one", + defaultLevel = Levels.Warning, + description = "Checks for expressions like x % 1.", + explanation = "Any expression x % 1 will always return 0." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,9 +21,9 @@ class ModOne extends Inspection("Integer mod one", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { - case Apply(Select(lhs, TermName("$percent")), List(Literal(Constant(1)))) if lhs.tpe <:< typeOf[Int] => - context.warn(tree.pos, self, - "Any expression x % 1 will always return 0. " + tree.toString().take(300)) + case Apply(Select(lhs, TermName("$percent")), List(Literal(Constant(1)))) + if lhs.tpe <:< typeOf[Int] => + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/NanComparison.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/NanComparison.scala index 83a09961..ebaa96e8 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/NanComparison.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/NanComparison.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.math import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class NanComparison extends Inspection("Nan comparision", Levels.Error, - "NaN comparision will always fail. Use value.isNan instead.") { +class NanComparison extends Inspection( + text = "Nan comparison", + defaultLevel = Levels.Error, + description = "Checks for x == Double.NaN which will always fail.", + explanation = "NaN comparison will always fail. Use value.isNan instead." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -21,10 +25,12 @@ class NanComparison extends Inspection("Nan comparision", Levels.Error, override def inspect(tree: Tree): Unit = { tree match { - case Apply(Select(lhs, TermName("$eq$eq")), List(Literal(Constant(x)))) if isFloatingPointType(lhs) && isNan(x) => - warn(tree) - case Apply(Select(Literal(Constant(x)), TermName("$eq$eq")), List(rhs)) if isFloatingPointType(rhs) && isNan(x) => - warn(tree) + case Apply(Select(lhs, TermName("$eq$eq")), List(Literal(Constant(x)))) + if isFloatingPointType(lhs) && isNan(x) => + context.warn(tree.pos, self) + case Apply(Select(Literal(Constant(x)), TermName("$eq$eq")), List(rhs)) + if isFloatingPointType(rhs) && isNan(x) => + context.warn(tree.pos, self) case _ => continue(tree) } } @@ -32,10 +38,6 @@ class NanComparison extends Inspection("Nan comparision", Levels.Error, private def isFloatingPointType(lhs: Tree): Boolean = { lhs.tpe <:< DoubleClass.tpe || lhs.tpe <:< FloatClass.tpe } - - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self) - } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseCbrt.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseCbrt.scala index c7988125..55c07360 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseCbrt.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseCbrt.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.math import com.sksamuel.scapegoat._ /** @author Matic Potočnik */ -class UseCbrt extends Inspection("Use cbrt", Levels.Info) { +class UseCbrt extends Inspection( + text = "Use cbrt", + defaultLevel = Levels.Info, + description = "Checks for use of math.pow for calculating math.cbrt.", + explanation = "Use math.cbrt, which is clearer and more performant than math.pow(x, 1/3)." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -18,9 +23,7 @@ class UseCbrt extends Inspection("Use cbrt", Levels.Info) { || pack.symbol.fullNameString == "java.lang.StrictMath") && third >= 0.3333332 && third <= 0.3333334 => - val math = pack.symbol.fullNameString.stripSuffix(".package").substring(pack.symbol.fullNameString.lastIndexOf('.')) - context.warn(tree.pos, self, - s"$math.cbrt is clearer and more performant than $math.pow(x, 1/3)") + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseExpM1.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseExpM1.scala index 907c93ea..968b277b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseExpM1.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseExpM1.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.math import com.sksamuel.scapegoat._ /** @author Matic Potočnik */ -class UseExpM1 extends Inspection("Use expm1", Levels.Info) { +class UseExpM1 extends Inspection( + text = "Use expm1", + defaultLevel = Levels.Info, + description = "Checks for use of math.exp(x) - 1 instead of math.expm1(x).", + explanation = "Use math.expm1(x), which is clearer and more performant than math.exp(x) - 1." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,10 +18,7 @@ class UseExpM1 extends Inspection("Use expm1", Levels.Info) { override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(Apply(Select(pack, TermName("exp")), List(_)), nme.SUB), List(Literal(Constant(1)))) => - val math = pack.toString().stripSuffix(".`package`").substring(pack.toString().lastIndexOf('.')) - context.warn(tree.pos, self, - s"$math.expm1(x) is clearer and more performant than $math.exp(x) - 1") - + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseLog10.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseLog10.scala index 69eccfff..d4cbe27a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseLog10.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseLog10.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections.math import com.sksamuel.scapegoat._ -class UseLog10 extends Inspection("Use log10", Levels.Info) { +class UseLog10 extends Inspection( + text = "Use log10", + defaultLevel = Levels.Info, + description = "Checks for use of math.log(x)/math.log(10) instead of math.log10(x).", + explanation = "Use math.log10(x), which is clearer and more performant than $math.log(x)/$math.log(10)." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -19,9 +24,7 @@ class UseLog10 extends Inspection("Use log10", Levels.Info) { tree match { case Apply(Select(Apply(Select(pack1, TermName("log")), List(_)), nme.DIV), List(Apply(Select(pack2, TermName("log")), List(Literal(Constant(10.0)))))) if isMathPackage(pack1.symbol.fullName) && isMathPackage(pack2.symbol.fullName) => - val math = pack1.toString().stripSuffix(".package").substring(pack2.toString().lastIndexOf('.')) - context.warn(tree.pos, self, - s"$math.log10(x) is clearer and more performant than $math.log(x)/$math.log(10)") + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseLog1P.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseLog1P.scala index 1f7d1f95..98cfe39b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseLog1P.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseLog1P.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections.math import com.sksamuel.scapegoat._ -class UseLog1P extends Inspection("Use log1p", Levels.Info) { +class UseLog1P extends Inspection( + text = "Use log1p", + defaultLevel = Levels.Info, + description = "Checks for use of math.log(x + 1) instead of math.log1p(x)", + explanation = "Use math.log1p(x) is clearer and more performant than $math.log(1 + x)." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,16 +21,12 @@ class UseLog1P extends Inspection("Use log1p", Levels.Info) { override def inspect(tree: Tree): Unit = { tree match { - case Apply(Select(pack, TermName("log")), List(Apply(Select(Literal(Constant(1)), nme.ADD), _))) if isMathPackage(pack.symbol.fullName) => - val math = pack.toString().stripSuffix(".`package`").substring(pack.toString().lastIndexOf('.')) - context.warn(tree.pos, self, - s"$math.log1p(x) is clearer and more performant than $math.log(1 + x)") - - case Apply(Select(pack, TermName("log")), List(Apply(Select(_, nme.ADD), List(Literal(Constant(1)))))) if isMathPackage(pack.symbol.fullName) => - val math = pack.toString().stripSuffix(".`package`").substring(pack.toString().lastIndexOf('.')) - context.warn(tree.pos, self, - s"$math.log1p(x) is clearer and more performant than $math.log(x + 1)") - + case Apply(Select(pack, TermName("log")), List(Apply(Select(Literal(Constant(1)), nme.ADD), _))) + if isMathPackage(pack.symbol.fullName) => + context.warn(tree.pos, self) + case Apply(Select(pack, TermName("log")), List(Apply(Select(_, nme.ADD), List(Literal(Constant(1)))))) + if isMathPackage(pack.symbol.fullName) => + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseSqrt.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseSqrt.scala index ce52bc4d..3a26f7c6 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseSqrt.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/UseSqrt.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections.math import com.sksamuel.scapegoat._ -class UseSqrt extends Inspection("Use sqrt", Levels.Info) { +class UseSqrt extends Inspection( + text = "Use sqrt", + defaultLevel = Levels.Info, + description = "Checks for use of math.pow for calculating math.sqrt.", + explanation = "Use math.sqrt, which is clearer and more performant than $math.pow(x, 0.5)." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { @@ -15,11 +20,8 @@ class UseSqrt extends Inspection("Use sqrt", Levels.Info) { case Apply(Select(pack, TermName("pow")), List(_, Literal(Constant(0.5d)))) if pack.symbol.fullNameString == "scala.math.package" || pack.symbol.fullNameString == "java.lang.StrictMath" - || pack.symbol.fullNameString == "java.lang.Math" - => - val math = pack.toString().stripPrefix("java.lang.").stripPrefix("scala.").stripSuffix(".`package`") - context.warn(tree.pos, self, - s"$math.sqrt is clearer and more performant than $math.pow(x, 0.5)") + || pack.symbol.fullNameString == "java.lang.Math" => + context.warn(tree.pos, self) case _ => continue(tree) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/ZeroNumerator.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/ZeroNumerator.scala index b0ab87fb..af67b558 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/ZeroNumerator.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/ZeroNumerator.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.math import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class ZeroNumerator extends Inspection("Zero numerator", Levels.Warning, - "Dividing zero by any number will always return zero") { +class ZeroNumerator extends Inspection( + text = "Zero numerator", + defaultLevel = Levels.Warning, + description = "Checks for dividing 0 by a number.", + explanation = "Dividing zero by any number will always return zero, e.g. 0 / x == 0." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { From 7b46f766cf8d7901456a90f18188d3551c749714 Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Sun, 8 Mar 2020 22:55:53 +0000 Subject: [PATCH 05/18] Add description to naming, nulls, option and string inspections. --- .../inspections/naming/ClassNames.scala | 10 ++++++--- .../inspections/naming/FieldNames.scala | 6 ------ .../inspections/naming/MethodNames.scala | 8 +++++-- .../inspections/naming/ObjectNames.scala | 9 ++++++-- .../inspections/naming/VariableNames.scala | 6 ------ .../inspections/nulls/NullAssignment.scala | 21 +++++++++++-------- .../inspections/nulls/NullParameter.scala | 14 +++++++------ .../inspections/option/EitherGet.scala | 9 ++++++-- .../ImpossibleOptionSizeCondition.scala | 10 ++++++--- .../option/InexhausticOptionMatch.scala | 6 ------ .../inspections/option/OptionComparison.scala | 6 ------ .../inspections/option/OptionGet.scala | 9 ++++++-- .../inspections/option/OptionHead.scala | 6 ------ .../inspections/option/OptionSize.scala | 11 +++++++--- .../inspections/string/ArraysInFormat.scala | 9 ++++++-- .../inspections/string/ArraysToString.scala | 12 +++++++---- .../string/EmptyInterpolatedString.scala | 13 +++++++----- .../string/IllegalFormatString.scala | 12 +++++++---- .../IncorrectNumberOfArgsToFormat.scala | 16 +++++++++++--- .../inspections/string/InvalidRegex.scala | 11 +++++++--- .../string/LooksLikeInterpolatedString.scala | 9 ++++++-- .../string/NegativeStringMultiplication.scala | 5 ----- .../string/StripMarginOnRegex.scala | 10 ++++++--- .../inspections/string/SubstringZero.scala | 10 ++++++--- .../string/UnsafeStringContains.scala | 17 ++++++++++----- 25 files changed, 154 insertions(+), 101 deletions(-) delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/naming/FieldNames.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/naming/VariableNames.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/option/InexhausticOptionMatch.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionComparison.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionHead.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/string/NegativeStringMultiplication.scala diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/naming/ClassNames.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/naming/ClassNames.scala index dd1e1c97..b301f3a5 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/naming/ClassNames.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/naming/ClassNames.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.naming import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class ClassNames extends Inspection("Class name not recommended", Levels.Info) { +class ClassNames extends Inspection( + text = "Class name not recommended", + defaultLevel = Levels.Info, + description = "Ensures class names adhere to the style guidelines.", + explanation = "Class names should begin with uppercase letter." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,8 +21,7 @@ class ClassNames extends Inspection("Class name not recommended", Levels.Info) { tree match { case ClassDef(_, name, _, _) if name.toString.contains("$anon") => case ClassDef(mods, name, _, _) if !mods.isSynthetic && !name.toString.matches(regex) => - context.warn(tree.pos, self , - s"Class names should begin with uppercase letter (bad = $name)") + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/naming/FieldNames.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/naming/FieldNames.scala deleted file mode 100644 index a3d58cc4..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/naming/FieldNames.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections.naming - -/** @author Stephen Samuel */ -class FieldNames { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/naming/MethodNames.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/naming/MethodNames.scala index 0e5051f3..67618caa 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/naming/MethodNames.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/naming/MethodNames.scala @@ -5,8 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.reflect.internal.Flags /** @author Stephen Samuel */ -class MethodNames extends Inspection("Method name not recommended", Levels.Info, - "Methods should be in camelCase style with the first letter lower-case. See http://docs.scala-lang.org/style/naming-conventions.html#methods") { +class MethodNames extends Inspection( + text = "Method name not recommended", + defaultLevel = Levels.Info, + description = "Warns on method names that don't adhere to the Scala style guidelines.", + explanation = "Methods should be in camelCase style with the first letter lower-case. See http://docs.scala-lang.org/style/naming-conventions.html#methods." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/naming/ObjectNames.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/naming/ObjectNames.scala index 5f9050b1..3634c055 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/naming/ObjectNames.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/naming/ObjectNames.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.naming import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class ObjectNames extends Inspection("Object name not recommended", Levels.Info) { +class ObjectNames extends Inspection( + text = "Object name not recommended", + defaultLevel = Levels.Info, + description = "Ensures object names adhere to the Scala style guidelines.", + explanation = "Object names should only contain alphanumeric characters." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -15,7 +20,7 @@ class ObjectNames extends Inspection("Object name not recommended", Levels.Info) override def inspect(tree: Tree): Unit = { tree match { case ModuleDef(mods, name, _) if !mods.isSynthetic && !name.toString.matches(regex) => - context.warn(tree.pos, self, s"Object names should only contain alphanum chars (bad = $name)") + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/naming/VariableNames.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/naming/VariableNames.scala deleted file mode 100644 index 275d3134..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/naming/VariableNames.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections.naming - -/** @author Stephen Samuel */ -class VariableNames { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullAssignment.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullAssignment.scala index b08ae685..4119c1e3 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullAssignment.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullAssignment.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.nulls import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class NullAssignment extends Inspection("Null assignment", Levels.Warning) { +class NullAssignment extends Inspection( + text = "Null assignment", + defaultLevel = Levels.Warning, + description = "Checks for use of null in assignments.", + explanation = "Use an Option instead when the value can be empty." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -12,19 +17,17 @@ class NullAssignment extends Inspection("Null assignment", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { - case ValDef(_, _, _, Literal(Constant(null))) => warn(tree) + case ValDef(_, _, _, Literal(Constant(null))) => + context.warn(tree.pos, self) case Apply(Select(_, name), List(Literal(Constant(null)))) => if (name.endsWith("_$eq")) - warn(tree) - case Assign(_, Literal(Constant(null))) => warn(tree) + context.warn(tree.pos, self) + case Assign(_, Literal(Constant(null))) => + context.warn(tree.pos, self) case DefDef(mods, _, _, _, _, _) if mods.hasFlag(Flag.SYNTHETIC) => case _ => continue(tree) } } - - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self, "Null assignment on line " + tree.pos.line) - } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameter.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameter.scala index 4eb9fe0e..18bd2497 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameter.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.nulls import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class NullParameter extends Inspection("Null parameter", Levels.Warning) { +class NullParameter extends Inspection( + text = "Null parameter", + defaultLevel = Levels.Warning, + description = "Checks for use of null in method invocation.", + explanation = "Use an Option instead when the value can be empty and pass down a None instead." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -20,14 +25,11 @@ class NullParameter extends Inspection("Null parameter", Levels.Warning) { case Apply(_, _) if tree.tpe.toString == "scala.xml.Elem" => case Apply(_, args) => if (containsNull(args)) - warn(tree) + context.warn(tree.pos, self) case DefDef(mods, _, _, _, _, _) if mods.hasFlag(Flag.SYNTHETIC) => case _ => continue(tree) } } - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self, "Null is used as a method parameter: " + tree.toString().take(300)) - } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/option/EitherGet.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/option/EitherGet.scala index 2fa1a7ec..bf55c238 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/option/EitherGet.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/option/EitherGet.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.option import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class EitherGet extends Inspection("Use of Either Right or Left Projection get", Levels.Error) { +class EitherGet extends Inspection( + text = "Use of Either.right or Either.left projection followed by a get", + defaultLevel = Levels.Error, + description = "Checks for use of .get on Left or Right projection.", + explanation = "Method .get on a Left and a Right projection in deprecated since 2.13, use Either.getOrElse or Either.swap.getOrElse instead." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -21,4 +26,4 @@ class EitherGet extends Inspection("Use of Either Right or Left Projection get", } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/option/ImpossibleOptionSizeCondition.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/option/ImpossibleOptionSizeCondition.scala index 6b9b1223..d7cbfb95 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/option/ImpossibleOptionSizeCondition.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/option/ImpossibleOptionSizeCondition.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.option import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class ImpossibleOptionSizeCondition extends Inspection("Impossible Option.size condition", Levels.Error) { +class ImpossibleOptionSizeCondition extends Inspection( + text = "Impossible Option.size condition", + defaultLevel = Levels.Error, + description = "Checks for code like option.size > 1.", + explanation = "Option.size > 1 can never be true, did you mean to use Option.nonEmpty instead?" +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -18,8 +23,7 @@ class ImpossibleOptionSizeCondition extends Inspection("Impossible Option.size c tree match { case Apply(Select(Select(Apply(TypeApply(Select(_, Opt2Iterable), _), _), Size), Greater), List(Literal(Constant(x: Int)))) if x > 1 => - context.warn(tree.pos, self, - "Option.size > " + x + " can never be true: " + tree.toString().take(200)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/option/InexhausticOptionMatch.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/option/InexhausticOptionMatch.scala deleted file mode 100644 index 1081e22d..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/option/InexhausticOptionMatch.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections.option - -/** @author Stephen Samuel */ -class InexhausticOptionMatch { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionComparison.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionComparison.scala deleted file mode 100644 index 3be78d15..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionComparison.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections.option - -/** @author Stephen Samuel */ -class OptionComparison { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionGet.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionGet.scala index 81717e24..bd43f954 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionGet.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionGet.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.option import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class OptionGet extends Inspection("Use of Option.get", Levels.Error) { +class OptionGet extends Inspection( + text = "Use of Option.get", + defaultLevel = Levels.Error, + description = "Checks for use of Option.get", + explanation = "Using Option.get defeats the purpose of using Option in the first place. Use the following instead: Option.getOrElse, Option.fold, pattern matching or don't take the value out of the container and map over it to transform it." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -14,7 +19,7 @@ class OptionGet extends Inspection("Use of Option.get", Levels.Error) { tree match { case Select(left, TermName("get")) => if (left.tpe.typeSymbol.fullName == "scala.Option") - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionHead.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionHead.scala deleted file mode 100644 index 0496737c..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionHead.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections.option - -/** @author Stephen Samuel */ -class OptionHead { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionSize.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionSize.scala index 02fd8d0f..1ff7e145 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionSize.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionSize.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.option import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class OptionSize extends Inspection("Prefer Option.isDefined instead of Option.size", Levels.Error) { +class OptionSize extends Inspection( + text = "Prefer Option.isDefined instead of Option.size", + defaultLevel = Levels.Error, + description = "Checks for use of Option.size.", + explanation = "Prefer to use Option.isDefined, Option.isEmpty or Option.nonEmpty instead of Option.size." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -14,10 +19,10 @@ class OptionSize extends Inspection("Prefer Option.isDefined instead of Option.s tree match { case Select(Apply(option2Iterable, List(_)), TermName("size")) => if (option2Iterable.symbol.fullName == "scala.Option.option2Iterable") - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysInFormat.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysInFormat.scala index 753d7d16..e2ed0274 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysInFormat.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysInFormat.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.string import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class ArraysInFormat extends Inspection("Array passed to String.format", Levels.Error) { +class ArraysInFormat extends Inspection( + text = "Array passed to String.format", + defaultLevel = Levels.Error, + description = "Checks for arrays passed to String.format.", + explanation = "An Array passed to String.format might result in an incorrect formatting." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -15,7 +20,7 @@ class ArraysInFormat extends Inspection("Array passed to String.format", Levels. override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(_, TermName("format")), args) if containsArrayType(args) => - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysToString.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysToString.scala index b176a087..d49bab64 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysToString.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysToString.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.string import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class ArraysToString extends Inspection("Use of Array.toString", Levels.Warning) { +class ArraysToString extends Inspection( + text = "Use of Array.toString", + defaultLevel = Levels.Warning, + description = "Checks for explicit toString calls on arrays.", + explanation = "Calling toString on an array does not perform a deep toString." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -15,11 +20,10 @@ class ArraysToString extends Inspection("Use of Array.toString", Levels.Warning) override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(lhs, ToString), Nil) if isArray(lhs) => - context.warn(tree.pos, self, - "toString on an array does not perform a deep toString: " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/EmptyInterpolatedString.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/EmptyInterpolatedString.scala index 95da3255..89a153b1 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/EmptyInterpolatedString.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/EmptyInterpolatedString.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.string import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class EmptyInterpolatedString extends Inspection("Empty interpolated string", Levels.Error) { +class EmptyInterpolatedString extends Inspection( + text = "Empty interpolated string", + defaultLevel = Levels.Error, + description = "Looks for interpolated strings that have no arguments.", + explanation = "String declared as interpolated but has no parameters can be turned into a regular string." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,12 +18,10 @@ class EmptyInterpolatedString extends Inspection("Empty interpolated string", Le override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(Apply(Select(_, TermName("apply")), List(_)), TermName("s")), Nil) => - context - .warn(tree.pos, self, - "String declared as interpolated but has no parameters: " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/IllegalFormatString.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/IllegalFormatString.scala index 70f1ccac..e6e99d47 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/IllegalFormatString.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/IllegalFormatString.scala @@ -5,7 +5,12 @@ import java.util.IllegalFormatException import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class IllegalFormatString extends Inspection("Illegal format string", Levels.Error) { +class IllegalFormatString extends Inspection( + text = "Illegal format string", + defaultLevel = Levels.Error, + description = "Checks for invalid format strings.", + explanation = "An unchecked exception will be thrown when a format string contains an illegal syntax or a format specifier that is incompatible with the given arguments." +) { // format is: %[argument_index$][flags][width][.precision][t]conversion final val argRegex = "%(\\d+\\$)?[-#+ 0,(\\<]*?\\d*(\\.\\d+)?[tT]?[a-zA-Z]".r @@ -24,9 +29,8 @@ class IllegalFormatString extends Inspection("Illegal format string", Levels.Err try { String.format(format.toString, args: _*) } catch { - case e: IllegalFormatException => - println(e) - context.warn(tree.pos, self, "A format string contains an illegal syntax: " + e.getMessage) + case _: IllegalFormatException => + context.warn(tree.pos, self) } case _ => continue(tree) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/IncorrectNumberOfArgsToFormat.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/IncorrectNumberOfArgsToFormat.scala index a6593cef..00fc50e4 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/IncorrectNumberOfArgsToFormat.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/IncorrectNumberOfArgsToFormat.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.string import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class IncorrectNumberOfArgsToFormat extends Inspection("Incorrect number of args for format", Levels.Error) { +class IncorrectNumberOfArgsToFormat extends Inspection( + text = "Incorrect number of args for format", + defaultLevel = Levels.Error, + description = "Checks for wrong number of arguments to String.format.", + explanation = "The number of arguments passed to String.format doesn't correspond to the number of fields in the format string." +) { // format is: %[argument_index$][flags][width][.precision][t]conversion // OR: %% @@ -23,9 +28,14 @@ class IncorrectNumberOfArgsToFormat extends Inspection("Incorrect number of args case Apply(Select(Apply(Select(_, TermName("augmentString")), List(Literal(Constant(format)))), TermName("format")), args) => // %% doesn't consume any arguments, but all other formats do - val argCount = argRegex.findAllIn(format.toString).matchData.filterNot(m => doesNotTakeArguments(m.matched)).size + val argCount = + argRegex + .findAllIn(format.toString) + .matchData + .filterNot(m => doesNotTakeArguments(m.matched)) + .size if (argCount > args.size) - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/InvalidRegex.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/InvalidRegex.scala index 54e8fbe4..fbb19358 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/InvalidRegex.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/InvalidRegex.scala @@ -5,7 +5,12 @@ import java.util.regex.PatternSyntaxException import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class InvalidRegex extends Inspection("Invalid regex", Levels.Info) { +class InvalidRegex extends Inspection( + text = "Invalid regex", + defaultLevel = Levels.Info, + description = "Checks for invalid regex literals.", + explanation = "Invalid regex literals can fail at compile time with a PatternSyntaxException. This could be caused by e.g. dangling meta characters, or unclosed escape characters, etc." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -19,11 +24,11 @@ class InvalidRegex extends Inspection("Invalid regex", Levels.Info) { regex.toString.r } catch { case e: PatternSyntaxException => - context.warn(tree.pos, self, e.getMessage) + context.warn(tree.pos, self) } case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/LooksLikeInterpolatedString.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/LooksLikeInterpolatedString.scala index 31a1bc49..8ed0d4b3 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/LooksLikeInterpolatedString.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/LooksLikeInterpolatedString.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections.string import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} -class LooksLikeInterpolatedString extends Inspection("Looks Like Interpolated String", Levels.Warning) { +class LooksLikeInterpolatedString extends Inspection( + text = "Looks like interpolated String", + defaultLevel = Levels.Warning, + description = "Checks for strings that look like they should be interpolated.", + explanation = "Did you forget to prefix this string with an s, f or raw to interpolate it?" +) { final val regex1 = "\\$\\{[a-z][.a-zA-Z0-9_]*\\}".r final val regex2 = "\\$[a-z][.a-zA-Z0-9_]*".r @@ -18,7 +23,7 @@ class LooksLikeInterpolatedString extends Inspection("Looks Like Interpolated St val possibles = Seq(regex1, regex2) .flatMap(_.findAllIn(str).toList.filterNot(_.contains("$anonfun"))) if (possibles.nonEmpty && !str.startsWith("$")) { - context.warn(tree.pos, self, str) + context.warn(tree.pos, self) } case _ => continue(tree) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/NegativeStringMultiplication.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/NegativeStringMultiplication.scala deleted file mode 100644 index 6c3545f0..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/NegativeStringMultiplication.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.sksamuel.scapegoat.inspections.string - -/** @author Stephen Samuel */ -class NegativeStringMultiplication { -} \ No newline at end of file diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/StripMarginOnRegex.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/StripMarginOnRegex.scala index f735f246..8a5f7941 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/StripMarginOnRegex.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/StripMarginOnRegex.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.string import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class StripMarginOnRegex extends Inspection("Strip margin on regex", Levels.Error, - "Strip margin will strip | from regex - possible corrupted regex") { +class StripMarginOnRegex extends Inspection( + text = "Strip margin on regex", + defaultLevel = Levels.Error, + description = "Checks for .stripMargin calls on regex strings that contain a pipe.", + explanation = "Strip margin will strip | from regex - possible corrupted regex." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -24,4 +28,4 @@ class StripMarginOnRegex extends Inspection("Strip margin on regex", Levels.Erro } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/SubstringZero.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/SubstringZero.scala index ef4ab9c9..859c871d 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/SubstringZero.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/SubstringZero.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.string import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class SubstringZero extends Inspection("String.substring(0)", Levels.Info) { +class SubstringZero extends Inspection( + text = "String.substring(0)", + defaultLevel = Levels.Info, + description = "Checks for String.substring(0).", + explanation = "Use of String.substring(0) will always return the same string." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,8 +21,7 @@ class SubstringZero extends Inspection("String.substring(0)", Levels.Info) { override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(lhs, Substring), List(Literal(Constant(0)))) if lhs.tpe <:< StringType => - context.warn(tree.pos, self, - "Use of String.substring(0) will always return the same string: " + tree.toString().take(100)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/UnsafeStringContains.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/UnsafeStringContains.scala index 86eb1d6b..5605445b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/UnsafeStringContains.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/UnsafeStringContains.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.string import com.sksamuel.scapegoat._ /** @author Zack Grannan */ -class UnsafeStringContains extends Inspection("Unsafe string contains", Levels.Error) { +class UnsafeStringContains extends Inspection( + text = "Unsafe string contains", + defaultLevel = Levels.Error, + description = "Checks for String.contains(value) for invalid types.", + explanation = "String.contains() accepts arguments af any type, which means you might be checking if your string contains an element of an unrelated type." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -26,10 +31,12 @@ class UnsafeStringContains extends Inspection("Unsafe string contains", Levels.E override def inspect(tree: Tree): Unit = { tree match { - case Applied(Select(lhs, Contains), targ :: Nil, (_ :: Nil) :: Nil) if isString(lhs) && !isCompatibleType(targ) => - context.warn(tree.pos, self, tree.toString().take(300)) - case Applied(Select(lhs, Contains), _, (arg :: Nil) :: Nil) if isString(lhs) && !isCompatibleType(arg) => - context.warn(tree.pos, self, tree.toString().take(300)) + case Applied(Select(lhs, Contains), targ :: Nil, (_ :: Nil) :: Nil) + if isString(lhs) && !isCompatibleType(targ) => + context.warn(tree.pos, self) + case Applied(Select(lhs, Contains), _, (arg :: Nil) :: Nil) + if isString(lhs) && !isCompatibleType(arg) => + context.warn(tree.pos, self) case _ => continue(tree) } From 17effc81dfea5b9bd18a3464b42d8a5d8f68e3a7 Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Mon, 9 Mar 2020 00:57:38 +0000 Subject: [PATCH 06/18] Add description to style, unnecessary, unsafe and other uncategorised inspections. --- .../scapegoat/inspections/AbstractTrait.scala | 5 ---- .../inspections/AbstractValueInTrait.scala | 5 ---- .../scapegoat/inspections/AnyUse.scala | 18 +++++++------ .../inspections/AvoidToMinusOne.scala | 13 +++++++--- .../inspections/ChainedPackageClause.scala | 5 ---- .../inspections/ClassExtendsApp.scala | 5 ---- .../scapegoat/inspections/DefaultArgUse.scala | 19 -------------- .../inspections/DoubleNegation.scala | 10 +++++--- .../inspections/EmptyCaseClass.scala | 8 ++++-- .../inspections/EqualInspection.scala | 6 ----- .../FinalModifierOnCaseClass.scala | 6 ++++- .../HardCodedWindowsFileSeparator.scala | 6 ----- .../inspections/HashcodeInspection.scala | 6 ----- .../inspections/LonelySealedTrait.scala | 9 +++++-- .../scapegoat/inspections/MaxParameters.scala | 10 +++++--- .../MethodWithUnitReturnIsParameterless.scala | 5 ---- .../scapegoat/inspections/NoClone.scala | 6 ----- .../scapegoat/inspections/NoOpOverride.scala | 10 +++++--- .../scapegoat/inspections/NoSuperClone.scala | 5 ---- .../inspections/OptionGetGetOrElse.scala | 6 ----- .../inspections/PublicFinalizer.scala | 13 +++++++--- .../RedundantFinalModifierOnMethod.scala | 9 ++++--- .../RedundantFinalModifierOnVar.scala | 7 ++++-- .../inspections/SetSeqToDistinctStub.scala | 3 --- .../scapegoat/inspections/TypeShadowing.scala | 13 +++++----- .../inspections/UnusedLocalVal.scala | 6 ----- .../inspections/UppercaseLChecker.scala | 6 ----- .../inspections/UselessObjectVisibility.scala | 6 ----- .../scapegoat/inspections/VarClosure.scala | 10 +++++--- .../scapegoat/inspections/VarFields.scala | 6 ----- .../scapegoat/inspections/VarUse.scala | 15 +++++++---- .../inspections/VariableShadowing.scala | 15 +++++------ .../inspections/math/ZeroNumerator.scala | 1 + .../style/AvoidOperatorOverload.scala | 11 +++++--- .../ParameterlessMethodReturnsUnit.scala | 12 +++++---- .../style/SimplifyBooleanExpression.scala | 10 +++++--- .../inspections/unneccesary/ConstantIf.scala | 10 +++++--- .../unneccesary/RedundantFinalizer.scala | 9 +++++-- .../unneccesary/UnnecessaryCatchBlock.scala | 5 ---- .../unneccesary/UnnecessaryConversion.scala | 25 +++++++++---------- .../unneccesary/UnnecessaryIf.scala | 17 +++++++------ .../unneccesary/UnnecessaryReturnUse.scala | 10 +++++--- .../UnnecessaryStoreBeforeReturn.scala | 6 ----- .../unneccesary/UnnecessarySubstring.scala | 6 ----- .../unneccesary/UnusedMethodParameter.scala | 22 ++++++++-------- .../unneccesary/VarCouldBeVal.scala | 13 +++++----- .../inspections/unsafe/AsInstanceOf.scala | 15 +++++++---- .../unsafe/FinalizerWithoutSuper.scala | 8 ++++-- .../inspections/unsafe/IsInstanceOf.scala | 12 ++++++--- .../scapegoat/inspections/unsafe/TryGet.scala | 10 ++++++-- 50 files changed, 223 insertions(+), 251 deletions(-) delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/AbstractTrait.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/AbstractValueInTrait.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/ChainedPackageClause.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/ClassExtendsApp.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/DefaultArgUse.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/EqualInspection.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/HardCodedWindowsFileSeparator.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/HashcodeInspection.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/MethodWithUnitReturnIsParameterless.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/NoClone.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/NoSuperClone.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/OptionGetGetOrElse.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/SetSeqToDistinctStub.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/UnusedLocalVal.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/UppercaseLChecker.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/UselessObjectVisibility.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/VarFields.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryCatchBlock.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryStoreBeforeReturn.scala delete mode 100644 src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessarySubstring.scala diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/AbstractTrait.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/AbstractTrait.scala deleted file mode 100644 index 31f383e1..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/AbstractTrait.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -class AbstractTrait { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/AbstractValueInTrait.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/AbstractValueInTrait.scala deleted file mode 100644 index 882b1fc6..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/AbstractValueInTrait.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -class AbstractValueInTrait { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/AnyUse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/AnyUse.scala index c428e4d1..fca3df32 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/AnyUse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/AnyUse.scala @@ -5,25 +5,27 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.reflect.internal.Flags /** @author Stephen Samuel */ -class AnyUse extends Inspection("AnyUse", Levels.Info) { +class AnyUse extends Inspection( + text = "Use of Any", + defaultLevel = Levels.Info, + description = "Checks for code returning Any.", + explanation = "Code returning Any is most likely an indication of a programming error." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { import context.global._ - def warn(tree: Tree) = { - context.warn(tree.pos, AnyUse.this, - "Use of Any should be avoided: " + tree.toString().take(200)) - } - override def inspect(tree: Tree): Unit = { tree match { case DefDef(mods, _, _, _, _, _) if mods.isSynthetic => case DefDef(mods, _, _, _, _, _) if mods.hasFlag(Flags.SetterFlags) => case DefDef(mods, _, _, _, _, _) if mods.hasFlag(Flags.GetterFlags) => - case ValDef(_, _, tpt, _) if tpt.tpe =:= typeOf[Any] => warn(tree) - case DefDef(_, _, _, _, tpt, _) if tpt.tpe =:= typeOf[Any] => warn(tree) + case ValDef(_, _, tpt, _) if tpt.tpe =:= typeOf[Any] => + context.warn(tree.pos, self) + case DefDef(_, _, _, _, tpt, _) if tpt.tpe =:= typeOf[Any] => + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/AvoidToMinusOne.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/AvoidToMinusOne.scala index 042275f1..5a917779 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/AvoidToMinusOne.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/AvoidToMinusOne.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.runtime.{RichInt, RichLong} /** @author Stephen Samuel */ -class AvoidToMinusOne extends Inspection("Avoid To Minus One", Levels.Info) { +class AvoidToMinusOne extends Inspection( + text = "Avoid (j to k - 1)", + defaultLevel = Levels.Info, + description = "Checks for ranges using (j to k - 1).", + explanation = "A range in the following format (j to k - 1) can be simplified to (j until k)." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -24,9 +29,9 @@ class AvoidToMinusOne extends Inspection("Avoid To Minus One", Levels.Info) { override def inspect(tree: Tree): Unit = { tree match { case Apply(TypeApply(Select(Apply(Select(lhs, To), - List(Apply(Select(loopvar, Minus), List(Literal(Constant(1)))))), Foreach), _), _) if isIntegral(lhs) && isIntegral(loopvar) => - context.warn(tree.pos, self, - "j to k - 1 can be better written as j until k: " + tree.toString().take(200)) + List(Apply(Select(loopvar, Minus), List(Literal(Constant(1)))))), Foreach), _), _) + if isIntegral(lhs) && isIntegral(loopvar) => + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/ChainedPackageClause.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/ChainedPackageClause.scala deleted file mode 100644 index 8f0afa47..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/ChainedPackageClause.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -class ChainedPackageClause { -} - diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/ClassExtendsApp.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/ClassExtendsApp.scala deleted file mode 100644 index 32cf7bb9..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/ClassExtendsApp.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -class ClassExtendsApp { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/DefaultArgUse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/DefaultArgUse.scala deleted file mode 100644 index 0b35bed4..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/DefaultArgUse.scala +++ /dev/null @@ -1,19 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} - -/** @author Stephen Samuel */ -class DefaultArgUse extends Inspection("DefaultArgUse", Levels.Info) { - def inspector(context: InspectionContext): Inspector = new Inspector(context) { - override def postTyperTraverser = Some apply new context.Traverser { - - import context.global._ - - override def inspect(tree: Tree): Unit = { - tree match { - case _ => continue(tree) - } - } - } - } -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/DoubleNegation.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/DoubleNegation.scala index 0bd9a8e1..cc407773 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/DoubleNegation.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/DoubleNegation.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class DoubleNegation extends Inspection("Double negation", Levels.Info) { +class DoubleNegation extends Inspection( + text = "Double negation", + defaultLevel = Levels.Info, + description = "Checks for code like !(!b).", + explanation = "Double negation can be removed, e.g. !(!b) it equal to just b." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -15,8 +20,7 @@ class DoubleNegation extends Inspection("Double negation", Levels.Info) { override def inspect(tree: Tree): Unit = { tree match { case Select(Select(_, Bang), Bang) => - context.warn(tree.pos, self, - "Double negation can be removed: " + tree.toString().take(200)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/EmptyCaseClass.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/EmptyCaseClass.scala index 2b9350c5..706d0ecc 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/EmptyCaseClass.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/EmptyCaseClass.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class EmptyCaseClass extends Inspection("Empty case class", Levels.Info, - "Empty case class can be rewritten as a case object") { +class EmptyCaseClass extends Inspection( + text = "Empty case class", + defaultLevel = Levels.Info, + description = "Checks for empty case classes like, e.g. case class Faceman().", + explanation = "An empty case class can be rewritten as a case object." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/EqualInspection.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/EqualInspection.scala deleted file mode 100644 index dad72ae2..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/EqualInspection.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -/** @author Stephen Samuel */ -class EqualInspection { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/FinalModifierOnCaseClass.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/FinalModifierOnCaseClass.scala index 1416105f..5a09ec83 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/FinalModifierOnCaseClass.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/FinalModifierOnCaseClass.scala @@ -3,7 +3,11 @@ package com.sksamuel.scapegoat.inspections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} class FinalModifierOnCaseClass extends Inspection( - "Missing final modifier on case class", Levels.Info, "Case classes should have final modifier") { + text = "Missing final modifier on case class", + defaultLevel = Levels.Info, + description = "Checks for case classes without a final modifier.", + explanation = "Using case classes without final modifier can lead to surprising breakage." +) { override def inspector(context: InspectionContext): Inspector = new Inspector(context) { diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/HardCodedWindowsFileSeparator.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/HardCodedWindowsFileSeparator.scala deleted file mode 100644 index 5f2ac115..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/HardCodedWindowsFileSeparator.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -/** @author Stephen Samuel */ -class HardCodedWindowsFileSeparator { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/HashcodeInspection.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/HashcodeInspection.scala deleted file mode 100644 index 5426e194..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/HashcodeInspection.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -/** @author Stephen Samuel */ -class HashcodeInspection { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/LonelySealedTrait.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/LonelySealedTrait.scala index b7788a2b..c61f70c0 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/LonelySealedTrait.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/LonelySealedTrait.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.collection.mutable /** @author Stephen Samuel */ -class LonelySealedTrait extends Inspection("Lonely sealed trait", Levels.Error) { +class LonelySealedTrait extends Inspection( + text = "Lonely sealed trait", + defaultLevel = Levels.Error, + description = "Checks for sealed traits without any classes extending it.", + explanation = "A sealed traits that is not extended is considered dead code." +) { override def inspector(context: InspectionContext): Inspector = new Inspector(context) { @@ -17,7 +22,7 @@ class LonelySealedTrait extends Inspection("Lonely sealed trait", Levels.Error) override def postInspection(): Unit = { for ((name, cdef) <- sealedClasses) { if (!implementedClasses.contains(name)) { - context.warn(cdef.pos, self, s"Sealed trait ${cdef.name} has no implementing classes") + context.warn(cdef.pos, self) } } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/MaxParameters.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/MaxParameters.scala index d2986433..4c1d1e01 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/MaxParameters.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/MaxParameters.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class MaxParameters extends Inspection("Max parameters", Levels.Info) { +class MaxParameters extends Inspection( + text = "Max parameters", + defaultLevel = Levels.Info, + description = "Checks for methods that have over 10 parameters.", + explanation = "Methods having a large number of parameters are more difficult to reason about, consider refactoring this code." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -21,8 +26,7 @@ class MaxParameters extends Inspection("Max parameters", Levels.Info) { case DefDef(_, name, _, _, _, _) if name == nme.CONSTRUCTOR => case DefDef(mods, _, _, _, _, _) if mods.isSynthetic => case DefDef(_, name, _, vparamss, _, _) if countExceeds(vparamss, 10) => - context.warn(tree.pos, self, - s"Method $name has ${count(vparamss)} parameters. Consider refactoring to a containing instance.") + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/MethodWithUnitReturnIsParameterless.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/MethodWithUnitReturnIsParameterless.scala deleted file mode 100644 index 14b06568..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/MethodWithUnitReturnIsParameterless.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -class MethodWithUnitReturnIsParameterless { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/NoClone.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/NoClone.scala deleted file mode 100644 index 4e8d7bd7..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/NoClone.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -/** @author Stephen Samuel */ -class NoClone { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/NoOpOverride.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/NoOpOverride.scala index 1bf5ffd0..dd5dabbf 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/NoOpOverride.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/NoOpOverride.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class NoOpOverride extends Inspection("No op Override", Levels.Info) { +class NoOpOverride extends Inspection( + text = "Noop override", + defaultLevel = Levels.Info, + description = "Checks for code that overrides parent method but simply calls super.", + explanation = "This method is overridden yet only calls super." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -22,8 +27,7 @@ class NoOpOverride extends Inspection("No op Override", Levels.Info) { tree match { case DefDef(_, name, _, vparamss, _, Apply(Select(Super(This(_), _), name2), args)) if name == name2 && vparamss.size == 1 && argumentsMatch(vparamss.head, args) => - context.warn(tree.pos, self, - "This method is overridden yet only calls super: " + tree.toString().take(200)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/NoSuperClone.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/NoSuperClone.scala deleted file mode 100644 index 995d4cd9..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/NoSuperClone.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.sksamuel.scapegoat.inspections - - -/** @author Stephen Samuel */ -class NoSuperClone \ No newline at end of file diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/OptionGetGetOrElse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/OptionGetGetOrElse.scala deleted file mode 100644 index 7a268dc5..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/OptionGetGetOrElse.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -// for protected on objects -class OptionGetGetOrElse { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/PublicFinalizer.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/PublicFinalizer.scala index 6687591a..706ced7f 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/PublicFinalizer.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/PublicFinalizer.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class PublicFinalizer extends Inspection("PublicFinalizer", Levels.Info, - "Public finalizer should be avoided as finalizers should not be programatically invoked") { +class PublicFinalizer extends Inspection( + text = "PublicFinalizer", + defaultLevel = Levels.Info, + description = "Checks for overridden finalizes that are public.", + explanation = "Public finalizer should be avoided as finalizers should not be programmatically invoked." +) { override def inspector(context: InspectionContext): Inspector = new Inspector(context) { @@ -14,8 +18,9 @@ class PublicFinalizer extends Inspection("PublicFinalizer", Levels.Info, override def inspect(tree: Tree): Unit = { tree match { - case DefDef(mods, TermName("finalize"), Nil, Nil, tpt, _) if mods.isPublic && tpt.tpe <:< typeOf[Unit] => - context.warn(tree.pos, self) + case DefDef(mods, TermName("finalize"), Nil, Nil, tpt, _) + if mods.isPublic && tpt.tpe <:< typeOf[Unit] => + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/RedundantFinalModifierOnMethod.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/RedundantFinalModifierOnMethod.scala index 1e2410c5..e9fb8a48 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/RedundantFinalModifierOnMethod.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/RedundantFinalModifierOnMethod.scala @@ -5,7 +5,11 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.reflect.internal.Flags class RedundantFinalModifierOnMethod extends Inspection( - "Redundant final modifier on method", Levels.Info) { + text = "Redundant final modifier on a method", + defaultLevel = Levels.Info, + description = "Checks for redundant final modifiers on methods.", + explanation = "A final modifier on methods that cannot be overridden is redundant." +) { override def inspector(context: InspectionContext): Inspector = new Inspector(context) { @@ -25,8 +29,7 @@ class RedundantFinalModifierOnMethod extends Inspection( tree.symbol.enclClass.isCase || tree.symbol.enclClass.isModuleOrModuleClass || tree.symbol.enclClass.isPackageObjectOrClass) => - context.warn(tree.pos, self, - s"${dd.symbol.fullName} cannot be overridden, final modifier is redundant") + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/RedundantFinalModifierOnVar.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/RedundantFinalModifierOnVar.scala index f5b4ecfa..494e083c 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/RedundantFinalModifierOnVar.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/RedundantFinalModifierOnVar.scala @@ -3,8 +3,11 @@ package com.sksamuel.scapegoat.inspections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} class RedundantFinalModifierOnVar extends Inspection( - "Redundant final modifier on var", Levels.Info, - "This var cannot be overridden, final modifier is redundant") { + text = "Redundant final modifier on a var", + defaultLevel = Levels.Info, + description = "Checks for redundant final modifier on vars.", + explanation = "A final modifier on a var that cannot be overridden is redundant." +) { override def inspector(context: InspectionContext): Inspector = new Inspector(context) { diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/SetSeqToDistinctStub.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/SetSeqToDistinctStub.scala deleted file mode 100644 index bd8652e7..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/SetSeqToDistinctStub.scala +++ /dev/null @@ -1,3 +0,0 @@ -class SetSeqToDistinctStub { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/TypeShadowing.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/TypeShadowing.scala index 206745ac..6c4480a5 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/TypeShadowing.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/TypeShadowing.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.collection.mutable /** @author Stephen Samuel */ -class TypeShadowing extends Inspection("Type shadowing", Levels.Warning) { +class TypeShadowing extends Inspection( + text = "Type shadowing", + defaultLevel = Levels.Warning, + description = "Checks for shadowed type parameters in methods.", + explanation = "Shadowing type parameters is considered a bad practice and should be avoided." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -20,17 +25,13 @@ class TypeShadowing extends Inspection("Type shadowing", Levels.Warning) { case dd @ DefDef(_, name, deftparams, _, _, _) => deftparams.foreach(tparam => { if (types.contains(tparam.name.toString)) - warn(dd, name, tparam) + context.warn(dd.pos, self) }) case ClassDef(_, _, tparams2, Template(_, _, body)) => checkShadowing(tparams2, body) case _ => } } - private def warn(dd: DefDef, name: TermName, tparam: TypeDef): Unit = { - context.warn(dd.pos, self, s"Method $name declares shadowed type parameter ${tparam.name}") - } - override def inspect(tree: Tree): Unit = { tree match { case ClassDef(_, _, tparams, Template(_, _, body)) => checkShadowing(tparams, body) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/UnusedLocalVal.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/UnusedLocalVal.scala deleted file mode 100644 index 87be2e11..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/UnusedLocalVal.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -/** @author Stephen Samuel */ -class UnusedLocalVal { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/UppercaseLChecker.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/UppercaseLChecker.scala deleted file mode 100644 index 8bce8751..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/UppercaseLChecker.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -/** @author Stephen Samuel */ -class UppercaseLChecker { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/UselessObjectVisibility.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/UselessObjectVisibility.scala deleted file mode 100644 index 18522553..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/UselessObjectVisibility.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -// for protected on objects -class UselessObjectVisibility { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/VarClosure.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/VarClosure.scala index af05c521..842162ce 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/VarClosure.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/VarClosure.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class VarClosure extends Inspection("Var in closure", Levels.Warning) { +class VarClosure extends Inspection( + text = "Var in closure", + defaultLevel = Levels.Warning, + description = "Finds closures that reference variables (var).", + explanation = "Closing over a var can lead to subtle bugs." +) { override def inspector(context: InspectionContext): Inspector = new Inspector(context) { @@ -16,8 +21,7 @@ class VarClosure extends Inspection("Var in closure", Levels.Warning) { case Apply(Select(_, _), args) => args.filter(_.symbol != null) .foreach(arg => if (arg.symbol.isMethod && arg.symbol.isGetter && !arg.symbol.isStable) { - context.warn(tree.pos, self, - "Closing over a var can lead to subtle bugs: " + tree.toString().take(500)) + context.warn(tree.pos, self) }) case _ => } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/VarFields.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/VarFields.scala deleted file mode 100644 index 94eeccb3..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/VarFields.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections - -/** @author Stephen Samuel */ -class VarFields { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/VarUse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/VarUse.scala index 78d72d77..9eda11da 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/VarUse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/VarUse.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections import com.sksamuel.scapegoat._ -class VarUse extends Inspection("Use of var", Levels.Warning) { +class VarUse extends Inspection( + text = "Use of var", + defaultLevel = Levels.Warning, + description = "Checks for use of variables (var).", + explanation = "Use of variables is generally discouraged, especially in the context of a shared mutable state. Consider using an immutable state or other referentially transparent alternatives." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,11 +21,11 @@ class VarUse extends Inspection("Use of var", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case ClassDef(_, _, _, Template(parents, _, _)) if parents.exists(isActor) => - case ModuleDef(_, _, Template(parents, _, _)) if parents.exists(isActor) => - case ValDef(mods, _, _, _) if mods.isSynthetic || mods.isMacro => - case ValDef(_, _, tpt, _) if isXmlLiteral(tpt.tpe) => + case ModuleDef(_, _, Template(parents, _, _)) if parents.exists(isActor) => + case ValDef(mods, _, _, _) if mods.isSynthetic || mods.isMacro => + case ValDef(_, _, tpt, _) if isXmlLiteral(tpt.tpe) => case ValDef(modifiers, _, _, _) if modifiers.hasFlag(Flag.MUTABLE) => - context.warn(tree.pos, self, "var used: " + tree.toString().take(300)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/VariableShadowing.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/VariableShadowing.scala index 3f7b90b4..0846f75e 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/VariableShadowing.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/VariableShadowing.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.collection.mutable import scala.collection.mutable.ListBuffer -class VariableShadowing extends Inspection("Variable shadowing", Levels.Warning) { +class VariableShadowing extends Inspection( + text = "Variable shadowing", + defaultLevel = Levels.Warning, + description = "Checks for multiple uses of the variable name in nested scopes.", + explanation = "Variable shadowing is very useful, but can easily lead to nasty bugs in your code. Shadowed variables can be potentially confusing to other maintainers when the same name is adopted to have a new meaning in a nested scope." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -16,10 +21,6 @@ class VariableShadowing extends Inspection("Variable shadowing", Levels.Warning) private def isDefined(name: String): Boolean = contexts exists (_.contains(name.trim)) - private def warn(tree: Tree): Unit = { - context.warn(tree.pos, self, "Variable is shadowed: " + tree.toString().take(200)) - } - private def enter(): Unit = contexts.push(new ListBuffer[String]) private def exit(): Unit = contexts.pop() @@ -37,12 +38,12 @@ class VariableShadowing extends Inspection("Variable shadowing", Levels.Warning) inspect(rhs) exit() case ValDef(_, TermName(name), _, _) => - if (isDefined(name)) warn(tree) + if (isDefined(name)) context.warn(tree.pos, self) contexts.top.append(name.trim) case Match(_, cases) => cases.foreach { case CaseDef(Bind(name, _), _, _) => - if (isDefined(name.toString)) warn(tree) + if (isDefined(name.toString)) context.warn(tree.pos, self) case _ => // do nothing } continue(tree) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/ZeroNumerator.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/ZeroNumerator.scala index af67b558..a74f5003 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/ZeroNumerator.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/ZeroNumerator.scala @@ -9,6 +9,7 @@ class ZeroNumerator extends Inspection( description = "Checks for dividing 0 by a number.", explanation = "Dividing zero by any number will always return zero, e.g. 0 / x == 0." ) { + def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/style/AvoidOperatorOverload.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/style/AvoidOperatorOverload.scala index daa8962b..a0d03d81 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/style/AvoidOperatorOverload.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/style/AvoidOperatorOverload.scala @@ -10,7 +10,12 @@ import scala.reflect.internal.Flags * * http://docs.scala-lang.org/style/naming-conventions.html#symbolic-method-names */ -class AvoidOperatorOverload extends Inspection("Avoid operator overload", Levels.Info) { +class AvoidOperatorOverload extends Inspection( + text = "Avoid operator overload", + defaultLevel = Levels.Info, + description = "Checks for symbolic method names.", + explanation = "Scala style guide advocates against routinely using operators as method names, see http://docs.scala-lang.org/style/naming-conventions.html#symbolic-method-names." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -23,9 +28,7 @@ class AvoidOperatorOverload extends Inspection("Avoid operator overload", Levels case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => case DefDef(_, TermName("$init$"), _, _, _, _) => case DefDef(_, name, _, _, _, _) if name.toChars.count(_ == '$') > 2 => - context.warn(tree.pos, self, - s"Scala style guide advocates against routinely using operators as method names (${name.decode})." + - "See http://docs.scala-lang.org/style/naming-conventions.html#symbolic-method-names") + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/style/ParameterlessMethodReturnsUnit.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/style/ParameterlessMethodReturnsUnit.scala index 77942cc6..84446df1 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/style/ParameterlessMethodReturnsUnit.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/style/ParameterlessMethodReturnsUnit.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.style import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class ParameterlessMethodReturnsUnit extends Inspection("Parameterless methods returns unit", Levels.Warning) { +class ParameterlessMethodReturnsUnit extends Inspection( + text = "Parameterless methods returns unit", + defaultLevel = Levels.Warning, + description = "Checks for methods returning Unit that are defined without empty parentheses.", + explanation = "Methods should be defined with empty parentheses if they have side effects. A method returning Unit must have side effects, therefore you should declare it with ()." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,10 +18,7 @@ class ParameterlessMethodReturnsUnit extends Inspection("Parameterless methods r override def inspect(tree: Tree): Unit = { tree match { case DefDef(_, name, _, vparamss, tpt, _) if tpt.tpe.toString == "Unit" && vparamss.isEmpty => - context.warn(tree.pos, self, - "Methods should be defined with () if they have side effects. A method returning unit must have side effects, otherwise it can be removed. " + name - .toString - .take(300)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/style/SimplifyBooleanExpression.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/style/SimplifyBooleanExpression.scala index 060ede19..8d19887d 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/style/SimplifyBooleanExpression.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/style/SimplifyBooleanExpression.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.style import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class SimplifyBooleanExpression extends Inspection("Simplify boolean expressions", Levels.Info) { +class SimplifyBooleanExpression extends Inspection( + text = "Simplify boolean expressions", + defaultLevel = Levels.Info, + description = "Checks for boolean expressions that can be simplified.", + explanation = "Boolean expressions such as x == false can be re-written as !x." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -14,8 +19,7 @@ class SimplifyBooleanExpression extends Inspection("Simplify boolean expressions override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(_, Equals), List(Literal(Constant(false)))) => - context.warn(tree.pos, self, - "Boolean expressions such as x == false can be re-written as !x: " + tree.toString().take(200)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/ConstantIf.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/ConstantIf.scala index 7c3f8111..9b27b3db 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/ConstantIf.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/ConstantIf.scala @@ -8,7 +8,12 @@ import com.sksamuel.scapegoat._ * Checks for if statements where the condition evaluates to a constant true or a constant false. * */ -class ConstantIf extends Inspection("Constant if expression", Levels.Warning) { +class ConstantIf extends Inspection( + text = "Constant if expression", + defaultLevel = Levels.Warning, + description = "Checks for code where the if condition compiles to a constant.", + explanation = "An if condition which gets compiled to a constant, like e.g. if (1 < 2) or if (false) doesn't add any value and should be avoided." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -21,8 +26,7 @@ class ConstantIf extends Inspection("Constant if expression", Levels.Warning) { case LabelDef(_, _, _) => case If(cond, _, _) => if (cond.toString() == "false" || cond.toString() == "true") - context.warn(tree.pos, self, - "Constant if expression " + tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/RedundantFinalizer.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/RedundantFinalizer.scala index a2df5ea6..f08bfd99 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/RedundantFinalizer.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/RedundantFinalizer.scala @@ -7,7 +7,12 @@ import com.sksamuel.scapegoat._ * * Inspired by http://findbugs.sourceforge.net/bugDescriptions.html#FI_USELESS */ -class RedundantFinalizer extends Inspection("Redundant finalizer", Levels.Warning) { +class RedundantFinalizer extends Inspection( + text = "Redundant finalizer", + defaultLevel = Levels.Warning, + description = "Checks for empty finalizers.", + explanation = "An empty finalizer, e.g. override def finalize: Unit = {} is redundant and should be removed." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -17,7 +22,7 @@ class RedundantFinalizer extends Inspection("Redundant finalizer", Levels.Warnin override def inspect(tree: Tree): Unit = { tree match { case DefDef(mods, name, _, _, tpt, _) if mods.hasFlag(Flag.OVERRIDE) && name.toString == "finalize" && tpt.toString() == "Unit" => - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryCatchBlock.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryCatchBlock.scala deleted file mode 100644 index 00bb04ee..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryCatchBlock.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.sksamuel.scapegoat.inspections.unneccesary - -/** @author Stephen Samuel */ -class UnnecessaryCatchBlock { -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryConversion.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryConversion.scala index 23cc31d6..a2adc965 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryConversion.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryConversion.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.unneccesary import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class UnnecessaryConversion extends Inspection("Unnecessary conversion", Levels.Warning) { +class UnnecessaryConversion extends Inspection( + text = "Unnecessary conversion", + defaultLevel = Levels.Warning, + description = "Checks for unnecessary toInt on instances of Int or toString on Strings, etc.", + explanation = "Calling e.g. toString on a String or toList on a List is completely unnecessary and it's an equivalent to identity." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -14,24 +19,18 @@ class UnnecessaryConversion extends Inspection("Unnecessary conversion", Levels. override def inspect(tree: Tree): Unit = { tree match { case Select(lhs, TermName("toString")) if lhs.tpe <:< StringClass.tpe => - context.warn(tree.pos, self, "Unnecessary toString on instance of String: " + tree.toString().take(200)) - + context.warn(tree.pos, self, "Unnecessary toString on instance of String.") case Select(lhs, TermName("toInt")) if lhs.tpe <:< IntClass.tpe && Option(lhs.symbol).fold(ifEmpty = true)(_.baseClasses.nonEmpty) => - context.warn(tree.pos, self, "Unnecessary toInt on instance of Int: " + tree.toString.take(200)) - + context.warn(tree.pos, self, "Unnecessary toInt on instance of Int.") case Select(lhs, TermName("toLong")) if lhs.tpe <:< LongClass.tpe => - context.warn(tree.pos, self, "Unnecessary toLong on instance of Long: " + tree.toString.take(200)) - + context.warn(tree.pos, self, "Unnecessary toLong on instance of Long.") case Select(lhs, TermName("toSet")) if isSet(lhs, allowMutableSet = false) => - context.warn(tree.pos, self, "Unnecessary toSet on a set: " + tree.toString.take(200)) - + context.warn(tree.pos, self, "Unnecessary toSet on a Set.") case Select(lhs, TermName("toList")) if isList(lhs) => - context.warn(tree.pos, self, "Unnecessary toList on a list: " + tree.toString.take(200)) - + context.warn(tree.pos, self, "Unnecessary toList on a List.") case Select(lhs, TermName("toSeq")) if isSeq(lhs) => - context.warn(tree.pos, self, "Unnecessary toSeq on a seq: " + tree.toString.take(200)) - + context.warn(tree.pos, self, "Unnecessary toSeq on a Seq.") case _ => } continue(tree) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryIf.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryIf.scala index 99d53915..4364a564 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryIf.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryIf.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.unneccesary import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class UnnecessaryIf extends Inspection("Unnecessary if condition.", Levels.Info) { +class UnnecessaryIf extends Inspection( + text = "Unnecessary if condition.", + defaultLevel = Levels.Info, + description = "Checks for code like if (expr) true else false.", + explanation = "If comparison is not needed. Use the condition, e.g. instead of if (a == b) true else false, use a == b or instead of if (a == b) false else true, use !(a == b)." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,16 +18,12 @@ class UnnecessaryIf extends Inspection("Unnecessary if condition.", Levels.Info) override def inspect(tree: Tree): Unit = { tree match { case If(_, Literal(Constant(true)), Literal(Constant(false))) => - context.warn(tree.pos, self, - "If comparison is not needed. Use the condition. Eg, instead of if (a == b) true else false, simply use a == b. : " + tree - .toString().take(500)) + context.warn(tree.pos, self) case If(_, Literal(Constant(false)), Literal(Constant(true))) => - context.warn(tree.pos, self, - "If comparison is not needed. Use the negated condition. Eg, instead of if (a == b) false else true, simply use !(a == b). : " + tree - .toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryReturnUse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryReturnUse.scala index 1b518a61..b934a9bf 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryReturnUse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryReturnUse.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.unneccesary import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class UnnecessaryReturnUse extends Inspection("Unnecessary return", Levels.Info, - "Scala returns the value of the last expression in a block. Use of return here is not idiomatic scala") { +class UnnecessaryReturnUse extends Inspection( + text = "Unnecessary return", + defaultLevel = Levels.Info, + description = "Checks for use of return keyword in blocks.", + explanation = "Scala returns the value of the last expression in a block. Use of return here is not an idiomatic Scala." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -20,4 +24,4 @@ class UnnecessaryReturnUse extends Inspection("Unnecessary return", Levels.Info, } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryStoreBeforeReturn.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryStoreBeforeReturn.scala deleted file mode 100644 index 41d0cbb9..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryStoreBeforeReturn.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections.unneccesary - -/** @author Stephen Samuel */ -class UnnecessaryStoreBeforeReturn { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessarySubstring.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessarySubstring.scala deleted file mode 100644 index 50a8d37c..00000000 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessarySubstring.scala +++ /dev/null @@ -1,6 +0,0 @@ -package com.sksamuel.scapegoat.inspections.unneccesary - -/** @author Stephen Samuel */ -class UnnecessarySubstring { - -} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnusedMethodParameter.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnusedMethodParameter.scala index 504e01ae..3fd99eb3 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnusedMethodParameter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnusedMethodParameter.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat._ import scala.reflect.internal.Flags /** @author Stephen Samuel */ -class UnusedMethodParameter extends Inspection("Unused parameter", Levels.Warning) { +class UnusedMethodParameter extends Inspection( + text = "Unused parameter", + defaultLevel = Levels.Warning, + description = "Checks for unused method parameters.", + explanation = "Unused constructor or method parameters should be removed." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -52,11 +57,8 @@ class UnusedMethodParameter extends Inspection("Unused parameter", Levels.Warnin vparam <- vparams ) { val paramName = vparam.name.toString - if (!usesParameter(paramName, constructorBody) && - !usesField(paramName, classBody)) { - - context.warn(vparam.pos, self, s"Unused constructor parameter (${vparam.name})") - } + if (!usesParameter(paramName, constructorBody) && !usesField(paramName, classBody)) + context.warn(vparam.pos, self) } } @@ -68,7 +70,6 @@ class UnusedMethodParameter extends Inspection("Unused parameter", Levels.Warnin case ClassDef(mods, _, _, _) if mods.hasAbstractFlag => case ClassDef(_, _, _, classBody @ Template(_, _, classTopLevelStmts)) => - classTopLevelStmts.foreach { case DefDef(_, nme.CONSTRUCTOR, _, vparamss, _, constructorBody) => checkConstructor(vparamss, constructorBody, classBody) @@ -76,7 +77,6 @@ class UnusedMethodParameter extends Inspection("Unused parameter", Levels.Warnin checkConstructor(vparamss, constructorBody, classBody) case _ => } - continue(tree) // ignore abstract methods obv. @@ -105,15 +105,13 @@ class UnusedMethodParameter extends Inspection("Unused parameter", Levels.Warnin tpt.tpe =:= UnitTpe && param.tpt.tpe =:= typeOf[Array[String]] => - case DefDef(_, _, _, vparamss, _, rhs) => for ( vparams <- vparamss; vparam <- vparams ) { - if (!usesParameter(vparam.name.toString, rhs)) { - context.warn(tree.pos, self, s"Unused method parameter ($vparam)") - } + if (!usesParameter(vparam.name.toString, rhs)) + context.warn(tree.pos, self) } case _ => continue(tree) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/VarCouldBeVal.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/VarCouldBeVal.scala index e8c87623..9ece1799 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/VarCouldBeVal.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/VarCouldBeVal.scala @@ -5,7 +5,12 @@ import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} import scala.collection.mutable /** @author Stephen Samuel */ -class VarCouldBeVal extends Inspection("Var could be val", Levels.Warning) { +class VarCouldBeVal extends Inspection( + text = "Var could be val", + defaultLevel = Levels.Warning, + description = "Checks for vars that could be declared as vals.", + explanation = "A variable (var) that is never written to could be turned into a value (val)." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -54,11 +59,7 @@ class VarCouldBeVal extends Inspection("Var could be val", Levels.Warning) { tree match { case DefDef(_, _, _, _, _, Block(stmt, expr)) => for ((unwritten, definitionTree) <- containsUnwrittenVar(stmt :+ expr)) { - context.warn( - definitionTree.pos, - self, - s"$unwritten is never written to, so could be a val: " + definitionTree.toString().take(200) - ) + context.warn(definitionTree.pos, self) } case _ => continue(tree) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/AsInstanceOf.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/AsInstanceOf.scala index f7f18c6d..45a31147 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/AsInstanceOf.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/AsInstanceOf.scala @@ -2,7 +2,12 @@ package com.sksamuel.scapegoat.inspections.unsafe import com.sksamuel.scapegoat._ -class AsInstanceOf extends Inspection("Use of asInstanceOf", Levels.Warning) { +class AsInstanceOf extends Inspection( + text = "Use of asInstanceOf", + defaultLevel = Levels.Warning, + description = "", + explanation = "Use of asInstanceOf is considered a bad practice - consider using pattern matching instead." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -12,9 +17,9 @@ class AsInstanceOf extends Inspection("Use of asInstanceOf", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { // this will skip any uses of manifest etc - case TypeApply(Select(qual, TermName("asInstanceOf")), _) if qual.toString != "classOf[java.lang.Class]" => - context.warn(tree.pos, self, - "asInstanceOf used near " + tree.toString().take(500) + ". Consider using pattern matching.") + case TypeApply(Select(qual, TermName("asInstanceOf")), _) + if qual.toString != "classOf[java.lang.Class]" => + context.warn(tree.pos, self) case DefDef(modifiers, _, _, _, _, _) if modifiers.hasFlag(Flag.SYNTHETIC) => // no further case Match(_, cases) => // ignore selector and process cases cases.foreach(traverse) @@ -23,4 +28,4 @@ class AsInstanceOf extends Inspection("Use of asInstanceOf", Levels.Warning) { } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/FinalizerWithoutSuper.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/FinalizerWithoutSuper.scala index 36b5a61f..0591fa55 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/FinalizerWithoutSuper.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/FinalizerWithoutSuper.scala @@ -3,8 +3,12 @@ package com.sksamuel.scapegoat.inspections.unsafe import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels} /** @author Stephen Samuel */ -class FinalizerWithoutSuper extends Inspection("Finalizer without super", Levels.Warning, - "Finalizers should call super.finalize() to ensure superclasses are able to run any finalization logic") { +class FinalizerWithoutSuper extends Inspection( + text = "Finalizer without super", + defaultLevel = Levels.Warning, + description = "Checks for overridden finalizers that do not call super.", + explanation = "Finalizers should call super.finalize() to ensure superclasses are able to run their finalization logic." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/IsInstanceOf.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/IsInstanceOf.scala index b882df4d..f3486d12 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/IsInstanceOf.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/IsInstanceOf.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.unsafe import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class IsInstanceOf extends Inspection("Use of isInstanceOf", Levels.Warning) { +class IsInstanceOf extends Inspection( + text = "Use of isInstanceOf", + defaultLevel = Levels.Warning, + description = "Checks for use of isInstanceOf.", + explanation = "Use of isInstanceOf is considered a bad practice - consider using pattern matching instead." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -13,8 +18,7 @@ class IsInstanceOf extends Inspection("Use of isInstanceOf", Levels.Warning) { override def inspect(tree: Tree): Unit = { tree match { case TypeApply(Select(_, TermName("isInstanceOf")), _) => - context.warn(tree.pos, self, - "Consider using a pattern match rather than isInstanceOf: " + tree.toString().take(500)) + context.warn(tree.pos, self) case DefDef(modifiers, _, _, _, _, _) if modifiers.hasFlag(Flag.SYNTHETIC) => // avoid partial function stuff case Match(_, cases) => // ignore selector and process cases cases.foreach(traverse) @@ -23,4 +27,4 @@ class IsInstanceOf extends Inspection("Use of isInstanceOf", Levels.Warning) { } } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/TryGet.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/TryGet.scala index c98f9ec3..2eb034dc 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/TryGet.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/TryGet.scala @@ -3,7 +3,12 @@ package com.sksamuel.scapegoat.inspections.unsafe import com.sksamuel.scapegoat._ /** @author Stephen Samuel */ -class TryGet extends Inspection("Use of Try.get", Levels.Error) { +class TryGet extends Inspection( + text = "Use of Try.get", + defaultLevel = Levels.Error, + description = "Checks for use of Try.get.", + explanation = "Using Try.get is unsafe because it will throw the underlying exception in case of a Failure. Use the following instead: Try.getOrElse, Try.fold, pattern matching or don't take the value out of the container and map over it to transform it." +) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { override def postTyperTraverser = Some apply new context.Traverser { @@ -14,10 +19,11 @@ class TryGet extends Inspection("Use of Try.get", Levels.Error) { tree match { case Select(left, TermName("get")) => if (left.tpe.typeSymbol.fullName == "scala.util.Try") - context.warn(tree.pos, self, tree.toString().take(500)) + context.warn(tree.pos, self) case _ => continue(tree) } } } } } + From 9c8a885bf4876b3e0a839f5b5019502fd2e26020 Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Mon, 9 Mar 2020 01:32:44 +0000 Subject: [PATCH 07/18] Fix tests. --- .../collections/MapGetAndGetOrElse.scala | 2 +- .../scapegoat/io/HtmlReportWriter.scala | 20 ++-- .../scapegoat/io/ScalastyleReportWriter.scala | 2 +- .../scapegoat/io/XmlReportWriter.scala | 2 +- .../com/sksamuel/scapegoat/FeedbackTest.scala | 93 ++++++++++++++++--- .../inspections/VarCouldBeValTest.scala | 2 - .../collections/MapGetAndGetOrElseTest.scala | 2 +- .../inspections/nulls/NullParameterTest.scala | 3 +- .../UnusedMethodParameterTest.scala | 18 ++-- 9 files changed, 98 insertions(+), 46 deletions(-) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala index 94dc413a..b7abef97 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala @@ -9,7 +9,7 @@ import com.sksamuel.scapegoat._ * myMap.get(key).getOrElse(defaultValue) –> myMap.getOrElse(key, defaultValue) */ class MapGetAndGetOrElse extends Inspection( - text = "Use of Map.get(key).getOrElse(value) instead of Map.getOrElse(key, value)", + text = "Use of Map.get().getOrElse instead of Map.getOrElse", defaultLevel = Levels.Error, description = "Checks whether Map.get().getOrElse() can be simplified to Map.getOrElse().", explanation = "Map.get(key).getOrElse(value) can be replaced with Map.getOrElse(key, value), which is more concise." diff --git a/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala b/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala index 49e9ce94..1673b3c9 100644 --- a/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala @@ -106,20 +106,16 @@ object HtmlReportWriter { }  { warning.text }  { warning.inspection } - { - warning.snippet match { - case None=> - case Some(snippet) => -
- { snippet } -
- } - } + +
+ { warning.description } +
} } - def generate(reporter: Feedback) = - { header }{ body(reporter) } - + def generate(reporter: Feedback) = + + { header }{ body(reporter) } + } diff --git a/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala b/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala index 559e9dd8..e93bdf9e 100644 --- a/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala @@ -24,7 +24,7 @@ object ScalastyleReportWriter { } private def warningToXml(warning: Warning) = { - + } } diff --git a/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala b/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala index 4d6fe408..66df6f7a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala @@ -14,6 +14,6 @@ object XmlReportWriter { } private def warning2xml(warning: Warning) = { - + } } diff --git a/src/test/scala/com/sksamuel/scapegoat/FeedbackTest.scala b/src/test/scala/com/sksamuel/scapegoat/FeedbackTest.scala index 9d506ea6..11088bd9 100644 --- a/src/test/scala/com/sksamuel/scapegoat/FeedbackTest.scala +++ b/src/test/scala/com/sksamuel/scapegoat/FeedbackTest.scala @@ -14,28 +14,48 @@ class FeedbackTest val position = NoPosition val defaultSourcePrefix = "src/main/scala/" - class DummyInspection(text: String, defaultLevel: Level) extends Inspection(text, defaultLevel) { + class DummyInspection( + text: String, + defaultLevel: Level, + description: String, + explanation: String + ) extends Inspection(text, defaultLevel, description, explanation) { override def inspector(context: InspectionContext): Inspector = ??? } "Feedback" - { "should report to reporter" - { "for error" in { - val inspection = new DummyInspection("My default is Error", Levels.Error) + val inspection = new DummyInspection( + "My default is Error", + Levels.Error, + "This is description.", + "This is explanation." + ) val reporter = new StoreReporter val feedback = new Feedback(false, reporter, defaultSourcePrefix) feedback.warn(position, inspection) reporter.infos should contain(reporter.Info(position, "My default is Error", reporter.ERROR)) } "for warning" in { - val inspection = new DummyInspection("My default is Warning", Levels.Warning) + val inspection = new DummyInspection( + "My default is Warning", + Levels.Warning, + "This is description.", + "This is explanation." + ) val reporter = new StoreReporter val feedback = new Feedback(false, reporter, defaultSourcePrefix) feedback.warn(position, inspection) reporter.infos should contain(reporter.Info(position, "My default is Warning", reporter.WARNING)) } "for info" in { - val inspection = new DummyInspection("My default is Info", Levels.Info) + val inspection = new DummyInspection( + "My default is Info", + Levels.Info, + "This is description.", + "This is explanation." + ) val reporter = new StoreReporter val feedback = new Feedback(false, reporter, defaultSourcePrefix) feedback.warn(position, inspection) @@ -72,9 +92,24 @@ class FeedbackTest } "should use minimal wawrning level in reports" - { "for `info`" in { - val inspectionError = new DummyInspection("My default is Error", Levels.Error) - val inspectionWarning = new DummyInspection("My default is Warning", Levels.Warning) - val inspectionInfo = new DummyInspection("My default is Info", Levels.Info) + val inspectionError = new DummyInspection( + "My default is Error", + Levels.Error, + "This is description.", + "This is explanation." + ) + val inspectionWarning = new DummyInspection( + "My default is Warning", + Levels.Warning, + "This is description.", + "This is explanation." + ) + val inspectionInfo = new DummyInspection( + "My default is Info", + Levels.Info, + "This is description.", + "This is explanation." + ) val inspections = Seq(inspectionError, inspectionWarning, inspectionInfo) val reporter = new StoreReporter val feedback = new Feedback(false, reporter, defaultSourcePrefix, Levels.Info) @@ -83,9 +118,24 @@ class FeedbackTest } "for `warning`" in { - val inspectionError = new DummyInspection("My default is Error", Levels.Error) - val inspectionWarning = new DummyInspection("My default is Warning", Levels.Warning) - val inspectionInfo = new DummyInspection("My default is Info", Levels.Info) + val inspectionError = new DummyInspection( + "My default is Error", + Levels.Error, + "This is description.", + "This is explanation." + ) + val inspectionWarning = new DummyInspection( + "My default is Warning", + Levels.Warning, + "This is description.", + "This is explanation." + ) + val inspectionInfo = new DummyInspection( + "My default is Info", + Levels.Info, + "This is description.", + "This is explanation." + ) val inspections = Seq(inspectionError, inspectionWarning, inspectionInfo) val reporter = new StoreReporter val feedback = new Feedback(false, reporter, defaultSourcePrefix, Levels.Warning) @@ -95,9 +145,24 @@ class FeedbackTest } "for `error`" in { - val inspectionError = new DummyInspection("My default is Error", Levels.Error) - val inspectionWarning = new DummyInspection("My default is Warning", Levels.Warning) - val inspectionInfo = new DummyInspection("My default is Info", Levels.Info) + val inspectionError = new DummyInspection( + "My default is Error", + Levels.Error, + "This is description.", + "This is explanation." + ) + val inspectionWarning = new DummyInspection( + "My default is Warning", + Levels.Warning, + "This is description.", + "This is explanation." + ) + val inspectionInfo = new DummyInspection( + "My default is Info", + Levels.Info, + "This is description.", + "This is explanation." + ) val inspections = Seq(inspectionError, inspectionWarning, inspectionInfo) val reporter = new StoreReporter val feedback = new Feedback(false, reporter, defaultSourcePrefix, Levels.Error) @@ -107,4 +172,4 @@ class FeedbackTest } } } -} \ No newline at end of file +} diff --git a/src/test/scala/com/sksamuel/scapegoat/inspections/VarCouldBeValTest.scala b/src/test/scala/com/sksamuel/scapegoat/inspections/VarCouldBeValTest.scala index 1c5d45bd..b871459d 100644 --- a/src/test/scala/com/sksamuel/scapegoat/inspections/VarCouldBeValTest.scala +++ b/src/test/scala/com/sksamuel/scapegoat/inspections/VarCouldBeValTest.scala @@ -65,9 +65,7 @@ class VarCouldBeValTest val warningsInOrder = compiler.scapegoat.feedback.warnings.sortBy(_.line) val Seq(barWarning, bazWarning) = warningsInOrder barWarning.line shouldBe 3 - barWarning.snippet should contain("bar is never written to, so could be a val: var bar: Int = 1") bazWarning.line shouldBe 5 - bazWarning.snippet should contain("baz is never written to, so could be a val: var baz: Int = 3") } } "should not report warning" - { diff --git a/src/test/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElseTest.scala b/src/test/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElseTest.scala index c2f691c5..eb7277d5 100644 --- a/src/test/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElseTest.scala +++ b/src/test/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElseTest.scala @@ -10,7 +10,7 @@ class MapGetAndGetOrElseTest extends FreeSpec with Matchers with PluginRunner { private def getOrElseAssertion(code: String): Unit = { compileCodeSnippet(code) compiler.scapegoat.feedback.warnings.size shouldBe 1 - compiler.scapegoat.feedback.warnings.head.text shouldBe ("Use of .get.getOrElse instead of .getOrElse") + compiler.scapegoat.feedback.warnings.head.text shouldBe ("Use of Map.get().getOrElse instead of Map.getOrElse") } "Map with get followed by getOrElse" - { diff --git a/src/test/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameterTest.scala b/src/test/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameterTest.scala index a46a486a..44ca3fa5 100644 --- a/src/test/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameterTest.scala +++ b/src/test/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameterTest.scala @@ -25,7 +25,6 @@ class NullParameterTest extends FreeSpec with Matchers with PluginRunner with On compileCodeSnippet(code) compiler.scapegoat.feedback.warnings.size shouldBe 1 - compiler.scapegoat.feedback.warnings.forall(_.snippet.get.contains("method argument")) } "should not report warning" - { "for override val in case class" in { @@ -55,4 +54,4 @@ class NullParameterTest extends FreeSpec with Matchers with PluginRunner with On // } } } -} \ No newline at end of file +} diff --git a/src/test/scala/com/sksamuel/scapegoat/inspections/unnecessary/UnusedMethodParameterTest.scala b/src/test/scala/com/sksamuel/scapegoat/inspections/unnecessary/UnusedMethodParameterTest.scala index cbcd59ba..1490f90c 100644 --- a/src/test/scala/com/sksamuel/scapegoat/inspections/unnecessary/UnusedMethodParameterTest.scala +++ b/src/test/scala/com/sksamuel/scapegoat/inspections/unnecessary/UnusedMethodParameterTest.scala @@ -69,16 +69,16 @@ class UnusedMethodParameterTest "for methods not returning" in { val code = """class Test { - | def foo(name:String) = throw new RuntimeException - |}""".stripMargin + | def foo(name:String) = throw new RuntimeException + |}""".stripMargin compileCodeSnippet(code) compiler.scapegoat.feedback.warnings.size shouldBe 0 } "for methods not returning when their return type is specified" in { val code = """class Test { - | def foo(name:String): String = throw new RuntimeException - |}""".stripMargin + | def foo(name:String): String = throw new RuntimeException + |}""".stripMargin compileCodeSnippet(code) compiler.scapegoat.feedback.warnings.size shouldBe 0 @@ -150,10 +150,7 @@ class UnusedMethodParameterTest val code = """case class Foo(x: Int)(y: Int)""" compileCodeSnippet(code) - compiler.scapegoat.feedback.warnings match { - case Seq(warning: Warning) => - warning.snippet.get should include("y") - } + compiler.scapegoat.feedback.warnings.size shouldBe 1 } "not warn on case class secondary params used as fields" in { @@ -180,10 +177,7 @@ class UnusedMethodParameterTest val code = """class Foo(x: Int)""" compileCodeSnippet(code) - compiler.scapegoat.feedback.warnings match { - case Seq(warning: Warning) => - warning.snippet.get should include("x") - } + compiler.scapegoat.feedback.warnings.size shouldBe 1 } "not warn on non-case class primary params used as fields" in { From d0808c6b99c526ff2e409b2a2c5d04419163dab6 Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Mon, 9 Mar 2020 01:40:10 +0000 Subject: [PATCH 08/18] Fix trailing comma. --- .../com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala index 1d61e3f4..953ed518 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala @@ -7,7 +7,7 @@ class EmptyFor extends Inspection( text = "Empty for loop", defaultLevel = Levels.Warning, description = "Checks for empty for loops.", - explanation = "An empty for loop isn't a common practice and in most cases is considered as dead code.", + explanation = "An empty for loop isn't a common practice and in most cases is considered as dead code." ) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { @@ -27,4 +27,3 @@ class EmptyFor extends Inspection( } } } - From 6f0dacad439e04ef8f4da57a9b1d5b9802472d99 Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Tue, 10 Mar 2020 01:11:07 +0000 Subject: [PATCH 09/18] Bring back code snippets to console output. --- .../com/sksamuel/scapegoat/Feedback.scala | 26 +++++++++++++++---- .../com/sksamuel/scapegoat/Inspection.scala | 20 +++++++++----- .../scapegoat/inspections/AnyUse.scala | 4 +-- .../inspections/AvoidToMinusOne.scala | 2 +- .../inspections/DoubleNegation.scala | 2 +- .../scapegoat/inspections/NoOpOverride.scala | 2 +- .../scapegoat/inspections/VarClosure.scala | 2 +- .../scapegoat/inspections/VarUse.scala | 2 +- .../inspections/VariableShadowing.scala | 4 +-- .../collections/AvoidSizeEqualsZero.scala | 2 +- .../collections/AvoidSizeNotEqualsZero.scala | 2 +- .../CollectionIndexOnNonIndexedSeq.scala | 2 +- .../CollectionNamingConfusion.scala | 16 +++++++++--- .../collections/CollectionNegativeIndex.scala | 2 +- .../CollectionPromotionToAny.scala | 2 +- .../collections/ComparisonToEmptyList.scala | 2 +- .../collections/ComparisonToEmptySet.scala | 2 +- .../collections/DuplicateMapKey.scala | 4 ++- .../collections/DuplicateSetValue.scala | 2 +- .../ExistsSimplifiableToContains.scala | 2 +- .../collections/FilterDotHead.scala | 2 +- .../collections/FilterDotHeadOption.scala | 2 +- .../collections/FilterDotIsEmpty.scala | 2 +- .../collections/FilterDotSize.scala | 2 +- .../collections/FilterOptionAndGet.scala | 2 +- ...indAndNotEqualsNoneReplaceWithExists.scala | 2 +- .../collections/FindDotIsDefined.scala | 2 +- .../collections/MapGetAndGetOrElse.scala | 2 +- .../collections/NegationIsEmpty.scala | 2 +- .../collections/NegationNonEmpty.scala | 2 +- .../collections/NegativeSeqPad.scala | 2 +- .../collections/PreferMapEmpty.scala | 2 +- .../collections/PreferSeqEmpty.scala | 2 +- .../collections/PreferSetEmpty.scala | 2 +- .../inspections/collections/ReverseFunc.scala | 4 +-- .../collections/ReverseTailReverse.scala | 4 +-- .../collections/ReverseTakeReverse.scala | 4 +-- .../collections/SwapSortFilter.scala | 6 ++--- .../collections/UnsafeContains.scala | 2 +- .../UnsafeTraversableMethods.scala | 2 +- .../controlflow/RepeatedIfElseBody.scala | 4 +-- .../inspections/controlflow/WhileTrue.scala | 4 +-- .../inspections/empty/EmptyFor.scala | 2 +- .../inspections/empty/EmptyIfBlock.scala | 2 +- .../inspections/empty/EmptyMethod.scala | 2 +- .../empty/EmptySynchronizedBlock.scala | 2 +- .../inspections/empty/EmptyTryBlock.scala | 2 +- .../inspections/empty/EmptyWhileBlock.scala | 2 +- .../equality/ComparingUnrelatedTypes.scala | 2 +- .../exception/CatchException.scala | 2 +- .../inspections/exception/CatchFatal.scala | 2 +- .../exception/CatchThrowable.scala | 2 +- .../IncorrectlyNamedExceptions.scala | 4 +-- .../exception/SwallowedException.scala | 4 +-- .../exception/UnreachableCatch.scala | 2 +- .../inspections/imports/WildcardImport.scala | 2 +- .../inference/BoundedByFinalType.scala | 2 +- .../inference/MethodReturningAny.scala | 2 +- .../inference/PointlessTypeBounds.scala | 2 +- .../ProductWithSerializableInferred.scala | 2 +- .../PartialFunctionInsteadOfMatch.scala | 8 +++--- .../matching/RepeatedCaseBody.scala | 2 +- .../SuspiciousMatchOnClassObject.scala | 2 +- .../math/BigDecimalDoubleConstructor.scala | 4 +-- .../inspections/math/BrokenOddness.scala | 2 +- .../scapegoat/inspections/math/ModOne.scala | 2 +- .../inspections/nulls/NullParameter.scala | 2 +- .../inspections/option/EitherGet.scala | 4 +-- .../ImpossibleOptionSizeCondition.scala | 2 +- .../inspections/option/OptionGet.scala | 2 +- .../inspections/option/OptionSize.scala | 2 +- .../inspections/string/ArraysInFormat.scala | 2 +- .../inspections/string/ArraysToString.scala | 2 +- .../string/EmptyInterpolatedString.scala | 2 +- .../IncorrectNumberOfArgsToFormat.scala | 2 +- .../inspections/string/SubstringZero.scala | 2 +- .../string/UnsafeStringContains.scala | 4 +-- .../ParameterlessMethodReturnsUnit.scala | 2 +- .../style/SimplifyBooleanExpression.scala | 2 +- .../inspections/unneccesary/ConstantIf.scala | 2 +- .../unneccesary/RedundantFinalizer.scala | 2 +- .../unneccesary/UnnecessaryConversion.scala | 12 ++++----- .../unneccesary/UnnecessaryIf.scala | 4 +-- .../unneccesary/UnusedMethodParameter.scala | 4 +-- .../unneccesary/VarCouldBeVal.scala | 2 +- .../inspections/unsafe/AsInstanceOf.scala | 2 +- .../inspections/unsafe/IsInstanceOf.scala | 2 +- .../scapegoat/inspections/unsafe/TryGet.scala | 2 +- .../scapegoat/io/HtmlReportWriter.scala | 11 ++++++-- .../scapegoat/io/ScalastyleReportWriter.scala | 2 +- .../scapegoat/io/XmlReportWriter.scala | 2 +- .../inspections/VarCouldBeValTest.scala | 2 ++ .../inspections/nulls/NullParameterTest.scala | 1 + .../UnusedMethodParameterTest.scala | 10 +++++-- 94 files changed, 179 insertions(+), 131 deletions(-) diff --git a/src/main/scala/com/sksamuel/scapegoat/Feedback.scala b/src/main/scala/com/sksamuel/scapegoat/Feedback.scala index c235de79..f093c207 100644 --- a/src/main/scala/com/sksamuel/scapegoat/Feedback.scala +++ b/src/main/scala/com/sksamuel/scapegoat/Feedback.scala @@ -20,10 +20,15 @@ class Feedback(consoleOutput: Boolean, reporter: Reporter, sourcePrefix: String, def warns = warnings(Levels.Warning) def warnings(level: Level): Seq[Warning] = warnings.filter(_.level == level) - def warn(pos: Position, inspection: Inspection, adhocDescription: Option[String] = None): Unit = { + def warn( + pos: Position, + inspection: Inspection, + snippet: Option[String] = None, + adhocExplanation: Option[String] = None + ): Unit = { val level = inspection.defaultLevel val text = inspection.text - val description = adhocDescription.getOrElse(inspection.description) + val explanation = adhocExplanation.getOrElse(inspection.explanation) val adjustedLevel = (levelOverridesByInspectionSimpleName.get("all"), levelOverridesByInspectionSimpleName.get(inspection.getClass.getSimpleName)) match { case (Some(l), _) => l case (None, Some(l)) => l @@ -32,11 +37,21 @@ class Feedback(consoleOutput: Boolean, reporter: Reporter, sourcePrefix: String, val sourceFileFull = pos.source.file.path val sourceFileNormalized = normalizeSourceFile(sourceFileFull) - val warning = Warning(text, pos.line, adjustedLevel, sourceFileFull, sourceFileNormalized, description, inspection.getClass.getCanonicalName) + val warning = Warning( + text = text, + line = pos.line, + level = adjustedLevel, + sourceFileFull = sourceFileFull, + sourceFileNormalized = sourceFileNormalized, + snippet = snippet, + explanation = explanation, + inspection = inspection.getClass.getCanonicalName + ) warningsBuffer.append(warning) if (shouldPrint(warning)) { println(s"[${warning.level.toString.toLowerCase}] $sourceFileNormalized:${warning.line}: $text") - description.foreach(s => println(s" $s")) + println(s" $explanation") + snippet.foreach(s => println(s" $s")) println() } @@ -61,7 +76,8 @@ case class Warning( level: Level, sourceFileFull: String, sourceFileNormalized: String, - description: String, + snippet: Option[String], + explanation: String, inspection: String ) { def hasMinimalLevelOf(minimalLevel: Level): Boolean = { diff --git a/src/main/scala/com/sksamuel/scapegoat/Inspection.scala b/src/main/scala/com/sksamuel/scapegoat/Inspection.scala index 481ec415..5645670e 100644 --- a/src/main/scala/com/sksamuel/scapegoat/Inspection.scala +++ b/src/main/scala/com/sksamuel/scapegoat/Inspection.scala @@ -41,14 +41,20 @@ abstract class Inspector(val context: InspectionContext) { } case class InspectionContext(global: Global, feedback: Feedback) { - - def warn(pos: Position, inspection: Inspection): Unit = { - feedback.warn(pos, inspection, None) - } - def warn(pos: Position, inspection: Inspection, adhocDescription: String): Unit = { - feedback.warn(pos, inspection, Some(adhocDescription)) - } + def warn(pos: Position, inspection: Inspection): Unit = + feedback.warn(pos, inspection, None, None) + + def warn(pos: Position, inspection: Inspection, snippet: String): Unit = + feedback.warn(pos, inspection, Some(snippet), None) + + def warn( + pos: Position, + inspection: Inspection, + snippet: String, + adhocExplanation: String + ): Unit = + feedback.warn(pos, inspection, Some(snippet), Some(adhocExplanation)) trait Traverser extends global.Traverser { diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/AnyUse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/AnyUse.scala index fca3df32..d70039d4 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/AnyUse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/AnyUse.scala @@ -23,9 +23,9 @@ class AnyUse extends Inspection( case DefDef(mods, _, _, _, _, _) if mods.hasFlag(Flags.SetterFlags) => case DefDef(mods, _, _, _, _, _) if mods.hasFlag(Flags.GetterFlags) => case ValDef(_, _, tpt, _) if tpt.tpe =:= typeOf[Any] => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(200)) case DefDef(_, _, _, _, tpt, _) if tpt.tpe =:= typeOf[Any] => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(200)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/AvoidToMinusOne.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/AvoidToMinusOne.scala index 5a917779..19cc37fd 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/AvoidToMinusOne.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/AvoidToMinusOne.scala @@ -31,7 +31,7 @@ class AvoidToMinusOne extends Inspection( case Apply(TypeApply(Select(Apply(Select(lhs, To), List(Apply(Select(loopvar, Minus), List(Literal(Constant(1)))))), Foreach), _), _) if isIntegral(lhs) && isIntegral(loopvar) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(200)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/DoubleNegation.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/DoubleNegation.scala index cc407773..4fd786dc 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/DoubleNegation.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/DoubleNegation.scala @@ -20,7 +20,7 @@ class DoubleNegation extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Select(Select(_, Bang), Bang) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(200)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/NoOpOverride.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/NoOpOverride.scala index dd5dabbf..d6165f28 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/NoOpOverride.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/NoOpOverride.scala @@ -27,7 +27,7 @@ class NoOpOverride extends Inspection( tree match { case DefDef(_, name, _, vparamss, _, Apply(Select(Super(This(_), _), name2), args)) if name == name2 && vparamss.size == 1 && argumentsMatch(vparamss.head, args) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(200)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/VarClosure.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/VarClosure.scala index 842162ce..e514ed84 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/VarClosure.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/VarClosure.scala @@ -21,7 +21,7 @@ class VarClosure extends Inspection( case Apply(Select(_, _), args) => args.filter(_.symbol != null) .foreach(arg => if (arg.symbol.isMethod && arg.symbol.isGetter && !arg.symbol.isStable) { - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) }) case _ => } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/VarUse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/VarUse.scala index 9eda11da..519e7f0a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/VarUse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/VarUse.scala @@ -25,7 +25,7 @@ class VarUse extends Inspection( case ValDef(mods, _, _, _) if mods.isSynthetic || mods.isMacro => case ValDef(_, _, tpt, _) if isXmlLiteral(tpt.tpe) => case ValDef(modifiers, _, _, _) if modifiers.hasFlag(Flag.MUTABLE) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/VariableShadowing.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/VariableShadowing.scala index 0846f75e..e2e42a66 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/VariableShadowing.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/VariableShadowing.scala @@ -38,12 +38,12 @@ class VariableShadowing extends Inspection( inspect(rhs) exit() case ValDef(_, TermName(name), _, _) => - if (isDefined(name)) context.warn(tree.pos, self) + if (isDefined(name)) context.warn(tree.pos, self, tree.toString.take(200)) contexts.top.append(name.trim) case Match(_, cases) => cases.foreach { case CaseDef(Bind(name, _), _, _) => - if (isDefined(name.toString)) context.warn(tree.pos, self) + if (isDefined(name.toString)) context.warn(tree.pos, self, tree.toString.take(200)) case _ => // do nothing } continue(tree) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala index 813882b0..fb798085 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala @@ -21,7 +21,7 @@ class AvoidSizeEqualsZero extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(Select(q, Size | Length), TermName("$eq$eq")), List(Literal(Constant(0)))) if isTraversable(q) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(100)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala index 98f6042b..3c9563b2 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala @@ -21,7 +21,7 @@ class AvoidSizeNotEqualsZero extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(Select(_, Length | Size), TermName("$bang$eq")), List(Literal(Constant(0)))) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(100)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionIndexOnNonIndexedSeq.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionIndexOnNonIndexedSeq.scala index fdb8b9e1..5abc5292 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionIndexOnNonIndexedSeq.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionIndexOnNonIndexedSeq.scala @@ -23,7 +23,7 @@ class CollectionIndexOnNonIndexedSeq extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(lhs, TermName("apply")), List(idx)) if isSeq(lhs) && !isIndexedSeq(lhs) && !isLiteral(idx)=> - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(100)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNamingConfusion.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNamingConfusion.scala index bbd8b188..8e5859a4 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNamingConfusion.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNamingConfusion.scala @@ -23,11 +23,19 @@ class CollectionNamingConfusion extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case ValDef(_, TermName(name), tpt, _) if isSet(tpt) && isNamedList(name) => - context.warn(tree.pos, self, - "An instance of a Set is confusingly referred to by a variable called/containing list.") + context.warn( + tree.pos, + self, + tree.toString.take(300), + "An instance of a Set is confusingly referred to by a variable called/containing list." + ) case ValDef(_, TermName(name), tpt, _) if isList(tpt) && isNamedSet(name) => - context.warn(tree.pos, self, - "An instance of a List is confusingly referred to by a variable called/containing set.") + context.warn( + tree.pos, + self, + tree.toString.take(300), + "An instance of a List is confusingly referred to by a variable called/containing set." + ) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNegativeIndex.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNegativeIndex.scala index 8aae30ff..e0261463 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNegativeIndex.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionNegativeIndex.scala @@ -19,7 +19,7 @@ class CollectionNegativeIndex extends Inspection( tree match { case Apply(Select(lhs, TermName("apply")), List(Literal(Constant(x: Int)))) if isList(lhs) && x < 0 => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(100)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionPromotionToAny.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionPromotionToAny.scala index 386489a5..f93d5199 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionPromotionToAny.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/CollectionPromotionToAny.scala @@ -39,7 +39,7 @@ class CollectionPromotionToAny extends Inspection( tree match { case TypeApply(Select(l, TermName("$colon$plus")), a :: _) => if (!isAnySeq(l) && isAny(a)) - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(100)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptyList.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptyList.scala index 00b4742c..f67253bd 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptyList.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptyList.scala @@ -31,7 +31,7 @@ class ComparisonToEmptyList extends Inspection( } private def warn(tree: Tree): Unit = - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(200)) } } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptySet.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptySet.scala index f71c1e8c..ab599490 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptySet.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ComparisonToEmptySet.scala @@ -31,7 +31,7 @@ class ComparisonToEmptySet extends Inspection( } private def warn(tree: Tree): Unit = - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(200)) } } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateMapKey.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateMapKey.scala index 7f711bfe..0e025bdc 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateMapKey.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateMapKey.scala @@ -29,7 +29,9 @@ class DuplicateMapKey extends Inspection( override def inspect(tree: Tree): Unit = { tree match { - case Apply(TypeApply(Select(Select(_, TermName("Map")), TermName("apply")), _), args) if isDuplicateKeys(args) => context.warn(tree.pos, self) + case Apply(TypeApply(Select(Select(_, TermName("Map")), TermName("apply")), _), args) + if isDuplicateKeys(args) => + context.warn(tree.pos, self, tree.toString.take(100)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateSetValue.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateSetValue.scala index 113394e2..400d2f5b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateSetValue.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/DuplicateSetValue.scala @@ -27,7 +27,7 @@ class DuplicateSetValue extends Inspection( tree match { case Apply(TypeApply(Select(Select(_, TermName("Set")), TermName("apply")), _), args) if hasDuplicates(args) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(100)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsSimplifiableToContains.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsSimplifiableToContains.scala index 960f6ede..8dd3efa4 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsSimplifiableToContains.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ExistsSimplifiableToContains.scala @@ -36,7 +36,7 @@ class ExistsSimplifiableToContains extends Inspection( tree match { case Apply(Select(lhs, TermName("exists")), List(Function(_, Apply(Select(_, Equals), List(x))))) if isContainsTraversable(lhs) && doesElementTypeMatch(lhs, x) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHead.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHead.scala index 5d17447d..5bcba6fa 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHead.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHead.scala @@ -21,7 +21,7 @@ class FilterDotHead extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Select(Apply(Select(_, Filter), _), Head) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHeadOption.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHeadOption.scala index 326bbe4a..caa6f708 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHeadOption.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotHeadOption.scala @@ -18,7 +18,7 @@ class FilterDotHeadOption extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Select(Apply(Select(_, TermName("filter")), _), TermName("headOption")) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotIsEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotIsEmpty.scala index f8142217..81e6b065 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotIsEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotIsEmpty.scala @@ -18,7 +18,7 @@ class FilterDotIsEmpty extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Select(Apply(Select(_, TermName("filter")), _), TermName("isEmpty")) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSize.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSize.scala index 620da6f7..9b9539e2 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSize.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterDotSize.scala @@ -22,7 +22,7 @@ class FilterDotSize extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Select(Apply(Select(_, TermName("filter")), _), TermName("size")) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterOptionAndGet.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterOptionAndGet.scala index 4bc7cc0c..064d58df 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterOptionAndGet.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FilterOptionAndGet.scala @@ -20,7 +20,7 @@ class FilterOptionAndGet extends Inspection( case Apply(TypeApply( Select(Apply(Select(_, TermName("filter")), List(Function(_, Select(_, TermName("isDefined"))))), TermName("map")), _), List(Function(_, Select(_, TermName("get"))))) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindAndNotEqualsNoneReplaceWithExists.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindAndNotEqualsNoneReplaceWithExists.scala index 748a9736..dea9b80b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindAndNotEqualsNoneReplaceWithExists.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindAndNotEqualsNoneReplaceWithExists.scala @@ -18,7 +18,7 @@ class FindAndNotEqualsNoneReplaceWithExists extends Inspection( tree match { case Apply(Select(Apply(Select(_, TermName("find")), _), TermName("$bang$eq")), List(Select(_, TermName("None")))) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindDotIsDefined.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindDotIsDefined.scala index 6015ebfc..2d82faf5 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindDotIsDefined.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/FindDotIsDefined.scala @@ -18,7 +18,7 @@ class FindDotIsDefined extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Select(Apply(Select(_, TermName("find")), _), TermName("isDefined")) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala index b7abef97..aabcf579 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/MapGetAndGetOrElse.scala @@ -24,7 +24,7 @@ class MapGetAndGetOrElse extends Inspection( tree match { case Apply(TypeApply(Select(Apply(Select(left, TermName("get")), List(key)), TermName("getOrElse")), _), List(defaultValue)) if isMap(left) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala index 56c0fe1b..08330149 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala @@ -21,7 +21,7 @@ class NegationIsEmpty extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Select(Select(lhs, IsEmpty), Bang) if isTraversable(lhs) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(100)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala index 30ceb947..fac1be17 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala @@ -21,7 +21,7 @@ class NegationNonEmpty extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Select(Select(lhs, IsEmpty), Bang) if isTraversable(lhs) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(100)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegativeSeqPad.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegativeSeqPad.scala index 3f749e7a..81003ae3 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegativeSeqPad.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegativeSeqPad.scala @@ -18,7 +18,7 @@ class NegativeSeqPad extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(TypeApply(Select(_, TermName("padTo")), _), Literal(Constant(_)) :: _) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferMapEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferMapEmpty.scala index 26523dac..909ec91e 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferMapEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferMapEmpty.scala @@ -21,7 +21,7 @@ class PreferMapEmpty extends Inspection( tree match { case a@Apply(TypeApply(Select(Select(_, MapTerm), ApplyTerm), _), List()) if a.tpe.toString.startsWith("scala.collection.immutable.") => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSeqEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSeqEmpty.scala index 99822df3..5b0163d3 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSeqEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSeqEmpty.scala @@ -22,7 +22,7 @@ class PreferSeqEmpty extends Inspection( tree match { case a@Apply(TypeApply(Select(Select(_, SeqTerm), ApplyTerm), _), List()) if (!a.tpe.toString.startsWith("scala.collection.mutable.")) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSetEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSetEmpty.scala index 7dabf45b..b7031769 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSetEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/PreferSetEmpty.scala @@ -22,7 +22,7 @@ class PreferSetEmpty extends Inspection( tree match { case a@Apply(TypeApply(Select(Select(_, SetTerm), ApplyTerm), _), List()) if a.tpe.toString.startsWith("scala.collection.immutable.") => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseFunc.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseFunc.scala index 7a167c92..ccf2626b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseFunc.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseFunc.scala @@ -30,10 +30,10 @@ class ReverseFunc extends Inspection( tree match { case Select(Select(c, TermName("reverse")), TermName(FuncReplace(func, replace))) if c.tpe <:< typeOf[Traversable[Any]] => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case Select(Apply(arrayOps1, List(Select(Apply(arrayOps2, List(_)), TermName("reverse")))), TermName(FuncReplace(func, replace))) if arrayOps1.toString.contains("ArrayOps") && arrayOps2.toString.contains("ArrayOps") => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTailReverse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTailReverse.scala index 05a9d07d..ad297f58 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTailReverse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTailReverse.scala @@ -17,12 +17,12 @@ class ReverseTailReverse extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Select(Select(Select(c, TermName("reverse")), TermName("tail")), TermName("reverse")) if isTraversable(c) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case Select(Apply(arrayOps0, List(Select(Apply(arrayOps1, List(Select(Apply(arrayOps2, List(_)), TermName("reverse")))), TermName("tail")))), TermName("reverse")) if (arrayOps0.toString.contains("ArrayOps")) && arrayOps1.toString.contains("ArrayOps") && arrayOps2.toString.contains("ArrayOps") => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTakeReverse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTakeReverse.scala index da2c53c2..7c184ff6 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTakeReverse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/ReverseTakeReverse.scala @@ -18,12 +18,12 @@ class ReverseTakeReverse extends Inspection( tree match { case Select(Apply(Select(Select(c, TermName("reverse")), TermName("take")), _), TermName("reverse")) if isTraversable(c) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case Select(Apply(arrayOps0, List(Apply(Select(Apply(arrayOps1, List(Select(Apply(arrayOps2, List(_)), TermName("reverse")))), TermName("take")), _))), TermName("reverse")) if (arrayOps0.toString.contains("ArrayOps")) && (arrayOps1.toString.contains("ArrayOps")) && (arrayOps2.toString.contains("ArrayOps")) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/SwapSortFilter.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/SwapSortFilter.scala index ae91787b..61ffb719 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/SwapSortFilter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/SwapSortFilter.scala @@ -18,11 +18,11 @@ class SwapSortFilter extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(Apply(TypeApply(Select(lhs, TermName("sorted")), _), _), TermName("filter")), _) if isSeq(lhs) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case Apply(Select(Apply(Apply(TypeApply(Select(lhs, TermName("sortBy")), _), _), _), TermName("filter")), _) if isSeq(lhs) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case Apply(Select(Apply(Select(lhs, TermName("sortWith")), _), TermName("filter")), _) if isSeq(lhs) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeContains.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeContains.scala index 3ac3460e..d71c2d5c 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeContains.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeContains.scala @@ -36,7 +36,7 @@ class UnsafeContains extends Inspection( override def inspect(tree: Tree): Unit = tree match { case Applied(Select(lhs, Contains), _, (arg :: Nil) :: Nil) if isSeqOrOption(lhs) && !isCompatibleType(lhs, arg) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeTraversableMethods.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeTraversableMethods.scala index dbeea9ef..4d248445 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeTraversableMethods.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/UnsafeTraversableMethods.scala @@ -32,7 +32,7 @@ class UnsafeTraversableMethods extends Inspection( tree match { case Select(left, TermName(method)) => if (isTraversable(left) && unsafeMethods.contains(method)) - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/RepeatedIfElseBody.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/RepeatedIfElseBody.scala index f7a0aaf6..4144e81a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/RepeatedIfElseBody.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/RepeatedIfElseBody.scala @@ -28,10 +28,10 @@ class RepeatedIfElseBody extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case If(_, mainBranch, elseBranch) if isRepeated(mainBranch, elseBranch) => - context.warn(tree.pos, self, "Main and else branches of if are repeated.") + context.warn(tree.pos, self, tree.toString.take(500), "Main and else branches of if are repeated.") case If(_, mainBranch@Block(_, _), elseBranch@Block(_, _)) if twoBlocksStartWithTheSame(mainBranch, elseBranch) => - context.warn(tree.pos, self, "Main and else branches start with the same command.") + context.warn(tree.pos, self, tree.toString.take(500), "Main and else branches start with the same command.") case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/WhileTrue.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/WhileTrue.scala index 6984ab1d..b33308ca 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/WhileTrue.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/controlflow/WhileTrue.scala @@ -20,9 +20,9 @@ class WhileTrue extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case LabelDef(name, _, If(cond, _, _)) if isWhile(name) && isConstantCondition(cond) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case LabelDef(name, _, Block(_, If(cond, _, _))) if isWhile(name) && isConstantCondition(cond) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala index 953ed518..063f8b6e 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyFor.scala @@ -20,7 +20,7 @@ class EmptyFor extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(TypeApply(Select(_, Foreach), _), List(Function(List(ValDef(_, _, _, EmptyTree)), Literal(Constant(()))))) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyIfBlock.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyIfBlock.scala index fe2c0f23..5a9c6234 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyIfBlock.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyIfBlock.scala @@ -18,7 +18,7 @@ class EmptyIfBlock extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case If(_, Literal(Constant(())), _) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyMethod.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyMethod.scala index 8127392a..92113bb2 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyMethod.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyMethod.scala @@ -22,7 +22,7 @@ class EmptyMethod extends Inspection( case ClassDef(mods, _, _, _) if mods.isTrait => continue(tree) case DefDef(_, _, _, _, _, _) if tree.symbol != null && tree.symbol.enclClass.isTrait => case DefDef(_, _, _, _, _, Literal(Constant(()))) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptySynchronizedBlock.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptySynchronizedBlock.scala index 2b9b2481..ebd803f9 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptySynchronizedBlock.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptySynchronizedBlock.scala @@ -20,7 +20,7 @@ class EmptySynchronizedBlock extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(TypeApply(Select(_, Sync), _), List(Literal(Constant(())))) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyTryBlock.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyTryBlock.scala index 2b383d49..4e88edbe 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyTryBlock.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyTryBlock.scala @@ -18,7 +18,7 @@ class EmptyTryBlock extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Try(Literal(Constant(())), _, _) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyWhileBlock.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyWhileBlock.scala index 33dddb0f..14226d89 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyWhileBlock.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/empty/EmptyWhileBlock.scala @@ -18,7 +18,7 @@ class EmptyWhileBlock extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case LabelDef(_, _, If(_, Block(List(Literal(Constant(()))), _), _)) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingUnrelatedTypes.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingUnrelatedTypes.scala index cda5ea69..99d132ba 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingUnrelatedTypes.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/equality/ComparingUnrelatedTypes.scala @@ -64,7 +64,7 @@ class ComparingUnrelatedTypes extends Inspection( } if (!hasSpecificEq(lhs.tpe.deconst) && !related(lhs.tpe.widen, rhs.tpe.widen)) { - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) } case _ => continue(tree) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchException.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchException.scala index 343cd708..866af766 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchException.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchException.scala @@ -28,7 +28,7 @@ class CatchException extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Try(_, cases, _) if catchesException(cases) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchFatal.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchFatal.scala index e654f684..cad3c1fa 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchFatal.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchFatal.scala @@ -38,7 +38,7 @@ class CatchFatal extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Try(_, cases, _) if catchesFatal(cases) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchThrowable.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchThrowable.scala index 288846a3..77a4639d 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchThrowable.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/CatchThrowable.scala @@ -28,7 +28,7 @@ class CatchThrowable extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Try(_, cases, _) if catchesThrowable(cases) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/IncorrectlyNamedExceptions.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/IncorrectlyNamedExceptions.scala index d16daa2a..9fd10221 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/IncorrectlyNamedExceptions.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/IncorrectlyNamedExceptions.scala @@ -41,9 +41,9 @@ class IncorrectlyNamedExceptions extends Inspection( (isNamedException, isAnon, isException) match { case (true, _, false) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case (false, false, true) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => } case _ => diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/SwallowedException.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/SwallowedException.scala index c7eb359b..a1dcf850 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/SwallowedException.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/SwallowedException.scala @@ -31,10 +31,10 @@ class SwallowedException extends Inspection( case CaseDef(Bind(TermName("ignored") | TermName("ignore"), _), _, _) => case cdef @ CaseDef(_, _, Literal(Constant(()))) if cdef.body.toString == "()" => - context.warn(cdef.pos, self) + context.warn(cdef.pos, self, cdef.toString.take(100)) case cdef @ CaseDef(Bind(caughtException, _), _, subtree) if containsMaskingThrow(caughtException, Seq(subtree)) => - context.warn(cdef.pos, self) + context.warn(cdef.pos, self, cdef.toString.take(100)) case _ => } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/UnreachableCatch.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/UnreachableCatch.scala index 06ee1a11..8f0a0579 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/exception/UnreachableCatch.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/exception/UnreachableCatch.scala @@ -40,7 +40,7 @@ class UnreachableCatch extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Try(_, cases, _) if isUnreachable(cases) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/imports/WildcardImport.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/imports/WildcardImport.scala index 39f42181..bf468cde 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/imports/WildcardImport.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/imports/WildcardImport.scala @@ -21,7 +21,7 @@ class WildcardImport extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Import(_, selector) if isWildcard(selector) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/BoundedByFinalType.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/BoundedByFinalType.scala index db7a8251..0939a958 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/BoundedByFinalType.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/BoundedByFinalType.scala @@ -23,7 +23,7 @@ class BoundedByFinalType extends Inspection( case TypeDef(_, _, _, typeTree: TypeTree) => typeTree.original match { case TypeBoundsTree(lo, hi) if lo.tpe.isFinalType && hi.tpe.isFinalType => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => } case _ => continue(tree) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/MethodReturningAny.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/MethodReturningAny.scala index ca525445..c2b32e98 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/MethodReturningAny.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/MethodReturningAny.scala @@ -26,7 +26,7 @@ class MethodReturningAny extends Inspection( /// ignore overridden methods as the parent will receive the warning case DefDef(mods, _, _, _, _, _) if mods.isOverride => case DefDef(_, _, _, _, tpt, _) if tpt.tpe =:= typeOf[Any] || tpt.tpe =:= typeOf[AnyRef] => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/PointlessTypeBounds.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/PointlessTypeBounds.scala index c7257225..85e1a4bf 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/PointlessTypeBounds.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/PointlessTypeBounds.scala @@ -20,7 +20,7 @@ class PointlessTypeBounds extends Inspection( case TypeDef(_, _, _, rhs) if rhs.tpe.bounds.isEmptyBounds && rhs.pos != null && (rhs.pos.lineContent.contains("<: Any") || rhs.pos.lineContent.contains(">: Nothing")) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/ProductWithSerializableInferred.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/ProductWithSerializableInferred.scala index cb0e9110..ff0fe35a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/inference/ProductWithSerializableInferred.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/inference/ProductWithSerializableInferred.scala @@ -34,7 +34,7 @@ class ProductWithSerializableInferred extends Inspection( tree match { case ValDef(mods, _, _, _) if mods.hasFlag(Flags.SYNTHETIC) => case ValDef(_, _, tpt, _) if isProductWithSerializable(tpt.tpe) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/PartialFunctionInsteadOfMatch.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/PartialFunctionInsteadOfMatch.scala index 04d6eaae..473a99d1 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/PartialFunctionInsteadOfMatch.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/PartialFunctionInsteadOfMatch.scala @@ -26,17 +26,17 @@ class PartialFunctionInsteadOfMatch extends Inspection( // need to not warn on the partial function style, they use x0$1 case Apply(_, List(Function(List(ValDef(_, name1, _, EmptyTree)), Match(name2, _)))) if name1.toString == name2.toString() => - if (!isPFBind(name1)) context.warn(tree.pos, self) + if (!isPFBind(name1)) context.warn(tree.pos, self, tree.toString.take(500)) case Apply(TypeApply(_, _), List(Function(List(ValDef(_, name1, _, EmptyTree)), Match(name2, _)))) if name1.toString == name2.toString() => - if (!isPFBind(name1)) context.warn(tree.pos, self) + if (!isPFBind(name1)) context.warn(tree.pos, self, tree.toString.take(500)) case TypeApply(_, List(Function(List(ValDef(_, name1, _, EmptyTree)), Match(name2, _)))) if name1.toString == name2.toString() => - if (!isPFBind(name1)) context.warn(tree.pos, self) + if (!isPFBind(name1)) context.warn(tree.pos, self, tree.toString.take(500)) // a => a match { case ...; case ... } // case Apply(_, List(Function(List(ValDef(mods, x1, TypeTree(), EmptyTree)), Match(x2, _)))) // if x1.toString == x2.toString() => - // context.warn(tree.pos, self) + // context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/RepeatedCaseBody.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/RepeatedCaseBody.scala index c8146a3b..54ff4bfe 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/RepeatedCaseBody.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/RepeatedCaseBody.scala @@ -29,7 +29,7 @@ class RepeatedCaseBody extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Match(_, cases) if isRepeated(cases) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/SuspiciousMatchOnClassObject.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/SuspiciousMatchOnClassObject.scala index 68cf42d5..eeaa3477 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/matching/SuspiciousMatchOnClassObject.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/matching/SuspiciousMatchOnClassObject.scala @@ -31,7 +31,7 @@ class SuspiciousMatchOnClassObject extends Inspection( pat.symbol.isModuleOrModuleClass && pat.tpe.typeSymbol.companionClass.isClass && !pat.tpe.typeSymbol.companionClass.isAbstractClass => - context.warn(c.pos, self) + context.warn(c.pos, self, c.toString.take(500)) true case _ => false } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalDoubleConstructor.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalDoubleConstructor.scala index 2c3ecb68..c35f1a52 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalDoubleConstructor.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/BigDecimalDoubleConstructor.scala @@ -26,10 +26,10 @@ class BigDecimalDoubleConstructor extends Inspection( tree match { case Apply(Select(pack, TermName("apply")), arg :: _) if isBigDecimal(pack) && isFloatingPointType(arg) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(100)) case Apply(Select(New(pack), nme.CONSTRUCTOR), arg :: _) if isBigDecimal(pack) && isFloatingPointType(arg) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(100)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/BrokenOddness.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/BrokenOddness.scala index 8dce6646..c8df9350 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/BrokenOddness.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/BrokenOddness.scala @@ -23,7 +23,7 @@ class BrokenOddness extends Inspection( tree match { case Apply(Select(Apply(Select(_, TermName("$percent")), List(Literal(Constant(2)))), TermName("$eq$eq")), List(Literal(Constant(1)))) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/math/ModOne.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/math/ModOne.scala index a067002a..52795a28 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/math/ModOne.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/math/ModOne.scala @@ -23,7 +23,7 @@ class ModOne extends Inspection( tree match { case Apply(Select(lhs, TermName("$percent")), List(Literal(Constant(1)))) if lhs.tpe <:< typeOf[Int] => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameter.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameter.scala index 18bd2497..68ca7d15 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameter.scala @@ -25,7 +25,7 @@ class NullParameter extends Inspection( case Apply(_, _) if tree.tpe.toString == "scala.xml.Elem" => case Apply(_, args) => if (containsNull(args)) - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case DefDef(mods, _, _, _, _, _) if mods.hasFlag(Flag.SYNTHETIC) => case _ => continue(tree) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/option/EitherGet.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/option/EitherGet.scala index bf55c238..55e2bbaf 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/option/EitherGet.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/option/EitherGet.scala @@ -18,9 +18,9 @@ class EitherGet extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Select(Select(_, TermName("right")), TermName("get")) => - context.warn(tree.pos, self, "Use of Either Right Projection get: " + tree.toString().take(500)) + context.warn(tree.pos, self, tree.toString.take(500)) case Select(Select(_, TermName("left")), TermName("get")) => - context.warn(tree.pos, self, "Use of Either Left Projection get: " + tree.toString().take(500)) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/option/ImpossibleOptionSizeCondition.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/option/ImpossibleOptionSizeCondition.scala index d7cbfb95..e9e703f2 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/option/ImpossibleOptionSizeCondition.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/option/ImpossibleOptionSizeCondition.scala @@ -23,7 +23,7 @@ class ImpossibleOptionSizeCondition extends Inspection( tree match { case Apply(Select(Select(Apply(TypeApply(Select(_, Opt2Iterable), _), _), Size), Greater), List(Literal(Constant(x: Int)))) if x > 1 => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(200)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionGet.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionGet.scala index bd43f954..d562bc17 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionGet.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionGet.scala @@ -19,7 +19,7 @@ class OptionGet extends Inspection( tree match { case Select(left, TermName("get")) => if (left.tpe.typeSymbol.fullName == "scala.Option") - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionSize.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionSize.scala index 1ff7e145..9198d811 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionSize.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/option/OptionSize.scala @@ -19,7 +19,7 @@ class OptionSize extends Inspection( tree match { case Select(Apply(option2Iterable, List(_)), TermName("size")) => if (option2Iterable.symbol.fullName == "scala.Option.option2Iterable") - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysInFormat.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysInFormat.scala index e2ed0274..499e05a6 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysInFormat.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysInFormat.scala @@ -20,7 +20,7 @@ class ArraysInFormat extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(_, TermName("format")), args) if containsArrayType(args) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysToString.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysToString.scala index d49bab64..0cd55e7b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysToString.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/ArraysToString.scala @@ -20,7 +20,7 @@ class ArraysToString extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(lhs, ToString), Nil) if isArray(lhs) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/EmptyInterpolatedString.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/EmptyInterpolatedString.scala index 89a153b1..c605a1b1 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/EmptyInterpolatedString.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/EmptyInterpolatedString.scala @@ -18,7 +18,7 @@ class EmptyInterpolatedString extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(Apply(Select(_, TermName("apply")), List(_)), TermName("s")), Nil) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/IncorrectNumberOfArgsToFormat.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/IncorrectNumberOfArgsToFormat.scala index 00fc50e4..67d83b12 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/IncorrectNumberOfArgsToFormat.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/IncorrectNumberOfArgsToFormat.scala @@ -35,7 +35,7 @@ class IncorrectNumberOfArgsToFormat extends Inspection( .filterNot(m => doesNotTakeArguments(m.matched)) .size if (argCount > args.size) - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/SubstringZero.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/SubstringZero.scala index 859c871d..b95ca996 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/SubstringZero.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/SubstringZero.scala @@ -21,7 +21,7 @@ class SubstringZero extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(lhs, Substring), List(Literal(Constant(0)))) if lhs.tpe <:< StringType => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(100)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/string/UnsafeStringContains.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/string/UnsafeStringContains.scala index 5605445b..77a858f5 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/string/UnsafeStringContains.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/string/UnsafeStringContains.scala @@ -33,10 +33,10 @@ class UnsafeStringContains extends Inspection( tree match { case Applied(Select(lhs, Contains), targ :: Nil, (_ :: Nil) :: Nil) if isString(lhs) && !isCompatibleType(targ) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case Applied(Select(lhs, Contains), _, (arg :: Nil) :: Nil) if isString(lhs) && !isCompatibleType(arg) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => continue(tree) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/style/ParameterlessMethodReturnsUnit.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/style/ParameterlessMethodReturnsUnit.scala index 84446df1..9c296ca4 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/style/ParameterlessMethodReturnsUnit.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/style/ParameterlessMethodReturnsUnit.scala @@ -18,7 +18,7 @@ class ParameterlessMethodReturnsUnit extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case DefDef(_, name, _, vparamss, tpt, _) if tpt.tpe.toString == "Unit" && vparamss.isEmpty => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(300)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/style/SimplifyBooleanExpression.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/style/SimplifyBooleanExpression.scala index 8d19887d..d72b2a8a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/style/SimplifyBooleanExpression.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/style/SimplifyBooleanExpression.scala @@ -19,7 +19,7 @@ class SimplifyBooleanExpression extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Apply(Select(_, Equals), List(Literal(Constant(false)))) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(200)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/ConstantIf.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/ConstantIf.scala index 9b27b3db..74718259 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/ConstantIf.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/ConstantIf.scala @@ -26,7 +26,7 @@ class ConstantIf extends Inspection( case LabelDef(_, _, _) => case If(cond, _, _) => if (cond.toString() == "false" || cond.toString() == "true") - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/RedundantFinalizer.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/RedundantFinalizer.scala index f08bfd99..eb959d2b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/RedundantFinalizer.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/RedundantFinalizer.scala @@ -22,7 +22,7 @@ class RedundantFinalizer extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case DefDef(mods, name, _, _, tpt, _) if mods.hasFlag(Flag.OVERRIDE) && name.toString == "finalize" && tpt.toString() == "Unit" => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryConversion.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryConversion.scala index a2adc965..44f20780 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryConversion.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryConversion.scala @@ -19,18 +19,18 @@ class UnnecessaryConversion extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case Select(lhs, TermName("toString")) if lhs.tpe <:< StringClass.tpe => - context.warn(tree.pos, self, "Unnecessary toString on instance of String.") + context.warn(tree.pos, self, tree.toString.take(200), "Unnecessary toString on instance of String.") case Select(lhs, TermName("toInt")) if lhs.tpe <:< IntClass.tpe && Option(lhs.symbol).fold(ifEmpty = true)(_.baseClasses.nonEmpty) => - context.warn(tree.pos, self, "Unnecessary toInt on instance of Int.") + context.warn(tree.pos, self, tree.toString.take(200), "Unnecessary toInt on instance of Int.") case Select(lhs, TermName("toLong")) if lhs.tpe <:< LongClass.tpe => - context.warn(tree.pos, self, "Unnecessary toLong on instance of Long.") + context.warn(tree.pos, self, tree.toString.take(200), "Unnecessary toLong on instance of Long.") case Select(lhs, TermName("toSet")) if isSet(lhs, allowMutableSet = false) => - context.warn(tree.pos, self, "Unnecessary toSet on a Set.") + context.warn(tree.pos, self, tree.toString.take(200), "Unnecessary toSet on a Set.") case Select(lhs, TermName("toList")) if isList(lhs) => - context.warn(tree.pos, self, "Unnecessary toList on a List.") + context.warn(tree.pos, self, tree.toString.take(200), "Unnecessary toList on a List.") case Select(lhs, TermName("toSeq")) if isSeq(lhs) => - context.warn(tree.pos, self, "Unnecessary toSeq on a Seq.") + context.warn(tree.pos, self, tree.toString.take(200), "Unnecessary toSeq on a Seq.") case _ => } continue(tree) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryIf.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryIf.scala index 4364a564..0da97768 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryIf.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnnecessaryIf.scala @@ -18,9 +18,9 @@ class UnnecessaryIf extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case If(_, Literal(Constant(true)), Literal(Constant(false))) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case If(_, Literal(Constant(false)), Literal(Constant(true))) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnusedMethodParameter.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnusedMethodParameter.scala index 3fd99eb3..1aad37f5 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnusedMethodParameter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/UnusedMethodParameter.scala @@ -58,7 +58,7 @@ class UnusedMethodParameter extends Inspection( ) { val paramName = vparam.name.toString if (!usesParameter(paramName, constructorBody) && !usesField(paramName, classBody)) - context.warn(vparam.pos, self) + context.warn(vparam.pos, self, s"Unused constructor parameter (${vparam.name}).") } } @@ -111,7 +111,7 @@ class UnusedMethodParameter extends Inspection( vparam <- vparams ) { if (!usesParameter(vparam.name.toString, rhs)) - context.warn(tree.pos, self) + context.warn(tree.pos, self, s"Unused method parameter ($vparam).") } case _ => continue(tree) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/VarCouldBeVal.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/VarCouldBeVal.scala index 9ece1799..e5aa23c1 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/VarCouldBeVal.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/VarCouldBeVal.scala @@ -59,7 +59,7 @@ class VarCouldBeVal extends Inspection( tree match { case DefDef(_, _, _, _, _, Block(stmt, expr)) => for ((unwritten, definitionTree) <- containsUnwrittenVar(stmt :+ expr)) { - context.warn(definitionTree.pos, self) + context.warn(definitionTree.pos, self, tree.toString.take(200)) } case _ => continue(tree) } diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/AsInstanceOf.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/AsInstanceOf.scala index 45a31147..58eb27a6 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/AsInstanceOf.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/AsInstanceOf.scala @@ -19,7 +19,7 @@ class AsInstanceOf extends Inspection( // this will skip any uses of manifest etc case TypeApply(Select(qual, TermName("asInstanceOf")), _) if qual.toString != "classOf[java.lang.Class]" => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case DefDef(modifiers, _, _, _, _, _) if modifiers.hasFlag(Flag.SYNTHETIC) => // no further case Match(_, cases) => // ignore selector and process cases cases.foreach(traverse) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/IsInstanceOf.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/IsInstanceOf.scala index f3486d12..9e5d401e 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/IsInstanceOf.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/IsInstanceOf.scala @@ -18,7 +18,7 @@ class IsInstanceOf extends Inspection( override def inspect(tree: Tree): Unit = { tree match { case TypeApply(Select(_, TermName("isInstanceOf")), _) => - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case DefDef(modifiers, _, _, _, _, _) if modifiers.hasFlag(Flag.SYNTHETIC) => // avoid partial function stuff case Match(_, cases) => // ignore selector and process cases cases.foreach(traverse) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/TryGet.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/TryGet.scala index 2eb034dc..79dad608 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/TryGet.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/unsafe/TryGet.scala @@ -19,7 +19,7 @@ class TryGet extends Inspection( tree match { case Select(left, TermName("get")) => if (left.tpe.typeSymbol.fullName == "scala.util.Try") - context.warn(tree.pos, self) + context.warn(tree.pos, self, tree.toString.take(500)) case _ => continue(tree) } } diff --git a/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala b/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala index 1673b3c9..d6a654b0 100644 --- a/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/io/HtmlReportWriter.scala @@ -107,9 +107,16 @@ object HtmlReportWriter {  { warning.text }  { warning.inspection } -
- { warning.description } +
+ { warning.explanation }
+ { warning.snippet match { + case None => + case Some(snippet) => +
+ { snippet } +
+ }}
} } diff --git a/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala b/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala index e93bdf9e..f57d583c 100644 --- a/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala @@ -24,7 +24,7 @@ object ScalastyleReportWriter { } private def warningToXml(warning: Warning) = { - + } } diff --git a/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala b/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala index 66df6f7a..d37b8857 100644 --- a/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala @@ -14,6 +14,6 @@ object XmlReportWriter { } private def warning2xml(warning: Warning) = { - + } } diff --git a/src/test/scala/com/sksamuel/scapegoat/inspections/VarCouldBeValTest.scala b/src/test/scala/com/sksamuel/scapegoat/inspections/VarCouldBeValTest.scala index b871459d..799e34ad 100644 --- a/src/test/scala/com/sksamuel/scapegoat/inspections/VarCouldBeValTest.scala +++ b/src/test/scala/com/sksamuel/scapegoat/inspections/VarCouldBeValTest.scala @@ -65,7 +65,9 @@ class VarCouldBeValTest val warningsInOrder = compiler.scapegoat.feedback.warnings.sortBy(_.line) val Seq(barWarning, bazWarning) = warningsInOrder barWarning.line shouldBe 3 + barWarning.snippet.exists(_.contains("var bar: Int = 1")) shouldBe true bazWarning.line shouldBe 5 + bazWarning.snippet.exists(_.contains("var baz: Int = 3")) shouldBe true } } "should not report warning" - { diff --git a/src/test/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameterTest.scala b/src/test/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameterTest.scala index 44ca3fa5..a89dd6bc 100644 --- a/src/test/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameterTest.scala +++ b/src/test/scala/com/sksamuel/scapegoat/inspections/nulls/NullParameterTest.scala @@ -25,6 +25,7 @@ class NullParameterTest extends FreeSpec with Matchers with PluginRunner with On compileCodeSnippet(code) compiler.scapegoat.feedback.warnings.size shouldBe 1 + compiler.scapegoat.feedback.warnings.forall(_.snippet.get.contains("println(null)")) } "should not report warning" - { "for override val in case class" in { diff --git a/src/test/scala/com/sksamuel/scapegoat/inspections/unnecessary/UnusedMethodParameterTest.scala b/src/test/scala/com/sksamuel/scapegoat/inspections/unnecessary/UnusedMethodParameterTest.scala index 1490f90c..a2c80b3e 100644 --- a/src/test/scala/com/sksamuel/scapegoat/inspections/unnecessary/UnusedMethodParameterTest.scala +++ b/src/test/scala/com/sksamuel/scapegoat/inspections/unnecessary/UnusedMethodParameterTest.scala @@ -150,7 +150,10 @@ class UnusedMethodParameterTest val code = """case class Foo(x: Int)(y: Int)""" compileCodeSnippet(code) - compiler.scapegoat.feedback.warnings.size shouldBe 1 + compiler.scapegoat.feedback.warnings match { + case Seq(warning: Warning) => + warning.snippet.get should include("y") + } } "not warn on case class secondary params used as fields" in { @@ -177,7 +180,10 @@ class UnusedMethodParameterTest val code = """class Foo(x: Int)""" compileCodeSnippet(code) - compiler.scapegoat.feedback.warnings.size shouldBe 1 + compiler.scapegoat.feedback.warnings match { + case Seq(warning: Warning) => + warning.snippet.get should include("x") + } } "not warn on non-case class primary params used as fields" in { From 3e2c5eb22474f66b560c6d36e50610e60c42e798 Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Tue, 10 Mar 2020 01:12:23 +0000 Subject: [PATCH 10/18] Bump up sbt. --- build.sbt | 2 +- project/build.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 6d3baa65..c3c90335 100644 --- a/build.sbt +++ b/build.sbt @@ -73,7 +73,7 @@ def check(code: String) = { // runner.reporter.reset val c = runner compileCodeSnippet code val feedback = c.scapegoat.feedback - feedback.warnings map (x => "%-40s %s".format(x.text, x.snippet getOrElse "")) foreach println + feedback.warnings map (x => "%-40s %s %s".format(x.text, x.explanation, x.snippet.getOrElse(""))) foreach println feedback } """ diff --git a/project/build.properties b/project/build.properties index c0bab049..a919a9b5 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.8 From a96a79b0090a453883e15c5c102d2bf5a05e1fea Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Wed, 11 Mar 2020 00:07:15 +0000 Subject: [PATCH 11/18] Remove 2.13 from the build matrix. --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2127eae6..f584bed8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ scala: - 2.12.8 - 2.12.9 - 2.12.10 - - 2.13.0 - 2.13.1 script: - sbt clean test @@ -24,4 +23,3 @@ cache: directories: - $HOME/.ivy2/cache - $HOME/.sbt/boot/ - From 99cf7e587a7b7b3f92e9ada119ab45631ae2e5dc Mon Sep 17 00:00:00 2001 From: Michael Wizner Date: Wed, 11 Mar 2020 00:07:55 +0000 Subject: [PATCH 12/18] Include code snippets in XML reports. --- .../com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala | 2 +- src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala b/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala index f57d583c..9ad7edb2 100644 --- a/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/io/ScalastyleReportWriter.scala @@ -24,7 +24,7 @@ object ScalastyleReportWriter { } private def warningToXml(warning: Warning) = { - + } } diff --git a/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala b/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala index d37b8857..edfcabb4 100644 --- a/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala +++ b/src/main/scala/com/sksamuel/scapegoat/io/XmlReportWriter.scala @@ -14,6 +14,6 @@ object XmlReportWriter { } private def warning2xml(warning: Warning) = { - + } } From 99b14fa656a0aeeb797f172427d75ac0202418a7 Mon Sep 17 00:00:00 2001 From: Greg Oledzki Date: Thu, 12 Mar 2020 14:25:10 +0100 Subject: [PATCH 13/18] Update src/main/scala/com/sksamuel/scapegoat/inspections/LonelySealedTrait.scala --- .../com/sksamuel/scapegoat/inspections/LonelySealedTrait.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/LonelySealedTrait.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/LonelySealedTrait.scala index c61f70c0..7fb62673 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/LonelySealedTrait.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/LonelySealedTrait.scala @@ -9,7 +9,7 @@ class LonelySealedTrait extends Inspection( text = "Lonely sealed trait", defaultLevel = Levels.Error, description = "Checks for sealed traits without any classes extending it.", - explanation = "A sealed traits that is not extended is considered dead code." + explanation = "A sealed trait that is not extended is considered dead code." ) { override def inspector(context: InspectionContext): Inspector = new Inspector(context) { From 509131c388c7a9bf29b560daa9d60542d6953aaf Mon Sep 17 00:00:00 2001 From: Greg Oledzki Date: Thu, 12 Mar 2020 14:25:18 +0100 Subject: [PATCH 14/18] Update src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala --- .../scapegoat/inspections/collections/AvoidSizeEqualsZero.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala index fb798085..61482a24 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeEqualsZero.scala @@ -7,7 +7,7 @@ class AvoidSizeEqualsZero extends Inspection( text = "Avoid Traversable.size == 0", defaultLevel = Levels.Warning, description = "Checks for use of Traversable.size.", - explanation = "Traversable.size can be slow for some data structure, prefer Traversable.isEmpty, which is O(1)." + explanation = "Traversable.size can be slow for some data structures, prefer Traversable.isEmpty, which is O(1)." ) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { From 56be483a5dd202ca76035cde5bc5e3657a0e72c9 Mon Sep 17 00:00:00 2001 From: Greg Oledzki Date: Thu, 12 Mar 2020 14:25:25 +0100 Subject: [PATCH 15/18] Update src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala --- .../inspections/collections/AvoidSizeNotEqualsZero.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala index 3c9563b2..bd61d59b 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/AvoidSizeNotEqualsZero.scala @@ -7,7 +7,7 @@ class AvoidSizeNotEqualsZero extends Inspection( text = "Avoid Traversable.size != 0", defaultLevel = Levels.Warning, description = "Checks for use of Traversable.size.", - explanation = "Traversable.size can be slow for some data structures, prefer Traversable.nonEmpty, which is O(1)." + explanation = ".size can be slow for some data structures, prefer .nonEmpty, which is O(1)." ) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { From 0db44a60d539257009da64e9092133551fcea271 Mon Sep 17 00:00:00 2001 From: Greg Oledzki Date: Thu, 12 Mar 2020 14:25:33 +0100 Subject: [PATCH 16/18] Update src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala --- .../scapegoat/inspections/collections/NegationNonEmpty.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala index fac1be17..13494134 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationNonEmpty.scala @@ -7,7 +7,7 @@ class NegationNonEmpty extends Inspection( text = "!nonEmpty can be replaced with isEmpty", defaultLevel = Levels.Info, description = "Checks whether !nonEmpty can be replaced with isEmpty.", - explanation = "!Traversable.nonEmpty can be replaced with Traversable.isEmpty to make it easier to reason about." + explanation = "!.nonEmpty can be replaced with.isEmpty to make it easier to reason about." ) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { From b5fa0923285116f0351e0e062dfded44fe64368f Mon Sep 17 00:00:00 2001 From: Greg Oledzki Date: Thu, 12 Mar 2020 14:25:39 +0100 Subject: [PATCH 17/18] Update src/main/scala/com/sksamuel/scapegoat/inspections/collections/JavaConversionsUse.scala --- .../scapegoat/inspections/collections/JavaConversionsUse.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/JavaConversionsUse.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/JavaConversionsUse.scala index 01937af2..753d17ad 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/JavaConversionsUse.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/JavaConversionsUse.scala @@ -7,7 +7,7 @@ class JavaConversionsUse extends Inspection( text = "Java conversions", defaultLevel = Levels.Warning, description = "Checks for use of Java conversions.", - explanation = "Use of java conversions can lead to unusual behaviour. It is recommended to use JavaConverters." + explanation = "Use of Java conversions can lead to unusual behaviour. It is recommended to use JavaConverters." ) { def inspector(context: InspectionContext): Inspector = new Inspector(context) { From f120a6666ac2823b9fa7596b3d045c2501b4841d Mon Sep 17 00:00:00 2001 From: Greg Oledzki Date: Thu, 12 Mar 2020 14:25:56 +0100 Subject: [PATCH 18/18] Update src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala --- .../scapegoat/inspections/collections/NegationIsEmpty.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala index 08330149..0b91774a 100644 --- a/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala +++ b/src/main/scala/com/sksamuel/scapegoat/inspections/collections/NegationIsEmpty.scala @@ -7,7 +7,7 @@ class NegationIsEmpty extends Inspection( text = "!isEmpty can be replaced with nonEmpty", defaultLevel = Levels.Info, description = "Checks whether !isEmpty can be replaced with nonEmpty.", - explanation = "!Traversable.isEmpty can be replaced with Traversable.nonEmpty to make it easier to reason about." + explanation = "!.isEmpty can be replaced with.nonEmpty to make it easier to reason about." ) { def inspector(context: InspectionContext): Inspector = new Inspector(context) {