Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE2] Work on more rules for linter #1871

Merged
merged 25 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
93e4776
Work on more rules
t1b00 May 10, 2024
4f0121b
Built IllegalFormatString rule (still some input values left to add) …
t1b00 May 17, 2024
3c4317a
Merge branch 'refs/heads/work-be2-thibault-linter_more_rules' into wo…
t1b00 May 17, 2024
b4dbf95
Added tests in format string rules, added support for literal.format(…
t1b00 May 17, 2024
ecfda68
Added IncorrectNumberOfArgsToFormat rule, reformatted other rules (co…
t1b00 May 17, 2024
2d3de5e
Added IncorrectNumberOfArgsToFormat rule, reformatted other rules (co…
t1b00 May 17, 2024
dc9cf71
Implemented IncorrectlyNamedExceptions
t1b00 May 22, 2024
8c562cb
Simplified test
t1b00 May 22, 2024
a495c08
Cleaned-up code
t1b00 May 22, 2024
e6b67f9
Implemented LonelySealedTrait
t1b00 May 23, 2024
80c99a7
Implemented LonelySealedTrait
t1b00 May 23, 2024
6999a27
Merge remote-tracking branch 'origin/work-be2-thibault-linter_master'…
t1b00 May 23, 2024
0680e5f
Added comments
t1b00 May 23, 2024
d5f9381
Added MapGetAndGetOrElse
t1b00 May 23, 2024
65c12bf
Added NanComparison rule
t1b00 May 29, 2024
ecbe196
Removed useless code in IncorrectNumberOfArgsToFormat
t1b00 May 29, 2024
e4aa58b
Added OptionGet and OptionSize
t1b00 May 29, 2024
cf66a86
Added StripMarginOnRegex rule
t1b00 May 30, 2024
70a9eb5
Removed unecessary println
t1b00 May 30, 2024
10add0c
Simplified code for StripMarginOnRegex
t1b00 May 30, 2024
3681835
Added TryGet rule
t1b00 May 30, 2024
39dfad3
Fixed wrong diag
t1b00 May 30, 2024
a836771
Made getType null safe
t1b00 May 31, 2024
4eb6797
Uncommented code
t1b00 Jun 4, 2024
9c32274
Added .equals() support in NanComparison
t1b00 Jun 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ object ArraysInFormat {
val str = f"Here are my cool elements ${array}" // assert: ArraysInFormat
val str2 = s"Here are my cool elements ${array}" // assert: ArraysInFormat
String.format("Here are my cool elements %d", array) // assert: ArraysInFormat
"Here are my cool elements %d".format(array) // assert: ArraysInFormat
val str3 = "Here are my cool elements %d"
str3.format(array) // assert: ArraysInFormat
String.format("Here are my cool elements %d", array) /* assert: ArraysInFormat
^^^^^
Array passed to format / interpolate string
*/

"Here are my cool elements %d".format(13) // scalafix: ok;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ package fix
object EmptyInterpolatedString {
def test(): Unit = {
print(f"Here's my cute interpolation") // assert: EmptyInterpolatedString
print(s"Here's my amazing interpolationg") // assert: EmptyInterpolatedString
print(String.format("I'm hungry!")) // assert: EmptyInterpolatedString
print(s"Here's my amazing interpolation") // assert: EmptyInterpolatedString
String.format("I'm hungry!") // assert: EmptyInterpolatedString
val str = "I'm hungry!"
str.format() // assert: EmptyInterpolatedString
"I'm hungry!".format() // assert: EmptyInterpolatedString
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
rule = IllegalFormatString
*/
package fix

object IllegalFormatString {
def test(): Unit = {
val name: String = "John"
val age: Integer = 30
val illegalFormatString1 = "%s is %d years old, %d"
String.format(illegalFormatString1, name, age) // assert: IllegalFormatString

illegalFormatString1.format(name, age) // assert: IllegalFormatString

"%s is %d years old, %d".format(name, age) // assert: IllegalFormatString

"%5.5q".format("sam") // assert: IllegalFormatString

"% s".format("sam") // assert: IllegalFormatString

"%qs".format("sam") // assert: IllegalFormatString

"%.-5s".format("sam") // assert: IllegalFormatString

"%.s".format("sam") // assert: IllegalFormatString

"%<s %s".format("sam", "sam") // assert: IllegalFormatString

"%.2f %s".format(14.5, "sammmmmmmmm") // scalafix: ok;
"%010d".format(0) // scalafix: ok;

"%s is %d years old, %d".format(name, age, 145) // scalafix: ok;


}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
rule = IncorrectNumberOfArgsToFormat
*/
package fix

object IncorrectNumberOfArgsToFormat {
def test() = {
val name: String = "John"
val age: Integer = 30
val illegalFormatString1 = "%s is %d years old, %d"
illegalFormatString1.format(name, age, 134, 54, "Olivia") // assert: IncorrectNumberOfArgsToFormat
illegalFormatString1.format(name) // assert: IncorrectNumberOfArgsToFormat

"%s is %d years old, %d".format(name, age, 134, 54, "Olivia") // assert: IncorrectNumberOfArgsToFormat
"%s is %d years old, %d".format(name) // assert: IncorrectNumberOfArgsToFormat

String.format(illegalFormatString1, name) // assert: IncorrectNumberOfArgsToFormat

illegalFormatString1.format(name, age, 134) // scalafix: ok;
"%s is %d years old, %d".format(name, age, 134) // scalafix: ok;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
rule = IncorrectlyNamedExceptions
*/
package fix

// Test values taken from Scapegoat test
object IncorrectlyNamedExceptions {
class NotException // scalafix: ok;

class IsException extends Exception // scalafix: ok;

class IsNotNamedCorrectly extends Exception // assert: IncorrectlyNamedExceptions

class Is extends Exception // assert: IncorrectlyNamedExceptions

class IsChild extends Is // assert: IncorrectlyNamedExceptions

class IsChildException // scalafix: ok;

class IsRuntimeException extends RuntimeException // scalafix: ok;

class IsRuntime extends Exception // assert: IncorrectlyNamedExceptions

}

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

// Test values taken from Scapegoat test
object LonelySealedTrait {

sealed trait NoImpl // assert: LonelySealedTrait

sealed trait Impl1 // scalafix: ok;
object Implemented extends Impl1

sealed trait Impl2 // scalafix: ok;
case object Implemented2 extends Impl2

sealed trait Impl3 // scalafix: ok;
case object Implemented3 extends Serializable with Impl3

sealed trait Impl4 // scalafix: ok;
case class Implemented4() extends Impl4

sealed trait Impl5 // scalafix: ok;
class Implemented extends Impl5

sealed trait Impl6 // scalafix: ok;
case class Implemented6(name: String) extends Serializable with Impl6

sealed trait Impl7 // scalafix: ok;
class Implemented7(name: String) extends Serializable with Impl7

sealed trait Impl8 // scalafix: ok;
case class Implemented8(name: String) extends Impl8

sealed trait Impl9 // scalafix: ok;
class Implemented9(name: String) extends Impl9

sealed trait Impl10 // scalafix: ok;
sealed trait Impl11 // scalafix: ok;
case class Implemented10(name: String) extends Impl10
case class Implemented11(name: String) extends Impl11

trait AnalyzerFilter {
def name: String
}

trait AnalyzerFilterDefinition {
def filterType: String
}

sealed trait CharFilter extends AnalyzerFilter // scalafix: ok;

sealed trait CharFilterDefinition extends CharFilter with AnalyzerFilterDefinition // scalafix: ok;

case object HtmlStripCharFilter extends CharFilter {
val name = "html_strip"
}

case class MappingCharFilter(name: String, mappings: (String, String)*)
extends CharFilterDefinition {
val filterType = "mapping"
}

case class PatternReplaceCharFilter(name: String, pattern: String, replacement: String)
extends CharFilterDefinition {
val filterType = "pattern_replace"
}

sealed abstract class MultiMode(val elastic: String) // scalafix: ok;
case object MultiMode {
case object Min extends MultiMode("min")
case object Max extends MultiMode("max")
case object Sum extends MultiMode("sum")
case object Avg extends MultiMode("avg")
}

sealed trait A[S] // scalafix: ok;
case object B extends A[String]
case object C extends A[BigDecimal]

sealed abstract class IndexOptionsNoImplementation(val value: String) // assert: LonelySealedTrait

sealed abstract class IndexOptions(val value: String) // scalafix: ok;
object IndexOptions {
case object Docs extends IndexOptions("docs")
case object Freqs extends IndexOptions("freqs")
case object Positions extends IndexOptions("positions")
}

sealed trait D // scalafix: ok;
sealed trait E extends D
sealed trait F extends E
object F1 extends F

sealed class G // scalafix: ok;
sealed class H extends G
sealed class I extends H
object I1 extends I

sealed trait J // scalafix: ok;
sealed trait K extends J
sealed class L extends K
object L1 extends L


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
rule = MapGetAndGetOrElse
*/
package fix

object MapGetAndGetOrElse {
def test() = {
val numMap1 = Map(1 -> "one", 2 -> "two")
numMap1.get(1).getOrElse("unknown") // assert: MapGetAndGetOrElse


val numMap2 = scala.collection.mutable.Map("one" -> 1, "two" -> 2)
numMap2.get("one").getOrElse(-1) // assert: MapGetAndGetOrElse

Map("John" -> "Smith", "Peter" -> "Rabbit").get("Sarah").getOrElse("-") // assert: MapGetAndGetOrElse

}

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

object NanComparison {

def test() = {
val d = 0.5d
d == Double.NaN // assert: NanComparison
Double.NaN == d // assert: NanComparison
d.equals(Double.NaN) // assert: NanComparison
Double.NaN.equals(d) // assert: NanComparison

val f = 0.5f
f == Double.NaN // assert: NanComparison
Double.NaN == f // assert: NanComparison

val g = Double.NaN
f == g // assert: NanComparison
g == f // assert: NanComparison

d == g // assert: NanComparison
g == d // assert: NanComparison
d.equals(g) // assert: NanComparison
g.equals(d) // assert: NanComparison
}

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

object OptionGet {
def test() = {
val o = Option("olivia")
o.get // assert: OptionGet

Option("layla").get // assert: OptionGet
}
}
13 changes: 13 additions & 0 deletions be2-scala/linter/input/src/main/scala/fix/OptionSize.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
rule = OptionSize
*/
package fix

object OptionSize {
def test() = {
val o = Option("olivia")
o.size // assert: OptionSize

Option("layla").size // assert: OptionSize
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
rule = StripMarginOnRegex
*/
package fix

object StripMarginOnRegex {
def test() = {
val regex = "match|this".stripMargin.r // assert: StripMarginOnRegex
"match|that".stripMargin.r // assert: StripMarginOnRegex
"match_this".stripMargin.r // scalafix: ok;
"match|this".r // scalafix: ok;
}
}
15 changes: 15 additions & 0 deletions be2-scala/linter/input/src/main/scala/fix/TryGet.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
rule = TryGet
*/
package fix

import scala.util.Try

object TryGet {
def test() = {
val o = Try { println("mojave") }
o.get // assert: TryGet

Try { println("ghost") }.get // assert: TryGet
}
}
43 changes: 43 additions & 0 deletions be2-scala/linter/project/TargetAxis.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import sbt._
import sbt.internal.ProjectMatrix
import sbtprojectmatrix.ProjectMatrixPlugin.autoImport._

/** Use on ProjectMatrix rows to tag an affinity to a custom scalaVersion */
case class TargetAxis(scalaVersion: String) extends VirtualAxis.WeakAxis {

private val scalaBinaryVersion = CrossVersion.binaryScalaVersion(scalaVersion)

override val idSuffix = s"Target${scalaBinaryVersion.replace('.', '_')}"
override val directorySuffix = s"target$scalaBinaryVersion"
}

object TargetAxis {

private def targetScalaVersion(virtualAxes: Seq[VirtualAxis]): String =
virtualAxes.collectFirst { case a: TargetAxis => a.scalaVersion }.get

/** When invoked on a ProjectMatrix with a TargetAxis, lookup the project generated by `matrix` with a scalaVersion matching the one declared in that TargetAxis, and resolve `key`.
*/
def resolve[T](
matrix: ProjectMatrix,
key: TaskKey[T]
): Def.Initialize[Task[T]] =
Def.taskDyn {
val sv = targetScalaVersion(virtualAxes.value)
val project = matrix.finder().apply(sv)
Def.task((project / key).value)
}

/** When invoked on a ProjectMatrix with a TargetAxis, lookup the project generated by `matrix` with a scalaVersion matching the one declared in that TargetAxis, and resolve `key`.
*/
def resolve[T](
matrix: ProjectMatrix,
key: SettingKey[T]
): Def.Initialize[T] =
Def.settingDyn {
val sv = targetScalaVersion(virtualAxes.value)
val project = matrix.finder().apply(sv)
Def.setting((project / key).value)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,13 @@ fix.CatchNpe
fix.ComparingFloatingTypes
fix.EmptyInterpolatedString
fix.ImpossibleOptionSizeCondition
fix.IllegalFormatString
fix.IncorrectNumberOfArgsToFormat
fix.IncorrectlyNamedExceptions
fix.LonelySealedTrait
fix.MapGetAndGetOrElse
fix.NanComparison
fix.OptionGet
fix.OptionSize
fix.StripMarginOnRegex
fix.TryGet
Loading
Loading