Empty Line Pattern Under Multiline

ID

kotlin.regex_empty_line_multiline

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Kotlin

Tags

code-style

Description

Reports a regex whose pattern is exactly ^$ and whose MULTILINE option is in effect — set as RegexOption.MULTILINE, as a member of an inline option set, as the Java-interop Pattern.MULTILINE flag, or through a leading inline (?m) in the pattern itself.

Rationale

Under MULTILINE, ^ matches the start of the input and after any line terminator, while $ matches before a line terminator or at the end of the input. But ^ never matches at the very end of the input — and an empty string is entirely "end of input":

// Bad
val emptyLine = Regex("^$", RegexOption.MULTILINE)

emptyLine.containsMatchIn("a\n\nb")   // true   — correct
emptyLine.containsMatchIn("")         // false  — wrong: the whole input *is* one empty line
  • The pattern works on every input the author is likely to have tried, and fails only on the one that most looks like an empty line: the empty string itself.

  • The failure is silent. Nothing throws; a blank input is simply reported as containing no empty line, and the defect surfaces far from its cause.

Remediation

Handle the empty input explicitly, since no MULTILINE pattern can match it:

// Good
val emptyLine = Regex("^$", RegexOption.MULTILINE)

fun hasEmptyLine(text: String) = text.isEmpty() || emptyLine.containsMatchIn(text)

If the intent was to find a blank line rather than a strictly empty one, say so in the pattern:

val blankLine = Regex("""^\s*$""", RegexOption.MULTILINE)

What is not reported

A pattern that is anything other than exactly ^$ is not reported — ^\s*$ and ^end$ behave as written. A regex without MULTILINE is not reported either: there ^$ matches the empty string and nothing else, which is usually exactly what was meant.

A class-level or top-level property initializer is exempt, matching the guard the same check carries in other languages: that shape compiles the pattern once as a shared constant, where the empty-input question belongs at the call sites rather than at the declaration. A local val inside a function is reported like any other construction.

An option set built elsewhere and handed in as a variable — Regex("^$", options) — is not reported, because whether MULTILINE is on cannot be told at the construction site.

© 2026 Xygeni. All rights reserved