Skip to content

Commit

Permalink
Add com-lihaoyi/acyclic to linting scala (com-lihaoyi#3605)
Browse files Browse the repository at this point in the history
  • Loading branch information
lihaoyi authored Sep 26, 2024
1 parent 2adfe28 commit 41ef503
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 1 deletion.
6 changes: 5 additions & 1 deletion docs/modules/ROOT/pages/Linting_Scala_Projects.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ It can also perform automated refactoring.
Mill supports Scalafix through the Mill-Scalafix third party module. See the module documentation
for more details:

* https://github.com/joan38/mill-scalafix
* https://github.com/joan38/mill-scalafix

== Acyclic Files Enforcement

include::example/scalalib/linting/3-acyclic.adoc[]
61 changes: 61 additions & 0 deletions example/scalalib/linting/3-acyclic/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// https://github.com/com-lihaoyi/acyclic[Acyclic] is a Scala compiler plugin that
// detects circular dependencies between files within a module. This can be very
// useful for ensuring code quality at a high-level:
//
// * While most linters can be concern themselves at a micro-level with formatting
// and whitespace, acyclic is concerned at a macro-level with how your codebase
// is structured into different files
//
// * While most linters satisfied by tweaking whitespace, circular dependencies may
// need significant refactorings like dependency-injection or
// interface-implementation-separation to resolve.
//
// As a Scala compiler plugin, Acyclic can be enabled on any `ScalaModule` by
// adding its `compileIvyDeps,` `scalacPluginIvyDeps`, and `scalacOptions` as
// shown below:

package build
import mill._, scalalib._

object `package` extends RootModule with ScalaModule {
def scalaVersion = "2.13.11"
def compileIvyDeps = Agg(ivy"com.lihaoyi:::acyclic:0.3.15")
def scalacPluginIvyDeps = Agg(ivy"com.lihaoyi:::acyclic:0.3.15")
def scalacOptions = Seq("-P:acyclic:force")
}

/** See Also: src/Foo.scala */

/** See Also: src/Bar.scala */

// Here we have a single `ScalaModule` with two files: `Foo.scala` and `Bar.scala`.
// `Bar` and `Foo` both depend on each other, which usually indicates an issue:

/** Usage

> ./mill compile
error: Unwanted cyclic dependency
...src/Bar.scala...
val value = Foo + " world"
^
symbol: object Foo
...src/Foo.scala...
println("hello " + Bar)
^
symbol: object Bar

*/

// Usually the code should be refactored such that references between files
// is only one way. For this example, we remove the reference to `Foo` in `Bar.scala`,
// which allows the code to compile:


/** Usage

> sed -i.bak 's/Foo/Bar/g' src/Bar.scala

> ./mill compile
done compiling

*/
4 changes: 4 additions & 0 deletions example/scalalib/linting/3-acyclic/src/Bar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package foo
object Bar{
val value = Foo + " world"
}
7 changes: 7 additions & 0 deletions example/scalalib/linting/3-acyclic/src/Foo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package foo
object Foo{
val value = 123
def main(args: Array[String]): Unit= {
println("hello " + Bar)
}
}

0 comments on commit 41ef503

Please sign in to comment.