-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding inspection for unnecessary value store before return (#653)
* Adding inspection for unnecessary value store * Now checking for a case where a value is stored in a block, and then immediately returned in the next line. * Tests for both implicit and explicit scope returns. * Adding test-case for mutually-recursive fns * 121 inspections Co-authored-by: Greg Oledzki <mccartney@users.noreply.github.com>
- Loading branch information
Showing
4 changed files
with
156 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
src/main/scala/com/sksamuel/scapegoat/inspections/unneccesary/StoreBeforeReturn.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.sksamuel.scapegoat.inspections.unneccesary | ||
|
||
import com.sksamuel.scapegoat._ | ||
|
||
class StoreBeforeReturn | ||
extends Inspection( | ||
text = "Unnecessary store before return.", | ||
defaultLevel = Levels.Info, | ||
description = "Checks for storing a value in a block, and immediately returning the value.", | ||
explanation = | ||
"Storing a value and then immediately returning it is equivalent to returning the raw value itself." | ||
) { | ||
|
||
override def inspector(context: InspectionContext): Inspector = | ||
new Inspector(context) { | ||
override def postTyperTraverser: context.Traverser = | ||
new context.Traverser { | ||
import context.global._ | ||
|
||
private def lastExprName(expr: Tree): Option[String] = | ||
expr match { | ||
case Return(Ident(name)) => Some(name.toString()) | ||
case Ident(name) => Some(name.toString()) | ||
case _ => None | ||
} | ||
|
||
override def inspect(tree: context.global.Tree): Unit = | ||
tree match { | ||
case DefDef(_, _, _, _, _, Block(stmts, lastExprInBody)) => | ||
val maybeLastExprName = lastExprName(lastExprInBody) | ||
stmts.lastOption.foreach { | ||
case defn @ ValDef(_, assignmentName, _, _) | ||
if maybeLastExprName.contains(assignmentName.toString()) => | ||
context.warn(defn.pos, self) | ||
case _ => stmts.foreach(inspect) | ||
} | ||
case _ => continue(tree) | ||
} | ||
} | ||
} | ||
} |
112 changes: 112 additions & 0 deletions
112
src/test/scala/com/sksamuel/scapegoat/inspections/unnecessary/StoreBeforeReturnTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package com.sksamuel.scapegoat.inspections.unnecessary | ||
|
||
import com.sksamuel.scapegoat.InspectionTest | ||
import com.sksamuel.scapegoat.inspections.unneccesary.StoreBeforeReturn | ||
|
||
class StoreBeforeReturnTest extends InspectionTest { | ||
|
||
override val inspections = Seq(new StoreBeforeReturn) | ||
|
||
"store value before explicit return" - { | ||
"should report warning" in { | ||
val code = | ||
""" | ||
|object Test { | ||
| def hello(): String = { | ||
| var s = "sammy" | ||
| return s | ||
| } | ||
|} | ||
|""".stripMargin | ||
compileCodeSnippet(code) | ||
compiler.scapegoat.feedback.warnings.size shouldBe 1 | ||
} | ||
|
||
"should report warning in a nested function" in { | ||
val code = | ||
""" | ||
|object Test { | ||
| def foo(): Int = { | ||
| def bar(): Int = { | ||
| val x = 1 | ||
| return x | ||
| } | ||
| return bar() | ||
| } | ||
|} | ||
|""".stripMargin | ||
compileCodeSnippet(code) | ||
compiler.scapegoat.feedback.warnings.size shouldBe 1 | ||
} | ||
} | ||
|
||
"store value before implicit return" - { | ||
"should report warning" in { | ||
val code = | ||
""" | ||
|object Test { | ||
| def hello(): String = { | ||
| var s = "sammy" | ||
| s | ||
| } | ||
|} | ||
|""".stripMargin | ||
compileCodeSnippet(code) | ||
compiler.scapegoat.feedback.warnings.size shouldBe 1 | ||
} | ||
|
||
"should report warning in a nested function" in { | ||
val code = | ||
""" | ||
|object Test { | ||
| def foo(): Int = { | ||
| def bar(): Int = { | ||
| val x = 1 | ||
| x | ||
| } | ||
| bar() | ||
| } | ||
|} | ||
|""".stripMargin | ||
|
||
compileCodeSnippet(code) | ||
compiler.scapegoat.feedback.warnings.size shouldBe 1 | ||
} | ||
} | ||
"store value and modify before return" - { | ||
"should not report warning" in { | ||
val code = | ||
""" | ||
|object Test { | ||
| def hello(): Int = { | ||
| var x = 1 | ||
| x = x + 1 | ||
| return x | ||
| } | ||
|} | ||
|""".stripMargin | ||
compileCodeSnippet(code) | ||
compiler.scapegoat.feedback.warnings.size shouldBe 0 | ||
} | ||
} | ||
|
||
"store value and return for mutually-recusive functions" - { | ||
"should report a warning, and terminate" in { | ||
val code = | ||
""" | ||
|object Test { | ||
| def foo(): Int = { | ||
| var x = bar() | ||
| return x | ||
| } | ||
| def bar(): Int = { | ||
| val x = foo() | ||
| return x | ||
| } | ||
|} | ||
|""".stripMargin | ||
compileCodeSnippet(code) | ||
compiler.scapegoat.feedback.warnings.size shouldBe 2 | ||
} | ||
} | ||
} |