Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit 6adbc19

Browse files
authored
Implement rule CurliesImportChecker (#12)
* Implement rule CurliesImportChecker As opposed to BlockImportChecker, this rule targets all imports with curly braces, including renaming or hiding imports. This is important because scala refactoring tools that allow to move classes to other packages are often error-prone in the presence of such imports. * Add CurliesImportChecker to default config, disabled.
1 parent 1076bf2 commit 6adbc19

File tree

7 files changed

+167
-0
lines changed

7 files changed

+167
-0
lines changed

src/main/resources/default_config.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,4 +304,5 @@
304304
</parameter>
305305
</parameters>
306306
</check>
307+
<check class="org.scalastyle.scalariform.CurliesImportChecker" level="warning" enabled="false"></check>
307308
</scalastyle>

src/main/resources/reference.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,10 @@ block.import.message = "Avoid block imports"
292292
block.import.label = "Avoid block imports"
293293
block.import.description = "Checks that block imports are not used."
294294

295+
curlies.import.message = "Avoid curlies imports"
296+
curlies.import.label = "Avoid curlies imports"
297+
curlies.import.description = "Checks that curlies imports are not used."
298+
295299
procedure.declaration.message = "Use : Unit = for procedures"
296300
procedure.declaration.label = "Use : Unit = for procedures"
297301
procedure.declaration.description = "Use a : Unit = for procedure declarations"

src/main/resources/scalastyle_definition.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
<checker class="org.scalastyle.scalariform.ImportGroupingChecker" id="import.grouping" defaultLevel="warning"/>
162162
<checker class="org.scalastyle.scalariform.NotImplementedErrorUsage" id="not.implemented.error.usage" defaultLevel="warning"/>
163163
<checker class="org.scalastyle.scalariform.BlockImportChecker" id="block.import" defaultLevel="warning"/>
164+
<checker class="org.scalastyle.scalariform.CurliesImportChecker" id="curlies.import" defaultLevel="warning"/>
164165
<checker class="org.scalastyle.scalariform.ProcedureDeclarationChecker" id="procedure.declaration" defaultLevel="warning"/>
165166
<checker class="org.scalastyle.scalariform.ForBraceChecker" id="for.brace" defaultLevel="warning">
166167
<parameters>

src/main/resources/scalastyle_documentation.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,16 @@ Note: If you intend to enable only if expressions in the format below, disable t
452452
]]>
453453
</example-configuration>
454454
</check>
455+
<check id="curlies.import">
456+
<justification>
457+
Curlies imports (e.g. `import a.{b, c}`) can lead to annoying merge errors in large code bases that are maintained by lot of developers. This rule allows to ensure that only single imports, no renaming and no hiding imports are used in order to minimize merge errors in import declarations.
458+
</justification>
459+
<example-configuration>
460+
<![CDATA[
461+
<check level="warning" class="org.scalastyle.scalariform.CurliesImportChecker" enabled="true"/>
462+
]]>
463+
</example-configuration>
464+
</check>
455465

456466
<check id="procedure.declaration">
457467
<justification>

src/main/resources/scalastyle_messages.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,10 @@ block.import.message = Avoid block imports
288288
block.import.label = Avoid block imports
289289
block.import.description = Checks that block imports are not used.
290290

291+
curlies.import.message = Avoid curlies imports
292+
curlies.import.label = Avoid curlies imports
293+
curlies.import.description = Checks that curlies imports are not used.
294+
291295
procedure.declaration.message = Use : Unit = for procedures
292296
procedure.declaration.label = Use : Unit = for procedures
293297
procedure.declaration.description = Use a : Unit = for procedure declarations
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (C) 2011-2012 the original author or authors.
2+
// See the LICENCE.txt file distributed with this work for additional
3+
// information regarding copyright ownership.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package org.scalastyle.scalariform
18+
19+
import _root_.scalariform.parser.AstNode
20+
import _root_.scalariform.parser.BlockImportExpr
21+
import _root_.scalariform.parser.CompilationUnit
22+
import _root_.scalariform.parser.ImportClause
23+
import org.scalastyle.PositionError
24+
import org.scalastyle.ScalariformChecker
25+
import org.scalastyle.ScalastyleError
26+
27+
class CurliesImportChecker extends ScalariformChecker {
28+
29+
val errorKey = "curlies.import"
30+
31+
def verify(ast: CompilationUnit): List[ScalastyleError] =
32+
findBlockImports(ast)
33+
34+
private def findBlockImports(in: AstNode): List[PositionError] = in match {
35+
36+
// comma separated import
37+
case ImportClause(_, firstImport, otherImports, _) if otherImports.nonEmpty =>
38+
List(PositionError(firstImport.firstToken.offset))
39+
// other block imports
40+
case b: BlockImportExpr => List(PositionError(b.firstToken.offset))
41+
42+
// remaining nodes
43+
case a: AstNode => a.immediateChildren flatMap findBlockImports
44+
}
45+
46+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (C) 2011-2012 the original author or authors.
2+
// See the LICENCE.txt file distributed with this work for additional
3+
// information regarding copyright ownership.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package org.scalastyle.scalariform
18+
19+
import org.junit.Test
20+
import org.scalastyle.file.CheckerTest
21+
import org.scalatestplus.junit.AssertionsForJUnit
22+
23+
// scalastyle:off magic.number
24+
25+
class CurliesImportCheckerTest extends AssertionsForJUnit with CheckerTest {
26+
27+
val key = "curlies.import"
28+
val classUnderTest: Class[CurliesImportChecker] = classOf[CurliesImportChecker]
29+
30+
@Test
31+
def singleImportIsNoBlockImport(): Unit = {
32+
val source = """
33+
import scala.collection.mutable.Buffer
34+
"""
35+
assertErrors(Nil, source)
36+
}
37+
38+
@Test
39+
def importAllIsNoBlockImport(): Unit = {
40+
val source = """
41+
import scala.collection.mutable._
42+
"""
43+
assertErrors(Nil, source)
44+
}
45+
46+
@Test
47+
def hideImportIsBlockImport(): Unit = {
48+
val source = """
49+
import scala.collection.mutable.{Buffer => _}
50+
"""
51+
assertErrors(List(columnError(2, 7)), source)
52+
}
53+
54+
@Test
55+
def renameImportIsBlockImport(): Unit = {
56+
val source = """
57+
import scala.collection.mutable.{Buffer => MB}
58+
"""
59+
assertErrors(List(columnError(2, 7)), source)
60+
}
61+
62+
@Test
63+
def commaSeparatedImportIsBlockImport(): Unit = {
64+
val source = """
65+
import scala.collection.mutable, mutable.Buffer, mutable.ArrayBuffer
66+
"""
67+
assertErrors(List(columnError(2, 7)), source)
68+
}
69+
70+
@Test
71+
def blockImportFound(): Unit = {
72+
val source = """
73+
import scala.collection.mutable.{Buffer, ArrayBuffer}
74+
"""
75+
assertErrors(List(columnError(2, 7)), source)
76+
}
77+
78+
@Test
79+
def wildcardImportAfterRenameImportsIsBlockImport(): Unit = {
80+
val source = """
81+
import scala.collection.mutable.{Buffer => MB, ArrayBuffer => _, _}
82+
"""
83+
assertErrors(List(columnError(2, 7)), source)
84+
}
85+
86+
@Test
87+
def wildcardImportAfterNormalImportAndRenameImportIsBlockImport(): Unit = {
88+
val source = """
89+
import scala.collection.mutable.{Buffer => MB, ArrayBuffer, _}
90+
"""
91+
assertErrors(List(columnError(2, 7)), source)
92+
}
93+
94+
@Test
95+
def wildcardImportAfterNormalImportIsBlockImport(): Unit = {
96+
val source = """
97+
import scala.collection.mutable.{ArrayBuffer, _}
98+
"""
99+
assertErrors(List(columnError(2, 7)), source)
100+
}
101+
}

0 commit comments

Comments
 (0)