Skip to content

Commit

Permalink
Added BigDecimalScaleWithoutRoundingMode and BooleanParameter
Browse files Browse the repository at this point in the history
Improved documentation in BigDecimalDoubleConstructor
  • Loading branch information
t1b00 committed Sep 12, 2024
1 parent ae9fe34 commit df53ce6
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 2 deletions.
23 changes: 23 additions & 0 deletions input/src/main/scala/fix/BigDecimalScaleWithoutRoundingMode.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
rule = BigDecimalScaleWithoutRoundingMode
*/
package fix

import scala.math.BigDecimal.RoundingMode

object BigDecimalScaleWithoutRoundingMode {
def test(): Unit = {
val b = BigDecimal(10)
b.setScale(2) // assert: BigDecimalScaleWithoutRoundingMode
BigDecimal(10).setScale(2) // assert: BigDecimalScaleWithoutRoundingMode

val b1 = new java.math.BigDecimal(2)
b1.setScale(2) // assert: BigDecimalScaleWithoutRoundingMode
new java.math.BigDecimal(2).setScale(2) // assert: BigDecimalScaleWithoutRoundingMode

b.setScale(2, RoundingMode.UP) // scalafix: ok;
BigDecimal(10).setScale(2, RoundingMode.DOWN) // scalafix: ok;
new java.math.BigDecimal(2).setScale(2, RoundingMode.FLOOR) // scalafix: ok;

}
}
15 changes: 15 additions & 0 deletions input/src/main/scala/fix/BooleanParameter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
rule = BooleanParameter
*/
package fix

object BooleanParameter {
def foo(bool: Boolean) = 4 // assert: BooleanParameter

def foo2(value: Int)(bool: Boolean) = 4 // assert: BooleanParameter

def foo3(value: Int) = 4 // scalafix: ok;
def foo3(value: Int)(dob: Double) = 4 // scalafix: ok;

final case class Test(bool: Boolean) // scalafix: ok;
}
4 changes: 3 additions & 1 deletion rules/src/main/resources/META-INF/services/scalafix.v1.Rule
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ fix.ArraysEquals
fix.ArraysToString
fix.AsInstanceOf
fix.AvoidToMinusOne
fix.BigDecimalDoubleConstructor
fix.BigDecimalDoubleConstructor
fix.BigDecimalScaleWithoutRoundingMode
fix.BooleanParameter
3 changes: 2 additions & 1 deletion rules/src/main/scala/fix/BigDecimalDoubleConstructor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class BigDecimalDoubleConstructor extends SemanticRule("BigDecimalDoubleConstruc

doc.tree.collect {
// Corresponds to BigDecimal(...) where ... is a floating point / double value.
case t @ Term.Apply.After_4_6_0(Term.Name("BigDecimal"), Term.ArgClause(List(arg), _)) if isFloatOrDouble(arg) => Patch.lint(diag(t.pos))
case t @ Term.Apply.After_4_6_0(Term.Name("BigDecimal"), Term.ArgClause(List(arg), _)) if isFloatOrDouble(arg) => Patch.lint(diag(t.pos))
// Corresponds to new java.math.BigDecimal(...) where ... is a floating point / double value.
case t @ Init.After_4_6_0(Type.Select(Term.Select(Term.Name("java"), Term.Name("math")), Type.Name("BigDecimal")), _, List(Term.ArgClause(List(arg), _))) if isFloatOrDouble(arg) => Patch.lint(diag(t.pos))
}.asPatch
}
Expand Down
32 changes: 32 additions & 0 deletions rules/src/main/scala/fix/BigDecimalScaleWithoutRoundingMode.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
rule = BigDecimalScaleWithoutRoundingMode
*/
package fix

import scalafix.lint.LintSeverity
import scalafix.v1._

import scala.meta._

class BigDecimalScaleWithoutRoundingMode extends SemanticRule("BigDecimalScaleWithoutRoundingMode") {

private def diag(pos: Position) = Diagnostic(
"",
"Checks for use of 'setScale()' on a BigDecimal without setting the rounding mode can throw an exception.",
pos,
"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)'?",
LintSeverity.Warning
)

override def fix(implicit doc: SemanticDocument): Patch = {

def isBigDecimal(arg: Stat) = Util.matchType(arg, "scala/math/BigDecimal") || Util.matchType(arg, "java/math/BigDecimal")

doc.tree.collect {
// Corresponds to b.setScale(2) where b is a BigDecimal or BigDecimal(2).setScale(2), i.e. there is only one argument passed.
// Also corresponds to new java.math.BigDecimal(2).setScale(2) or b.setScale(2) where b is a java.math.BigDecimal
case t @ Term.Apply.After_4_6_0(Term.Select(_, Term.Name("setScale")), Term.ArgClause(List(_), _)) if isBigDecimal(t) => Patch.lint(diag(t.pos))
}.asPatch
}

}
47 changes: 47 additions & 0 deletions rules/src/main/scala/fix/BooleanParameter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
rule = BooleanParameter
*/
package fix

import scalafix.lint.LintSeverity
import scalafix.v1._

import scala.meta._

class BooleanParameter extends SemanticRule("BooleanParameter") {

private def diag(pos: Position) = Diagnostic(
"",
"Checks for functions that have a Boolean parameter.",
pos,
"Method has Boolean parameter. Consider splitting into two methods or using a case class.",
LintSeverity.Warning
)

override def fix(implicit doc: SemanticDocument): Patch = {

def shouldBeIgnored(mods: List[Mod]) = mods.exists {
case Mod.Override() => true
case Mod.Annot(Init.After_4_6_0(Type.Name("getter" | "setter"), _, _)) => true
case Mod.Abstract() => true
case _ => false
}

def hasBooleanParameter(paramClauseGroups: List[Member.ParamClauseGroup]) = paramClauseGroups.exists {
case Member.ParamClauseGroup(_, paramClauses) => paramClauses.exists {
case Term.ParamClause(params, _) => params.exists {
case Term.Param(_, _, Some(Type.Name("Boolean")), _) => true
case _ => false
}
case _ => false
}
case _ => false
}

doc.tree.collect {
// Corresponds to def foo(..., bool: Boolean, ...) where ... is a list of parameters (possibly empty). Also handles function with multiple param clauses
case d @ Defn.Def.After_4_7_3(mods, _, paramClauseGroups, _, _) if !shouldBeIgnored(mods) && hasBooleanParameter(paramClauseGroups) => Patch.lint(diag(d.pos))
}.asPatch
}

}

0 comments on commit df53ce6

Please sign in to comment.