Skip to content

Commit 41ef503

Browse files
authored
Add com-lihaoyi/acyclic to linting scala (#3605)
1 parent 2adfe28 commit 41ef503

File tree

4 files changed

+77
-1
lines changed

4 files changed

+77
-1
lines changed

docs/modules/ROOT/pages/Linting_Scala_Projects.adoc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ It can also perform automated refactoring.
3131
Mill supports Scalafix through the Mill-Scalafix third party module. See the module documentation
3232
for more details:
3333

34-
* https://github.com/joan38/mill-scalafix
34+
* https://github.com/joan38/mill-scalafix
35+
36+
== Acyclic Files Enforcement
37+
38+
include::example/scalalib/linting/3-acyclic.adoc[]
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// https://github.com/com-lihaoyi/acyclic[Acyclic] is a Scala compiler plugin that
2+
// detects circular dependencies between files within a module. This can be very
3+
// useful for ensuring code quality at a high-level:
4+
//
5+
// * While most linters can be concern themselves at a micro-level with formatting
6+
// and whitespace, acyclic is concerned at a macro-level with how your codebase
7+
// is structured into different files
8+
//
9+
// * While most linters satisfied by tweaking whitespace, circular dependencies may
10+
// need significant refactorings like dependency-injection or
11+
// interface-implementation-separation to resolve.
12+
//
13+
// As a Scala compiler plugin, Acyclic can be enabled on any `ScalaModule` by
14+
// adding its `compileIvyDeps,` `scalacPluginIvyDeps`, and `scalacOptions` as
15+
// shown below:
16+
17+
package build
18+
import mill._, scalalib._
19+
20+
object `package` extends RootModule with ScalaModule {
21+
def scalaVersion = "2.13.11"
22+
def compileIvyDeps = Agg(ivy"com.lihaoyi:::acyclic:0.3.15")
23+
def scalacPluginIvyDeps = Agg(ivy"com.lihaoyi:::acyclic:0.3.15")
24+
def scalacOptions = Seq("-P:acyclic:force")
25+
}
26+
27+
/** See Also: src/Foo.scala */
28+
29+
/** See Also: src/Bar.scala */
30+
31+
// Here we have a single `ScalaModule` with two files: `Foo.scala` and `Bar.scala`.
32+
// `Bar` and `Foo` both depend on each other, which usually indicates an issue:
33+
34+
/** Usage
35+
36+
> ./mill compile
37+
error: Unwanted cyclic dependency
38+
...src/Bar.scala...
39+
val value = Foo + " world"
40+
^
41+
symbol: object Foo
42+
...src/Foo.scala...
43+
println("hello " + Bar)
44+
^
45+
symbol: object Bar
46+
47+
*/
48+
49+
// Usually the code should be refactored such that references between files
50+
// is only one way. For this example, we remove the reference to `Foo` in `Bar.scala`,
51+
// which allows the code to compile:
52+
53+
54+
/** Usage
55+
56+
> sed -i.bak 's/Foo/Bar/g' src/Bar.scala
57+
58+
> ./mill compile
59+
done compiling
60+
61+
*/
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package foo
2+
object Bar{
3+
val value = Foo + " world"
4+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package foo
2+
object Foo{
3+
val value = 123
4+
def main(args: Array[String]): Unit= {
5+
println("hello " + Bar)
6+
}
7+
}

0 commit comments

Comments
 (0)