Skip to content

Commit

Permalink
Merge branch 'master' of github.com:scalastyle/scalastyle
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewfarwell committed Feb 28, 2016
2 parents ccdc92d + bea4e6f commit 5a0e2b6
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 10 deletions.
5 changes: 5 additions & 0 deletions src/main/resources/default_config.xml
Expand Up @@ -44,6 +44,11 @@
<parameter name="regex"><![CDATA[[A-Z][A-Za-z]*]]></parameter>
</parameters>
</check>
<check level="warning" class="org.scalastyle.scalariform.PackageNamesChecker" enabled="true">
<parameters>
<parameter name="regex"><![CDATA[^[a-z][A-Za-z]*$]]></parameter>
</parameters>
</check>
<check level="warning" class="org.scalastyle.scalariform.PackageObjectNamesChecker" enabled="true">
<parameters>
<parameter name="regex"><![CDATA[^[a-z][A-Za-z]*$]]></parameter>
Expand Down
6 changes: 6 additions & 0 deletions src/main/resources/reference.conf
Expand Up @@ -106,6 +106,12 @@ structural.type.message = "Avoid using structural types"
structural.type.label = "Structural type"
structural.type.description = "Check that structural types are not used."

package.name.message = "Package name does not match the regular expression ''{0}''"
package.name.label = "Package name"
package.name.description = "Check that package names match a regular expression"
package.name.regex.label = "Regular expression"
package.name.regex.description = "The package names must match this regular expression"

package.object.name.message = "Package object name does not match the regular expression ''{0}''"
package.object.name.label = "Package object name"
package.object.name.description = "Check that package object names match a regular expression"
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/scalastyle_definition.xml
Expand Up @@ -32,6 +32,11 @@
<parameter name="regex" type="string" default="^[A-Z][A-Za-z]*$" />
</parameters>
</checker>
<checker class="org.scalastyle.scalariform.PackageNamesChecker" id="package.name" defaultLevel="warning">
<parameters>
<parameter name="regex" type="string" default="^[a-z][A-Za-z]*$" />
</parameters>
</checker>
<checker class="org.scalastyle.scalariform.PackageObjectNamesChecker" id="package.object.name" defaultLevel="warning">
<parameters>
<parameter name="regex" type="string" default="^[a-z][A-Za-z]*$" />
Expand Down
15 changes: 15 additions & 0 deletions src/main/resources/scalastyle_documentation.xml
Expand Up @@ -613,6 +613,21 @@ To bring consistency with how comments should be formatted, leave a space right
</example-configuration>
</check>

<check id="package.name">
<justification>
The Scala style guide recommends that package names conform to certain standards.
</justification>
<example-configuration>
<![CDATA[
<check level="warning" class="org.scalastyle.scalariform.PackageNamesChecker" enabled="true">
<parameters>
<parameter name="regex">^[a-z][A-Za-z]*$</parameter>
</parameters>
</check>
]]>
</example-configuration>
</check>

<check id="package.object.name">
<justification>
The Scala style guide recommends that package object names conform to certain standards.
Expand Down
6 changes: 6 additions & 0 deletions src/main/resources/scalastyle_messages.properties
Expand Up @@ -106,6 +106,12 @@ structural.type.message = Avoid using structural types
structural.type.label = Structural type
structural.type.description = Check that structural types are not used.

package.name.message = Package name does not match the regular expression ''{0}''
package.name.label = Package name
package.name.description = Check that package names match a regular expression
package.name.regex.label = Regular expression
package.name.regex.description = The package names must match this regular expression

package.object.name.message = Package object name does not match the regular expression ''{0}''
package.object.name.label = Package object name
package.object.name.description = Check that package object names match a regular expression
Expand Down
42 changes: 42 additions & 0 deletions src/main/scala/org/scalastyle/scalariform/ClassNamesChecker.scala
Expand Up @@ -25,6 +25,8 @@ import scalariform.lexer.Tokens.OBJECT
import scalariform.lexer.Tokens.PACKAGE
import scalariform.lexer.Tokens.VAL
import scalariform.lexer.Tokens.VAR
import scalariform.lexer.Tokens.VARID
import scalariform.lexer.Tokens.DOT
import scalariform.parser.CompilationUnit
import scala.util.matching.Regex
import scalariform.lexer.Token
Expand Down Expand Up @@ -69,6 +71,46 @@ class ObjectNamesChecker extends ScalariformChecker {
}
}

class PackageNamesChecker extends ScalariformChecker {
val DefaultRegex = "^[a-z][A-Za-z]*$"
val errorKey = "package.name"

def verify(ast: CompilationUnit): List[PositionError] = {
val regexString = getString("regex", DefaultRegex)
val regex = regexString.r

def isPartOfPackageName(t: Token): Boolean = (t.tokenType == DOT) || (t.tokenType == VARID)

@annotation.tailrec
def getNextPackageName(tokens: List[Token]): (List[Token], List[Token]) = tokens match {
case Nil => (Nil, Nil)
case hd :: tail if (hd.tokenType == PACKAGE) => tail.span(isPartOfPackageName(_))
case l => getNextPackageName(l.dropWhile(tok => tok.tokenType != PACKAGE))
}

@annotation.tailrec
def getPackageNameLoop(tokens: List[Token], myAccumulator: List[List[Token]]): List[List[Token]] =
getNextPackageName(tokens) match {
case (Nil, Nil) => myAccumulator.reverse // Return the result, but reverse since we gathered backward
case (Nil, remainder) => getPackageNameLoop(remainder, myAccumulator) // Found package object - try again
case (l, remainder) => // add match to results, go look again
var pkgName = l.filter(tok => tok.tokenType != DOT) // Strip out the dots between varids
getPackageNameLoop(remainder, pkgName :: myAccumulator)
}

var packageNames = getPackageNameLoop(ast.tokens, Nil)

val it = for {
pkgName <- packageNames.flatten;
if ((regex findAllIn (pkgName.text)).size == 0)
} yield {
PositionError(pkgName.offset, List(regexString))
}

it.toList
}
}

class PackageObjectNamesChecker extends ScalariformChecker {
val DefaultRegex = "^[a-z][A-Za-z]*$"
val errorKey = "package.object.name"
Expand Down
Expand Up @@ -399,7 +399,7 @@ class ImportOrderChecker extends ScalariformChecker {
if (isName1UpperCase == isName2UpperCase) {
name1.compareToIgnoreCase(name2)
} else {
if (isName1UpperCase && !isImport) 1 else -1
if (isName1UpperCase && !isImport) -1 else 1
}
}
} else {
Expand Down
Expand Up @@ -97,6 +97,90 @@ package object foobar {
}


class PackageNamesCheckerTest extends AssertionsForJUnit with CheckerTest {
val key = "package.name"
val classUnderTest = classOf[PackageNamesChecker]

@Test def testSinglePartNoError(): Unit = {
val source = """
package foobar
""";

assertErrors(List(), source)
}

@Test def testSinglePartError(): Unit = {
val source = """
package FooBar
""";

assertErrors(List(columnError(2, 8, List("^[a-z][A-Za-z]*$"))), source)
}

@Test def testMultiPartNoError(): Unit = {
val source = """
package abc.foobar
""";

assertErrors(List(), source)
}

@Test def testMultiPartError(): Unit = {
val source = """
package abc.foo_bar
""";

assertErrors(List(columnError(2, 12, List("^[a-z][A-Za-z]*$"))), source)
}

@Test def testPackageObjectNoError(): Unit = {
val source = """
package object _foo_bar
""";

assertErrors(List(), source)
}

// Check case where the package name is built up by successive package statements.
@Test def testMultiLinePackageNoError(): Unit = {
val source = """
package foo
package bar
""";

assertErrors(List(), source)
}

// Check case where the package name is built up by successive package statements.
@Test def testMultiLinePackageError(): Unit = {
val source = """
package foo
package Bar
""";

assertErrors(List(columnError(3, 8, List("^[a-z][A-Za-z]*$"))), source)
}

@Test def testMultiLinePackageMultipleError(): Unit = {
val source = """
package Foo
package Bar
""";

assertErrors(List(columnError(2, 8, List("^[a-z][A-Za-z]*$")), columnError(3, 8, List("^[a-z][A-Za-z]*$"))), source)
}

@Test def testPackageAndPackageObjectNoError(): Unit = {
val source = """
package org.thisone
package object _foo
""";

assertErrors(List(), source)
}

}

class PackageObjectNamesCheckerTest extends AssertionsForJUnit with CheckerTest {
val key = "package.object.name"
val classUnderTest = classOf[PackageObjectNamesChecker]
Expand Down
36 changes: 27 additions & 9 deletions src/test/scala/org/scalastyle/scalariform/ImportsCheckerTest.scala
Expand Up @@ -182,6 +182,33 @@ class ImportOrderCheckerTest extends AssertionsForJUnit with CheckerTest {
val key = "import.ordering"
val classUnderTest = classOf[ImportOrderChecker]

val params = Map(
"groups" -> "java,scala,others,project2",
"group.java" -> "javax?\\..+",
"group.scala" -> "scala\\..+",
"group.others" -> "(?!my\\.org\\.project2\\.).*",
"group.project2" -> "my\\.org\\.project2\\..*",
"maxBlankLines" -> "2"
)

@Test def testSubPackage(): Unit = {
val source = """
|package foobar
|
|import foobar.subpackage.Foo
|import foobar.Bar
|
|object Foobar {
|}
""".stripMargin;

val expected = List(
columnError(5, 0, errorKey = errorKey("wrongOrderInGroup"),
args = List("foobar.Bar", "foobar.subpackage.Foo")))

assertErrors(expected, source, params = params)
}

@Test def testImportGrouping(): Unit = {
val source = """
|package foobar
Expand All @@ -207,15 +234,6 @@ class ImportOrderCheckerTest extends AssertionsForJUnit with CheckerTest {
|}
""".stripMargin;

val params = Map(
"groups" -> "java,scala,others,project2",
"group.java" -> "javax?\\..+",
"group.scala" -> "scala\\..+",
"group.others" -> "(?!my\\.org\\.project2\\.).*",
"group.project2" -> "my\\.org\\.project2\\..*",
"maxBlankLines" -> "2"
)

val expected = List(
columnError(5, 20, errorKey = errorKey("wrongOrderInSelector"), args = List("Cipher", "Mac")),
columnError(6, 0, errorKey = errorKey("wrongOrderInGroup"),
Expand Down

0 comments on commit 5a0e2b6

Please sign in to comment.