Skip to content

Analyzer rule to check for a required base package #433

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

Merged
merged 5 commits into from
Mar 9, 2023
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ final class AnalyzerPlugin(val global: Global) extends Plugin { plugin =>
new DiscardedMonixTask(global),
new BadSingletonComponent(global),
new ConstantDeclarations(global),
new BasePackage(global),
)

private lazy val rulesByName = rules.map(r => (r.name, r)).toMap
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.avsystem.commons
package analyzer

import scala.annotation.tailrec
import scala.tools.nsc.Global

class BasePackage(g: Global) extends AnalyzerRule(g, "basePackage") {

import global._

def analyze(unit: CompilationUnit): Unit = if (argument != null) {
val requiredBasePackage = argument

@tailrec def validate(tree: Tree): Unit = tree match {
case PackageDef(pid, _) if pid.symbol.hasPackageFlag && pid.symbol.fullName == requiredBasePackage =>
case PackageDef(_, List(stat)) => validate(stat)
case t => report(t.pos, s"`$requiredBasePackage` must be one of the base packages in this file")
}

validate(unit.body)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.avsystem.commons
package analyzer

import org.scalatest.funsuite.AnyFunSuite

class BasePackageTest extends AnyFunSuite with AnalyzerTest {
settings.pluginOptions.value ++= List("AVSystemAnalyzer:+basePackage:com.avsystem.commons")

test("base package only") {
assertNoErrors(
"""
|package com.avsystem.commons
|
|object bar
|""".stripMargin)
}

test("chained base package") {
assertNoErrors(
"""
|package com.avsystem
|package commons
|
|object bar
|""".stripMargin)
}

test("base package with chained subpackage") {
assertNoErrors(
"""
|package com.avsystem.commons
|package core
|
|object bar
|""".stripMargin)
}

test("base package object") {
assertNoErrors(
"""
|package com.avsystem
|
|package object commons
|""".stripMargin)
}

test("no base package") {
assertErrors(1,
"""
|object bar
|""".stripMargin)
}

test("wrong base package") {
assertErrors(1,
"""
|package com.avsystem.kommons
|
|object bar
|""".stripMargin)
}

test("unchained subpackage") {
assertErrors(1,
"""
|package com.avsystem.commons.core
|
|object bar
|""".stripMargin)
}
}
15 changes: 13 additions & 2 deletions docs/Analyzer.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,29 @@ Here's a list of currently supported rules:
| `discardedMonixTask` | warning | Makes sure that expressions evaluating to Monix `Task`s are not accidentally discarded by conversion to `Unit` |
| `throwableObjects` | warning | Makes sure that objects are never used as `Throwable`s (unless they have stack traces disabled) |
| `constantDeclarations` | off | Checks if constants are always declared as `final val`s with `UpperCamelCase` and no explicit type annotation for literal values |
| `basePackage | warning | Checks if all sources are within the specified base package |

Rules may be enabled and disabled in `build.sbt` with Scala compiler options:

```scala
scalacOptions += "-P:AVSystemAnalyzer:<level><ruleName>,<level><ruleName>,..."
scalacOptions += "-P:AVSystemAnalyzer:<level><ruleName>[:<arg>],<level><ruleName>[:<arg>],..."
```

`<name>` must be one of the rule names listed above or `_` to apply to all rules.
`<ruleName>` must be one of the rule names listed above or `_` to apply to all rules.

`<level>` may be one of:

* `-` to disable rule
* `*` for "info" level
* _empty_ for "warning" level
* `+` for "error" level

`<arg>` is an argument specific for given rule

For example, this options sets all rules on "error" level except for `constantDeclarations` which is disabled
and `discardedMonixTask` which is lowered to "warning" level. The `basePackage` rule is also lowered to "warning" level
and `com.avsystem.commons` is specified as the base package.

```scala
scalacOptions += "-P:AVSystemAnalyzer:+_,-constantDeclarations,discardedMonixTask,basePackage:com.avsystem.commons
```