One way to test for empty lines is to use the regex "^$"
, which can be extremely handy when filtering out empty lines from collections
of Strings, for instance. With regard to this, the Javadoc for Pattern (Line Terminators) states the
following:
By default, the regular expressions ^ and $
ignore line terminators and only match at the beginning and the end, respectively, of
the entire input sequence. If MULTILINE
mode is activated then ^ matches at the beginning of input and after any line terminator
except at the end of input. When in MULTILINE
mode $
matches just before a line terminator or the end of
the input sequence.
As emphasized, ^ is not going to match at the end of an input, and the end of the input is necessarily included in the empty string, which might
lead to completely missing empty lines, while it would be the initial reason for using such regex.
Therefore, when searching for empty lines using a multi-line regular expression, you should also check whether the string is empty.
This rule is raising an issue every time a pattern that can match the empty string is used with MULTILINE
flag and without calling
isEmpty()
on the string.
Noncompliant code example
val p = Pattern.compile("^$", Pattern.MULTILINE) // Noncompliant
val r = Regex("^$", RegexOption.MULTILINE) // Noncompliant
// Alternatively
val p = Pattern.compile("(?m)^$") // Noncompliant
val r = Regex("(?m)^$") // Noncompliant
fun containsEmptyLines(str: String) : Boolean {
return p.matcher(str).find()
}
fun containsEmptyLinesKotlin(str: String) = r.find(str) != null
// ...
println(containsEmptyLines("a\n\nb")) // correctly prints 'true'
println(containsEmptyLinesKotlin("a\n\nb")) // correctly prints 'true'
println(containsEmptyLines("")) // incorrectly prints 'false'
println(containsEmptyLinesKotlin("")) // incorrectly prints 'false'
Compliant solution
val p = Pattern.compile("^$", Pattern.MULTILINE) // Noncompliant
val r = Regex("^$", RegexOption.MULTILINE) // Noncompliant
fun containsEmptyLines(str: String) : Boolean {
return p.matcher(str).find() || str.isEmpty()
}
fun containsEmptyLinesKotlin(str: String) = r.find(str) != null || str.isEmpty()
// ...
println(containsEmptyLines("a\n\nb")) // correctly prints 'true'
println(containsEmptyLinesKotlin("a\n\nb")) // correctly prints 'true'
println(containsEmptyLines("")) // correctly prints 'true'
println(containsEmptyLinesKotlin("")) // correctly prints 'true'