From 41ef5032113ab93bd520a3fc46725f8f7ad7344c Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 26 Sep 2024 16:21:57 +0800 Subject: [PATCH] Add com-lihaoyi/acyclic to linting scala (#3605) --- .../ROOT/pages/Linting_Scala_Projects.adoc | 6 +- example/scalalib/linting/3-acyclic/build.mill | 61 +++++++++++++++++++ .../scalalib/linting/3-acyclic/src/Bar.scala | 4 ++ .../scalalib/linting/3-acyclic/src/Foo.scala | 7 +++ 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 example/scalalib/linting/3-acyclic/build.mill create mode 100644 example/scalalib/linting/3-acyclic/src/Bar.scala create mode 100644 example/scalalib/linting/3-acyclic/src/Foo.scala diff --git a/docs/modules/ROOT/pages/Linting_Scala_Projects.adoc b/docs/modules/ROOT/pages/Linting_Scala_Projects.adoc index 4ccf42e0acb..305c4153502 100644 --- a/docs/modules/ROOT/pages/Linting_Scala_Projects.adoc +++ b/docs/modules/ROOT/pages/Linting_Scala_Projects.adoc @@ -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 \ No newline at end of file +* https://github.com/joan38/mill-scalafix + +== Acyclic Files Enforcement + +include::example/scalalib/linting/3-acyclic.adoc[] \ No newline at end of file diff --git a/example/scalalib/linting/3-acyclic/build.mill b/example/scalalib/linting/3-acyclic/build.mill new file mode 100644 index 00000000000..7e3a8c351d5 --- /dev/null +++ b/example/scalalib/linting/3-acyclic/build.mill @@ -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 + +*/ diff --git a/example/scalalib/linting/3-acyclic/src/Bar.scala b/example/scalalib/linting/3-acyclic/src/Bar.scala new file mode 100644 index 00000000000..bf34b54b8f4 --- /dev/null +++ b/example/scalalib/linting/3-acyclic/src/Bar.scala @@ -0,0 +1,4 @@ +package foo +object Bar{ + val value = Foo + " world" +} diff --git a/example/scalalib/linting/3-acyclic/src/Foo.scala b/example/scalalib/linting/3-acyclic/src/Foo.scala new file mode 100644 index 00000000000..5adfa327c4c --- /dev/null +++ b/example/scalalib/linting/3-acyclic/src/Foo.scala @@ -0,0 +1,7 @@ +package foo +object Foo{ + val value = 123 + def main(args: Array[String]): Unit= { + println("hello " + Bar) + } +}