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

Check cycles on package level #119

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion acyclic/src/acyclic/plugin/Plugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,24 @@ class TestPlugin(val global: Global, cycleReporter: Seq[(Value, SortedSet[Int])]
val name = "acyclic"

var force = false
var forcePkg = false
var fatal = true

// Yeah processOptions is deprecated but keep using it anyway for 2.10.x compatibility
override def processOptions(options: List[String], error: String => Unit): Unit = {
if (options.contains("force")) {
force = true
}
if (options.contains("forcePkg")) {
forcePkg = true
}
if (options.contains("warn")) {
fatal = false
}
}
val description = "Allows the developer to prohibit inter-file dependencies"

val components = List[tools.nsc.plugins.PluginComponent](
new PluginPhase(this.global, cycleReporter, force, fatal)
new PluginPhase(this.global, cycleReporter, force, forcePkg, fatal)
)
}
41 changes: 27 additions & 14 deletions acyclic/src/acyclic/plugin/PluginPhase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class PluginPhase(
val global: Global,
cycleReporter: Seq[(Value, SortedSet[Int])] => Unit,
force: => Boolean,
forcePkg: => Boolean,
fatal: => Boolean
) extends PluginComponent
with GraphAnalysis { t =>
Expand Down Expand Up @@ -59,23 +60,35 @@ class PluginPhase(
} yield {
Value.File(unit.source.path, pkgName(unit))
}
val stdPackages = if (forcePkg) packages else Seq.empty[Value.Pkg]
val acyclicPkgNames = packageObjects ++ stdPackages
(skipNodePaths, acyclicNodePaths, acyclicPkgNames)
}

val acyclicPkgNames = for {
unit <- units
pkgObject <- unit.body.collect { case x: ModuleDef if x.name.toString == "package" => x }
if pkgObject.impl.children.collect { case Import(expr, List(sel)) =>
expr.symbol.toString == "package acyclic" && sel.name.toString == "pkg"
}.exists(x => x)
} yield {
Value.Pkg(
pkgObject.symbol
.enclosingPackageClass
.fullName
private def packageObjects = for {
unit <- units
pkgObject <- unit.body.collect { case x: ModuleDef if x.name.toString == "package" => x }
if pkgObject.impl.children.collect { case Import(expr, List(sel)) =>
expr.symbol.toString == "package acyclic" && sel.name.toString == "pkg"
}.exists(x => x)
} yield {
Value.Pkg(
pkgObject.symbol
.enclosingPackageClass
.fullName
.split('.')
.toList
)
}

private def packages: Seq[Value.Pkg] = {
units.map(x => x.body).collect { case y: PackageDef => y }.map(z => z.symbol.fullName).filter(x => x != "<empty>")
.distinct
.map(c => Value.Pkg(
c
.split('.')
.toList
)
}
(skipNodePaths, acyclicNodePaths, acyclicPkgNames)
))
}

override def newPhase(prev: Phase): Phase = new Phase(prev) {
Expand Down
22 changes: 22 additions & 0 deletions acyclic/test/src/acyclic/CycleTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,28 @@ object CycleTests extends TestSuite {
))
test("pass") - make("force/simple")
test("skip") - make("force/skip", force = true)
test("mutualcyclic") - make("success/pkg/mutualcyclic", force = true)
}
test("forcepkg") - {
test("fail") - {
makeFail("forcepkg/cyclicpackage", force = false, forcePkg = true)(
Seq(
Pkg("forcepkg.cyclicpackage.b") -> SortedSet(4),
Pkg("forcepkg.cyclicpackage.a") -> SortedSet(4)
)
)
}
test("success") - {
make("forcepkg/simple", force = false, forcePkg = true)
}
test("fail") - {
makeFail("forcepkg/simple", force = true, forcePkg = true)(
Seq(
File("B.scala") -> SortedSet(4, 5),
File("A.scala") -> SortedSet(5)
)
)
}
}
}
}
6 changes: 4 additions & 2 deletions acyclic/test/src/acyclic/TestUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ object TestUtils {
path: String,
extraIncludes: Seq[String] = Seq("acyclic/src/acyclic/package.scala"),
force: Boolean = false,
forcePkg: Boolean = false,
warn: Boolean = false,
collectInfo: Boolean = true
): Seq[(Position, String, String)] = {
Expand All @@ -52,6 +53,7 @@ object TestUtils {

val opts = List(
if (force) Seq("force") else Seq(),
if (forcePkg) Seq("forcePkg") else Seq(),
if (warn) Seq("warn") else Seq()
).flatten
if (opts.nonEmpty) {
Expand Down Expand Up @@ -83,13 +85,13 @@ object TestUtils {
storeReporter.map(_.infos.toSeq.map(i => (i.pos, i.msg, i.severity.toString))).getOrElse(Seq.empty)
}

def makeFail(path: String, force: Boolean = false)(expected: Seq[(Value, SortedSet[Int])]*) = {
def makeFail(path: String, force: Boolean = false, forcePkg: Boolean = false)(expected: Seq[(Value, SortedSet[Int])]*) = {
def canonicalize(cycle: Seq[(Value, SortedSet[Int])]): Seq[(Value, SortedSet[Int])] = {
val startIndex = cycle.indexOf(cycle.minBy(_._1.toString))
cycle.toList.drop(startIndex) ++ cycle.toList.take(startIndex)
}

val ex = intercept[CompilationException] { make(path, force = force, collectInfo = false) }
val ex = intercept[CompilationException] { make(path, force = force, forcePkg = forcePkg,collectInfo = false) }
val cycles = ex.cycles
.map(canonicalize)
.map(
Expand Down
6 changes: 6 additions & 0 deletions src/test/resources/forcepkg/cyclicpackage/a/A1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package forcepkg.cyclicpackage
package a

class A1 extends b.B1{

}
5 changes: 5 additions & 0 deletions src/test/resources/forcepkg/cyclicpackage/a/A2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package forcepkg.cyclicpackage.a

class A2 {

}
3 changes: 3 additions & 0 deletions src/test/resources/forcepkg/cyclicpackage/b/B1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package forcepkg.cyclicpackage.b

class B1
4 changes: 4 additions & 0 deletions src/test/resources/forcepkg/cyclicpackage/b/B2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package forcepkg.cyclicpackage
package b

class B2 extends a.A2
6 changes: 6 additions & 0 deletions src/test/resources/forcepkg/simple/A.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package forcepkg.simple


class A {
val b: B = null
}
6 changes: 6 additions & 0 deletions src/test/resources/forcepkg/simple/B.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package forcepkg.simple

class B {
val a1: A = new A
val a2: A = new A
}
Loading