Skip to content

Commit

Permalink
add MyScalafixRuleRule
Browse files Browse the repository at this point in the history
  • Loading branch information
xuwei-k committed Jan 12, 2024
1 parent 0018725 commit 5b30dff
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 18 deletions.
51 changes: 33 additions & 18 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,35 @@ commonSettings

publish / skip := true

lazy val resourceGenSettings = Def.settings(
Compile / resourceGenerators += Def.task {
val rules = (Compile / compile).value
.asInstanceOf[sbt.internal.inc.Analysis]
.apis
.internal
.collect {
case (className, analyzed) if analyzed.api.classApi.structure.parents.collect { case p: xsbti.api.Projection =>
p.id
}.exists(Set("SyntacticRule", "SemanticRule")) =>
className
}
.toList
.sorted
assert(rules.nonEmpty)
val output = (Compile / resourceManaged).value / "META-INF" / "services" / "scalafix.v1.Rule"
IO.writeLines(output, rules)
Seq(output)
}.taskValue,
)

lazy val myRuleRule = project.settings(
commonSettings,
scalaVersion := V.scala212,
libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % V.scalafixVersion,
resourceGenSettings,
publish / skip := true
)

lazy val rules = projectMatrix
.settings(
commonSettings,
Expand All @@ -89,33 +118,19 @@ lazy val rules = projectMatrix
Nil
}
},
Compile / resourceGenerators += Def.task {
val rules = (Compile / compile).value
.asInstanceOf[sbt.internal.inc.Analysis]
.apis
.internal
.collect {
case (className, analyzed) if analyzed.api.classApi.structure.parents.collect {
case p: xsbti.api.Projection => p.id
}.exists(Set("SyntacticRule", "SemanticRule")) =>
className
}
.toList
.sorted
assert(rules.nonEmpty)
val output = (Compile / resourceManaged).value / "META-INF" / "services" / "scalafix.v1.Rule"
IO.writeLines(output, rules)
Seq(output)
}.taskValue,
resourceGenSettings,
)
.defaultAxes(VirtualAxis.jvm)
.jvmPlatform(rulesCrossVersions)

lazy val rules212 = rules
.jvm(V.scala212)
.enablePlugins(ScriptedPlugin)
.enablePlugins(ScalafixPlugin)
.dependsOn(myRuleRule % ScalafixConfig)
.settings(
Test / test := (Test / test).dependsOn(scripted.toTask("")).value,
Compile / compile := (Compile / compile).dependsOn((Compile / scalafix).toTask(" MyScalafixRuleRule")).value,
scriptedBufferLog := false,
scriptedLaunchOpts += ("-Dscalafix-rules.version=" + version.value),
scriptedLaunchOpts += ("-Dscalafix.version=" + _root_.scalafix.sbt.BuildInfo.scalafixVersion),
Expand Down
93 changes: 93 additions & 0 deletions myRuleRule/src/main/scala/fix/MyScalafixRuleRule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package fix

import scala.meta.Defn
import scala.meta.Init
import scala.meta.Lit
import scala.meta.Mod
import scala.meta.Template
import scala.meta.Term
import scala.meta.Type
import scalafix.Patch
import scalafix.lint.Diagnostic
import scalafix.lint.LintSeverity
import scalafix.v1.SyntacticDocument
import scalafix.v1.SyntacticRule

class MyScalafixRuleRule extends SyntacticRule("MyScalafixRuleRule") {
private[this] def when(cond: Boolean)(patch: => Patch): Patch = {
if (cond) {
patch
} else {
Patch.empty
}
}

override def fix(implicit doc: SyntacticDocument): Patch = {
doc.tree.collect {
case Defn.Class.After_4_6_0(
_,
className,
_,
primaryCtor,
Template.After_4_4_0(
_,
List(
Init.After_4_6_0(
Type.Name("SyntacticRule" | "SemanticRule"),
_,
List(Term.ArgClause(Lit.String(ruleName) :: Nil, _))
)
),
_,
stats,
_
)
) =>
val withConfigurationMethodOpt =
stats.collectFirst {
case d: Defn.Def if d.name.value == "withConfiguration" && d.mods.exists(_.is[Mod.Override]) =>
d
}
Seq(
when(className.value != ruleName) {
Patch.lint(
Diagnostic(
id = "",
message = s"${className} != ${ruleName}",
position = className.pos,
severity = LintSeverity.Error
)
)
},
when(primaryCtor.paramClauses.nonEmpty) {
withConfigurationMethodOpt match {
case Some(withConfigurationMethod) =>
when(
withConfigurationMethod.body.collect {
case Lit.String(x) if x == ruleName => ()
}.isEmpty
) {
Patch.lint(
Diagnostic(
id = "",
message = "maybe incorrect `withConfiguration` method",
position = className.pos,
severity = LintSeverity.Error
)
)
}
case None =>
Patch.lint(
Diagnostic(
id = "",
message = "there is primary constructor args but not defined `withConfiguration` method",
position = className.pos,
severity = LintSeverity.Error
)
)
}
}
).asPatch
}.asPatch
}
}

0 comments on commit 5b30dff

Please sign in to comment.