Skip to content

Commit 249a070

Browse files
fntzsaeltz
andauthored
Add require check (#888)
* require check for scala 2 * add check for scala 3 * add . to the end for description * reorder imports for scalafix * fix review --------- Co-authored-by: Bendix Sältz <bendix@saeltz.de>
1 parent 5808954 commit 249a070

File tree

7 files changed

+199
-1
lines changed

7 files changed

+199
-1
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ To suppress warnings globally for the project, use `disabledInspections` or `ove
157157

158158
### Inspections
159159

160-
There are currently 122 inspections for Scala 2, and 2 for Scala 3.
160+
There are currently 123 inspections for Scala 2, and 3 for Scala 3.
161161
An overview list is given, followed by a more detailed description of each inspection after the list (todo: finish rest of detailed descriptions)
162162

163163
| Name | Brief Description | Default Level | Scala 2 | Scala 3 |
@@ -168,6 +168,7 @@ An overview list is given, followed by a more detailed description of each inspe
168168
| ArraysToString | Checks for explicit toString calls on arrays | Warning | Yes | No |
169169
| AsInstanceOf | Checks for use of `asInstanceOf` | Warning | Yes | No |
170170
| AvoidOperatorOverload | Checks for mental symbolic method names | Info | Yes | No |
171+
| AvoidRequire | Use of require | Warning | Yes | Yes |
171172
| AvoidSizeEqualsZero | Traversable.size can be slow for some data structure, prefer .isEmpty | Warning | Yes | No |
172173
| AvoidSizeNotEqualsZero | Traversable.size can be slow for some data structure, prefer .nonEmpty | Warning | Yes | No |
173174
| AvoidToMinusOne | Checks for loops that use `x to n-1` instead of `x until n` | Info | Yes | No |

src/main/scala-2/com/sksamuel/scapegoat/Inspections.scala

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ object Inspections extends App {
2727

2828
def inspections: Seq[Inspection] =
2929
Seq(
30+
new AvoidRequire,
3031
new ArrayEquals,
3132
new ArraysInFormat,
3233
new ArraysToString,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.sksamuel.scapegoat.inspections
2+
3+
import com.sksamuel.scapegoat.{Inspection, InspectionContext, Inspector, Levels}
4+
5+
class AvoidRequire extends
6+
Inspection(
7+
text = "Use of require",
8+
defaultLevel = Levels.Warning,
9+
description = "Use require in code.",
10+
explanation = "Using require throws an untyped Exception."
11+
) {
12+
13+
def inspector(ctx: InspectionContext): Inspector =
14+
new Inspector(ctx) {
15+
override def postTyperTraverser: context.Traverser =
16+
new context.Traverser {
17+
18+
import context.global._
19+
20+
override def inspect(tree: Tree): Unit = {
21+
tree match {
22+
case Select(lhs, TermName("require")) if lhs.tpe.typeSymbol.fullName == "scala.Predef" =>
23+
context.warn(tree.pos, self, tree.toString.take(200))
24+
case _ =>
25+
continue(tree)
26+
}
27+
}
28+
}
29+
}
30+
31+
}

src/main/scala-3/com/sksamuel/scapegoat/Inspections.scala

+2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package com.sksamuel.scapegoat
22

3+
import com.sksamuel.scapegoat.inspections.AvoidRequire
34
import com.sksamuel.scapegoat.inspections.option._
45
import com.sksamuel.scapegoat.inspections.traits._
56

67
object Inspections {
78

89
final val inspections: List[Inspection] = List(
910
new OptionGet,
11+
new AvoidRequire,
1012
new AbstractTrait
1113
)
1214

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.sksamuel.scapegoat.inspections
2+
3+
import com.sksamuel.scapegoat.*
4+
import dotty.tools.dotc.ast.Trees.*
5+
import dotty.tools.dotc.ast.tpd
6+
import dotty.tools.dotc.core.Contexts.Context
7+
import dotty.tools.dotc.core.Symbols.Symbol
8+
import dotty.tools.dotc.core.Types.TermRef
9+
import dotty.tools.dotc.util.SourcePosition
10+
11+
class AvoidRequire
12+
extends Inspection(
13+
text = "Use of require",
14+
defaultLevel = Levels.Warning,
15+
description = "Use require in code.",
16+
explanation = "Using require throws an untyped Exception."
17+
) {
18+
19+
import tpd.*
20+
21+
def inspect(feedback: Feedback[SourcePosition], tree: tpd.Tree)(using ctx: Context): Unit = {
22+
val traverser = new InspectionTraverser {
23+
def traverse(tree: Tree)(using Context): Unit = {
24+
tree match {
25+
case Apply(ident: Ident, _) if ident.name.toString == "require" =>
26+
ident.tpe.normalizedPrefix match {
27+
case TermRef(tx, nm: Symbol)
28+
if nm.toString == "object Predef" &&
29+
tx.normalizedPrefix.typeSymbol.name.toString == "<root>" =>
30+
feedback.warn(tree.sourcePos, self, tree.asSnippet)
31+
case x =>
32+
}
33+
case _ => traverseChildren(tree)
34+
}
35+
}
36+
}
37+
traverser.traverse(tree)
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.sksamuel.scapegoat.inspections
2+
3+
import com.sksamuel.scapegoat.{Inspection, InspectionTest}
4+
5+
class AvoidRequireTest extends InspectionTest {
6+
override val inspections = Seq[Inspection](new AvoidRequire)
7+
8+
"require use" - {
9+
"should return warning in method" in {
10+
val code =
11+
"""
12+
object Test {
13+
def test(x: Int): Int = {
14+
require(x == 1)
15+
x
16+
}
17+
}
18+
""".stripMargin
19+
20+
compileCodeSnippet(code)
21+
compiler.scapegoat.feedback.warnings.size shouldBe 1
22+
}
23+
24+
"should return warning in class" in {
25+
val code =
26+
"""
27+
class Test(val x: Int) {
28+
require(x == 1, "oops")
29+
}
30+
""".stripMargin
31+
32+
compileCodeSnippet(code)
33+
compiler.scapegoat.feedback.warnings.size shouldBe 1
34+
}
35+
36+
"should not return warning on own require method" in {
37+
val code =
38+
"""
39+
object T {
40+
def require(x: Boolean): Boolean = false
41+
42+
def foo(): Boolean = {
43+
require(1 == 1)
44+
}
45+
}
46+
""".stripMargin
47+
48+
compileCodeSnippet(code)
49+
compiler.scapegoat.feedback.warnings.size shouldBe 0
50+
}
51+
52+
"should not return warning if no require" in {
53+
val code =
54+
"""
55+
class Test(val x: Int) { }
56+
""".stripMargin
57+
58+
compileCodeSnippet(code)
59+
compiler.scapegoat.feedback.warnings.size shouldBe 0
60+
}
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.sksamuel.scapegoat.inspections
2+
3+
import com.sksamuel.scapegoat.InspectionTest
4+
5+
class AvoidRequireTest extends InspectionTest(classOf[AvoidRequire]) {
6+
7+
"require use" - {
8+
"should return warning in method" in {
9+
val code =
10+
"""
11+
object Test {
12+
def test(x: Int): Int = {
13+
require(x == 1)
14+
x
15+
}
16+
}
17+
""".stripMargin
18+
19+
val feedback = runner.compileCodeSnippet(code)
20+
feedback.warnings.assertable.size shouldBe 1
21+
}
22+
23+
"should return warning in class" in {
24+
val code =
25+
"""
26+
class Test(val x: Int) {
27+
require(x == 1, "oops")
28+
}
29+
""".stripMargin
30+
31+
val feedback = runner.compileCodeSnippet(code)
32+
feedback.warnings.assertable.size shouldBe 1
33+
}
34+
35+
"should not return warning on own require method" in {
36+
val code =
37+
"""
38+
object T {
39+
def require(x: Boolean): Boolean = false
40+
41+
def foo(): Boolean = {
42+
require(1 == 1)
43+
}
44+
}
45+
""".stripMargin
46+
47+
val feedback = runner.compileCodeSnippet(code)
48+
feedback.warnings.assertable.size shouldBe 0
49+
}
50+
51+
"should not return warning if no require" in {
52+
val code =
53+
"""
54+
class Test(val x: Int) { }
55+
""".stripMargin
56+
57+
val feedback = runner.compileCodeSnippet(code)
58+
feedback.warnings.assertable.size shouldBe 0
59+
}
60+
}
61+
62+
}

0 commit comments

Comments
 (0)