diff --git a/README.md b/README.md index b3d4aa90..5662deb1 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 123 inspections for Scala 2, and 2 for Scala 3. +There are currently 123 inspections for Scala 2, and 3 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 2697c656..76a189c2 100644 --- a/src/main/scala-3/com/sksamuel/scapegoat/Inspections.scala +++ b/src/main/scala-3/com/sksamuel/scapegoat/Inspections.scala @@ -2,12 +2,14 @@ package com.sksamuel.scapegoat import com.sksamuel.scapegoat.inspections.AvoidRequire import com.sksamuel.scapegoat.inspections.option._ +import com.sksamuel.scapegoat.inspections.traits._ object Inspections { final val inspections: List[Inspection] = List( new OptionGet, - new AvoidRequire + new AvoidRequire, + new AbstractTrait ) } 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 + } + } +}