From ffe90c589d0000eaad161d0f6ac8a4016e5c4dce Mon Sep 17 00:00:00 2001 From: mike Date: Sun, 20 Oct 2024 21:57:12 +0300 Subject: [PATCH] Add abstract trait inspections to scala3 (#889) * add abstract trait to scala3 * remove unused imports * reorder imports for scalafix --- README.md | 4 +- .../com/sksamuel/scapegoat/Inspections.scala | 4 +- .../com/sksamuel/scapegoat/Plugin.scala | 1 - .../inspections/traits/AbstractTrait.scala | 41 +++++++++++++++++++ .../traits/AbstractTraitTest.scala | 36 ++++++++++++++++ 5 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/main/scala-3/com/sksamuel/scapegoat/inspections/traits/AbstractTrait.scala create mode 100644 src/test/scala-3/com/sksamuel/scapegoat/inspections/traits/AbstractTraitTest.scala diff --git a/README.md b/README.md index aa26ee52..4769df31 100644 --- a/README.md +++ b/README.md @@ -157,12 +157,12 @@ To suppress warnings globally for the project, use `disabledInspections` or `ove ### Inspections -There are currently 122 inspections for Scala 2, and 1 for Scala 3. +There are currently 122 inspections for Scala 2, and 2 for Scala 3. An overview list is given, followed by a more detailed description of each inspection after the list (todo: finish rest of detailed descriptions) | Name | Brief Description | Default Level | Scala 2 | Scala 3 | |---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|---------|---------| -| AbstractTrait | Check if trait is abstract | Info | Yes | No | +| AbstractTrait | Check if trait is abstract | Info | Yes | Yes | | ArrayEquals | Checks for comparison of arrays using `==` which will always return false | Info | Yes | No | | ArraysInFormat | Checks for arrays passed to String.format | Error | Yes | No | | ArraysToString | Checks for explicit toString calls on arrays | Warning | Yes | No | diff --git a/src/main/scala-3/com/sksamuel/scapegoat/Inspections.scala b/src/main/scala-3/com/sksamuel/scapegoat/Inspections.scala index 945eb501..c5dae87c 100644 --- a/src/main/scala-3/com/sksamuel/scapegoat/Inspections.scala +++ b/src/main/scala-3/com/sksamuel/scapegoat/Inspections.scala @@ -1,11 +1,13 @@ package com.sksamuel.scapegoat import com.sksamuel.scapegoat.inspections.option._ +import com.sksamuel.scapegoat.inspections.traits._ object Inspections { final val inspections: List[Inspection] = List( - new OptionGet + new OptionGet, + new AbstractTrait ) } diff --git a/src/main/scala-3/com/sksamuel/scapegoat/Plugin.scala b/src/main/scala-3/com/sksamuel/scapegoat/Plugin.scala index c082c59f..6433efcf 100644 --- a/src/main/scala-3/com/sksamuel/scapegoat/Plugin.scala +++ b/src/main/scala-3/com/sksamuel/scapegoat/Plugin.scala @@ -32,7 +32,6 @@ class ScapegoatPhase(var configuration: Configuration, override val inspections: extends PluginPhase with ScapegoatBasePlugin { - private[scapegoat] var feedback: Option[FeedbackDotty] = None override def phaseName: String = "scapegoat" diff --git a/src/main/scala-3/com/sksamuel/scapegoat/inspections/traits/AbstractTrait.scala b/src/main/scala-3/com/sksamuel/scapegoat/inspections/traits/AbstractTrait.scala new file mode 100644 index 00000000..f6092552 --- /dev/null +++ b/src/main/scala-3/com/sksamuel/scapegoat/inspections/traits/AbstractTrait.scala @@ -0,0 +1,41 @@ +package com.sksamuel.scapegoat.inspections.traits + +import com.sksamuel.scapegoat.* +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.core.Symbols.ClassSymbol +import dotty.tools.dotc.core.Types.TypeRef +import dotty.tools.dotc.util.SourcePosition + +class AbstractTrait + extends Inspection( + text = "Use of abstract trait", + defaultLevel = Levels.Info, + description = "Traits are automatically abstract.", + explanation = + "The abstract modifier is used in class definitions. It is redundant for traits, and mandatory for all other classes which have incomplete members." + ) { + + import tpd.* + + def inspect(feedback: Feedback[SourcePosition], tree: tpd.Tree)(using ctx: Context): Unit = { + val traverser = new InspectionTraverser { + def traverse(tree: Tree)(using Context): Unit = { + tree match { + case tDef: TypeDef => + tDef.tpe match { + case TypeRef(_, kls: ClassSymbol) + if kls.flags.is(Flags.Trait) && kls.flags.is(Flags.Abstract) => + feedback.warn(tree.sourcePos, self, tree.asSnippet) + case _ => + } + + case _ => + traverseChildren(tree) + } + } + } + traverser.traverse(tree) + } +} diff --git a/src/test/scala-3/com/sksamuel/scapegoat/inspections/traits/AbstractTraitTest.scala b/src/test/scala-3/com/sksamuel/scapegoat/inspections/traits/AbstractTraitTest.scala new file mode 100644 index 00000000..288008df --- /dev/null +++ b/src/test/scala-3/com/sksamuel/scapegoat/inspections/traits/AbstractTraitTest.scala @@ -0,0 +1,36 @@ +package com.sksamuel.scapegoat.inspections.traits + +import com.sksamuel.scapegoat.InspectionTest + +class AbstractTraitTest extends InspectionTest(classOf[AbstractTrait]) { + + "abstract trait use" - { + "should report warning" in { + val code = "abstract trait Test { }" + + val feedback = runner.compileCodeSnippet(code) + feedback.warnings.assertable.size shouldBe 1 + } + + "should not report warning on sealed" in { + val code = "sealed trait Test { val x: Int = 1 }" + + val feedback = runner.compileCodeSnippet(code) + feedback.warnings.assertable.size shouldBe 0 + } + + "should not report warning on trait without modifiers" in { + val code = "trait Test1 { val x: Int = 1 }" + + val feedback = runner.compileCodeSnippet(code) + feedback.warnings.assertable.size shouldBe 0 + } + + "should not report on private trait" in { + val code = "private trait Test1 { val x: Int = 1 }" + + val feedback = runner.compileCodeSnippet(code) + feedback.warnings.assertable.size shouldBe 0 + } + } +}