Skip to content

Commit 129957e

Browse files
Merge pull request scalastyle#201 from shanielh/master
ScalaDocChecker: Add ignoreTokenTypes option
2 parents 2af6c1e + d1a17e5 commit 129957e

File tree

6 files changed

+144
-92
lines changed

6 files changed

+144
-92
lines changed

src/main/resources/reference.conf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ scaladoc.label = "Missing or badly formed ScalaDoc: {0}"
284284
scaladoc.description = "Checks that the ScalaDoc on documentable members is well-formed"
285285
scaladoc.ignoreRegex.label = "Regular expression"
286286
scaladoc.ignoreRegex.description = "Class names matching this regular expression will be ignored"
287+
scaladoc.ignoreTokenTypes.label = "Comma Separated String"
288+
scaladoc.ignoreTokenTypes.description = "Include the following to ignore : PatDefOrDcl (variables), TmplDef (classes, traits), TypeDefOrDcl (type definitions), FunDefOrDcl (functions)"
287289

288290
disallow.space.after.token.message = "Space after token {0}"
289291
disallow.space.after.token.label = "Space after tokens"
@@ -340,4 +342,3 @@ todo.comment.label = "TODO/FIXME comment"
340342
todo.comment.description = "Check for use of TODO/FIXME single line comments"
341343
todo.comment.words.label = "Word list"
342344
todo.comment.words.description = "Alternative list of words to look for, separated by |"
343-

src/main/resources/scalastyle_definition.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
<checker class="org.scalastyle.scalariform.ScalaDocChecker" id="scaladoc" defaultLevel="warning">
153153
<parameters>
154154
<parameter name="ignoreRegex" type="string" default="^$"/>
155+
<parameter name="ignoreTokenTypes" type="string" default="^$"/>
155156
</parameters>
156157
</checker>
157158
<checker class="org.scalastyle.scalariform.DisallowSpaceAfterTokenChecker" id="disallow.space.after.token" defaultLevel="warning"/>

src/main/resources/scalastyle_documentation.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,11 +839,15 @@ To bring consistency with how comments should be formatted, leave a space right
839839
<justification>
840840
Scaladoc is generally considered a good thing. Within reason.
841841
</justification>
842+
<extra-description>
843+
Ignore tokens is a comma separated string that may include the following : PatDefOrDcl (variables), TmplDef (classes, traits), TypeDefOrDcl (type definitions), FunDefOrDcl (functions)
844+
</extra-description>
842845
<example-configuration>
843846
<![CDATA[
844847
<check level="warning" class="org.scalastyle.scalariform.ScalaDocChecker" enabled="true">
845848
<parameters>
846849
<parameter name="ignoreRegex">(.*Spec$)|(.*SpecIT$)</parameter>
850+
<parameter name="ignoreTokenTypes">PatDefOrDcl,TypeDefOrDcl,FunDefOrDcl,TmplDef</parameter>
847851
</parameters>
848852
</check>
849853
]]>

src/main/resources/scalastyle_messages.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ scaladoc.label = Missing or badly formed ScalaDoc: {0}
279279
scaladoc.description = Checks that the ScalaDoc on documentable members is well-formed
280280
scaladoc.ignoreRegex.label = "Regular expression"
281281
scaladoc.ignoreRegex.description = "Class names matching this regular expression will be ignored"
282+
scaladoc.ignoreTokenTypes.label = Comma Separated String
283+
scaladoc.ignoreTokenTypes.description = "Include the following to ignore : PatDefOrDcl (variables), TmplDef (classes, traits), TypeDefOrDcl (type definitions), FunDefOrDcl (functions)"
282284

283285
indentation.message = Use correct indentation
284286
indentation.label = Use correct indentation

src/main/scala/org/scalastyle/scalariform/ScalaDocChecker.scala

Lines changed: 121 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import _root_.scalariform.lexer.Token
2929
import _root_.scalariform.lexer.TokenType
3030
import _root_.scalariform.lexer.Tokens.CLASS
3131
import _root_.scalariform.parser.AccessModifier
32+
import _root_.scalariform.parser.AstNode
3233
import _root_.scalariform.parser.FullDefOrDcl
3334
import _root_.scalariform.parser.FunDefOrDcl
3435
import _root_.scalariform.parser.ParamClauses
@@ -54,6 +55,7 @@ class ScalaDocChecker extends CombinedChecker {
5455
protected val errorKey: String = "scaladoc"
5556

5657
val DefaultIgnoreRegex = "^$"
58+
val DefaultIgnoreTokenTypes = ""
5759

5860
val skipPrivate = true
5961
val skipQualifiedPrivate = false
@@ -62,7 +64,11 @@ class ScalaDocChecker extends CombinedChecker {
6264

6365
override def verify(ast: CombinedAst): List[ScalastyleError] = {
6466
val tokens = ast.compilationUnit.tokens
67+
6568
val ignoreRegex = getString("ignoreRegex", DefaultIgnoreRegex)
69+
val tokensToIgnore = getString("ignoreTokenTypes", DefaultIgnoreTokenTypes).split(",").filterNot(_.isEmpty).toSet
70+
71+
assertTokensToIgnore(tokensToIgnore)
6672

6773
def trimToTokenOfType(list: List[Token], tokenType: TokenType): List[Token] = {
6874
if (list.isEmpty) {
@@ -79,7 +85,7 @@ class ScalaDocChecker extends CombinedChecker {
7985
val ignore = ts.nonEmpty && ts(1).text.matches(ignoreRegex)
8086
ignore match {
8187
case true => Nil
82-
case false => localVisit(skip = false, HiddenTokens(Nil), ast.lines)(ast.compilationUnit.immediateChildren.head)
88+
case false => localVisit(skip = false, HiddenTokens(Nil), ast.lines, tokensToIgnore)(ast.compilationUnit.immediateChildren.head)
8389
}
8490
}
8591

@@ -179,97 +185,114 @@ class ScalaDocChecker extends CombinedChecker {
179185
*
180186
* we do not bother descending down any further
181187
*/
182-
private def localVisit(skip: Boolean, fallback: HiddenTokens, lines: Lines)(ast: Any): List[ScalastyleError] = ast match {
183-
case t: FullDefOrDcl =>
184-
// private, private[xxx];
185-
// protected, protected[xxx];
186-
187-
// check if we are going to include or skip depending on access modifier
188-
val accessModifier = t.modifiers.find {
189-
case AccessModifier(_, _) => true
190-
case _ => false
191-
}
192-
val skip = accessModifier.exists {
193-
case AccessModifier(pop, Some(_)) =>
194-
if (pop.text == "private") skipQualifiedPrivate else skipQualifiedProtected
195-
case AccessModifier(pop, None) =>
196-
if (pop.text == "private") skipPrivate else skipProtected
197-
case _ =>
198-
false
199-
}
188+
private def localVisit(skip: Boolean, fallback: HiddenTokens, lines: Lines, tokensToIgnore: Set[String])(ast: Any): List[ScalastyleError] = {
200189

201-
// pick the ScalaDoc "attached" to the modifier, which actually means
202-
// ScalaDoc of the following member
203-
val scalaDocs = for {
204-
token <- t.tokens
205-
comment <- token.associatedWhitespaceAndComments
206-
if comment.token.isScalaDocComment
207-
} yield comment
208-
209-
// descend
210-
visit(t, localVisit(skip, HiddenTokens(fallback.tokens ++ scalaDocs), lines))
211-
case t: TmplDef =>
212-
// trait Foo, trait Foo[A];
213-
// class Foo, class Foo[A](a: A);
214-
// case class Foo(), case class Foo[A](a: A);
215-
// object Foo;
216-
val (_, line) = lines.findLineAndIndex(t.firstToken.offset).get
217-
218-
// we are checking parameters and type parameters
219-
val errors = if (skip) Nil else findScalaDoc(t.firstToken, fallback).
220-
map { scalaDoc =>
221-
paramErrors(line, t.paramClausesOpt)(scalaDoc) ++
222-
tparamErrors(line, t.typeParamClauseOpt)(scalaDoc)
223-
}.getOrElse(List(LineError(line, List(Missing))))
224-
225-
// and we descend, because we're interested in seeing members of the types
226-
errors ++ visit(t, localVisit(skip, NoHiddenTokens, lines))
227-
case t: FunDefOrDcl =>
228-
// def foo[A, B](a: Int): B = ...
229-
val (_, line) = lines.findLineAndIndex(t.firstToken.offset).get
230-
231-
// we are checking parameters, type parameters and returns
232-
val errors = if (skip) Nil else findScalaDoc(t.firstToken, fallback).
233-
map { scalaDoc =>
234-
paramErrors(line, Some(t.paramClauses))(scalaDoc) ++
235-
tparamErrors(line, t.typeParamClauseOpt)(scalaDoc) ++
236-
returnErrors(line, t.returnTypeOpt)(scalaDoc)
237-
}.
238-
getOrElse(List(LineError(line, List(Missing))))
239-
240-
// we don't descend any further
241-
errors
242-
case t: TypeDefOrDcl =>
243-
// type Foo = ...
244-
val (_, line) = lines.findLineAndIndex(t.firstToken.offset).get
245-
246-
// error is non-existence
247-
val errors = if (skip) Nil else findScalaDoc(t.firstToken, fallback).
248-
map(_ => Nil).
249-
getOrElse(List(LineError(line, List(Missing))))
250-
251-
// we don't descend any further
252-
errors
253-
254-
case t: PatDefOrDcl =>
255-
// val a = ...
256-
// var a = ...
257-
val (_, line) = lines.findLineAndIndex(t.valOrVarToken.offset).get
258-
val errors = if (skip) Nil else findScalaDoc(t.firstToken, fallback).
259-
map(_ => Nil).
260-
getOrElse(List(LineError(line, List(Missing))))
261-
// we don't descend any further
262-
errors
263-
264-
case t: StatSeq =>
265-
localVisit(skip, fallback, lines)(t.firstStatOpt) ++ (
266-
for(statOpt <- t.otherStats)
267-
yield localVisit(skip, statOpt._1.associatedWhitespaceAndComments, lines)(statOpt._2)
268-
).flatten
269-
270-
case t: Any =>
271-
// anything else, we descend (unless we stopped above)
272-
visit(t, localVisit(skip, fallback, lines))
190+
def shouldSkip(node: AstNode) = skip || tokensToIgnore.contains(node.getClass.getSimpleName)
191+
192+
ast match {
193+
case t: FullDefOrDcl =>
194+
// private, private[xxx];
195+
// protected, protected[xxx];
196+
197+
// check if we are going to include or skip depending on access modifier
198+
val accessModifier = t.modifiers.find {
199+
case AccessModifier(_, _) => true
200+
case _ => false
201+
}
202+
val skip = accessModifier.exists {
203+
case AccessModifier(pop, Some(_)) =>
204+
if (pop.text == "private") skipQualifiedPrivate else skipQualifiedProtected
205+
case AccessModifier(pop, None) =>
206+
if (pop.text == "private") skipPrivate else skipProtected
207+
case _ =>
208+
false
209+
}
210+
211+
// pick the ScalaDoc "attached" to the modifier, which actually means
212+
// ScalaDoc of the following member
213+
val scalaDocs = for {
214+
token <- t.tokens
215+
comment <- token.associatedWhitespaceAndComments
216+
if comment.token.isScalaDocComment
217+
} yield comment
218+
219+
// descend
220+
visit(t, localVisit(skip, HiddenTokens(fallback.tokens ++ scalaDocs), lines, tokensToIgnore))
221+
case t: TmplDef =>
222+
// trait Foo, trait Foo[A];
223+
// class Foo, class Foo[A](a: A);
224+
// case class Foo(), case class Foo[A](a: A);
225+
// object Foo;
226+
val (_, line) = lines.findLineAndIndex(t.firstToken.offset).get
227+
228+
// we are checking parameters and type parameters
229+
val errors = if (shouldSkip(t)) Nil
230+
else findScalaDoc(t.firstToken, fallback).
231+
map { scalaDoc =>
232+
paramErrors(line, t.paramClausesOpt)(scalaDoc) ++
233+
tparamErrors(line, t.typeParamClauseOpt)(scalaDoc)
234+
}.getOrElse(List(LineError(line, List(Missing))))
235+
236+
// and we descend, because we're interested in seeing members of the types
237+
errors ++ visit(t, localVisit(skip, NoHiddenTokens, lines, tokensToIgnore))
238+
case t: FunDefOrDcl =>
239+
// def foo[A, B](a: Int): B = ...
240+
val (_, line) = lines.findLineAndIndex(t.firstToken.offset).get
241+
242+
// we are checking parameters, type parameters and returns
243+
val errors = if (shouldSkip(t)) Nil
244+
else findScalaDoc(t.firstToken, fallback).
245+
map { scalaDoc =>
246+
paramErrors(line, Some(t.paramClauses))(scalaDoc) ++
247+
tparamErrors(line, t.typeParamClauseOpt)(scalaDoc) ++
248+
returnErrors(line, t.returnTypeOpt)(scalaDoc)
249+
}.
250+
getOrElse(List(LineError(line, List(Missing))))
251+
252+
// we don't descend any further
253+
errors
254+
case t: TypeDefOrDcl =>
255+
// type Foo = ...
256+
val (_, line) = lines.findLineAndIndex(t.firstToken.offset).get
257+
258+
// error is non-existence
259+
val errors = if (shouldSkip(t)) Nil
260+
else findScalaDoc(t.firstToken, fallback).
261+
map(_ => Nil).
262+
getOrElse(List(LineError(line, List(Missing))))
263+
264+
// we don't descend any further
265+
errors
266+
267+
case t: PatDefOrDcl =>
268+
// val a = ...
269+
// var a = ...
270+
val (_, line) = lines.findLineAndIndex(t.valOrVarToken.offset).get
271+
val errors = if (shouldSkip(t)) Nil
272+
else findScalaDoc(t.firstToken, fallback).
273+
map(_ => Nil).
274+
getOrElse(List(LineError(line, List(Missing))))
275+
// we don't descend any further
276+
errors
277+
278+
case t: StatSeq =>
279+
localVisit(skip, fallback, lines, tokensToIgnore)(t.firstStatOpt) ++ (
280+
for (statOpt <- t.otherStats)
281+
yield localVisit(skip, statOpt._1.associatedWhitespaceAndComments, lines, tokensToIgnore)(statOpt._2)
282+
).flatten
283+
284+
case t: Any =>
285+
// anything else, we descend (unless we stopped above)
286+
visit(t, localVisit(skip, fallback, lines, tokensToIgnore))
287+
}
288+
}
289+
290+
private def assertTokensToIgnore(tokensToIgnore: Iterable[String]): Unit = {
291+
val wrongTokensToIgnore = tokensToIgnore.filterNot(availableTokensToIgnore.contains)
292+
if (wrongTokensToIgnore.nonEmpty) {
293+
throw new IllegalArgumentException(s"ignoreTokenTypes contained wrong types: $wrongTokensToIgnore, " +
294+
s"available types are $availableTokensToIgnore")
295+
}
273296
}
274297

275298
}
@@ -278,6 +301,13 @@ class ScalaDocChecker extends CombinedChecker {
278301
* Contains the ScalaDoc model with trivial parsers
279302
*/
280303
object ScalaDocChecker {
304+
305+
private val availableTokensToIgnore = Set(classOf[PatDefOrDcl],
306+
classOf[TypeDefOrDcl],
307+
classOf[FunDefOrDcl],
308+
classOf[TmplDef])
309+
.map(_.getSimpleName)
310+
281311
val Missing = "Missing"
282312
def missingParam(name: String): String = "Missing @param " + name
283313
def extraParam(name: String): String = "Extra @param " + name

src/test/scala/org/scalastyle/scalariform/ScalaDocCheckerTest.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,4 +372,18 @@ class ScalaDocCheckerTest extends AssertionsForJUnit with CheckerTest {
372372
assertErrors(List(lineError(7, List(Missing))), source)
373373
}
374374

375+
@Test def ignoreTokenTypes(): Unit = {
376+
377+
val cases = Seq(Seq("val a = 1", "var a = 2") -> "PatDefOrDcl",
378+
Seq("class A", "case class A", "object A", "trait A") -> "TmplDef",
379+
Seq("type B = A") -> "TypeDefOrDcl",
380+
Seq("def A(): Unit") -> "FunDefOrDcl")
381+
382+
for ((declerations, ignoreTokenType) <- cases;
383+
decleration <- declerations) {
384+
assertErrors(Nil, decleration, Map("ignoreTokenTypes" -> ignoreTokenType))
385+
}
386+
}
387+
388+
375389
}

0 commit comments

Comments
 (0)