skip to content

How do you suppress a single detekt finding in Kotlin source without editing detekt.yml?

level: juniorimportance: should knowfreq 50%

answer

  1. It reuses a standard Kotlin annotation
  2. Rule id goes in the annotation string
  3. Smallest enclosing declaration, not the class
  4. Aliases like 'unused' also match

basics

~20 s

Annotate the smallest declaration that contains the finding with Kotlin's @Suppress, passing detekt's rule id — for example @Suppress("MagicNumber") on one function. detekt reuses the standard annotation; a configured alias or the rule set id also works.

solid answer

~40 s

detekt honours Kotlin's own `@Suppress` annotation, so a local exception is an annotation on the **smallest enclosing declaration** — the property, function, or class that contains the flagged code — for example `@Suppress("MagicNumber")` on a single function, or `@file:Suppress("MagicNumber")` at the top of a file when the whole file is the exception. The string is detekt's rule id; several rules also declare an `aliases` list in the config, which is why `@Suppress("unused")` silences `UnusedPrivateProperty` and `@Suppress("DEPRECATION")` silences the `Deprecation` rule. A rule set id such as `complexity` suppresses the whole set for that declaration, which is almost always too coarse. I treat every suppression as debt: keep it on the narrowest declaration and put a one-line comment next to it explaining why the rule does not apply here, so review can challenge it later.

code

kotlin · 11 lines
kotlin
@Suppress("MagicNumber") // protocol field offsets fixed by the spec
fun decodeHeader(bytes: ByteArray): Header =
    Header(
        version = bytes[0].toInt(),
        flags = bytes[3].toInt(),
        length = bytes[7].toInt(),
    )

// Alias declared by the rule's config: silences UnusedPrivateProperty too
@Suppress("unused")
private val reservedForFutureUse = 0

go deeper

for a junior

Recall that detekt reuses Kotlin's @Suppress and that the string is the rule id shown in the report. Be able to place it on the offending function rather than the whole file.

for a middle

Explain the resolution order: annotation on the nearest declaration, alias names from the rule's config, rule set ids as a coarse fallback, and why file-level targeting needs the @file: prefix.

for a senior

Show judgment about which mechanism fits: one justified exception is an annotation, a category of files is a path exclude, and pre-existing violations are a different tool entirely. Treat unexplained suppressions as review findings.

for a principal

Own the policy: how many suppressions a module may carry before the rule itself is considered mis-tuned, and how suppression density is surfaced so standards stay a decision rather than an accumulation of quiet holes.

## The mechanism detekt does not invent its own off-switch syntax. It reads Kotlin's standard `@Suppress` annotation, the same one the Kotlin compiler and IntelliJ inspections use, and skips any finding whose location sits inside a declaration annotated with a name that matches the reporting rule. That is the whole model: there is nothing to learn beyond *which name to pass* and *where to put the annotation*. ## Which name to pass Three kinds of string work: - **The rule id** — the exact rule name as it appears in the report and in `detekt.yml`: `MagicNumber`, `LongMethod`, `SwallowedException`, `TooGenericExceptionCaught`. This is the normal choice. - **A configured alias.** Many built-in rules ship with an `aliases` list in the default configuration so that the names people already use from IntelliJ keep working. `UnusedPrivateProperty`, `UnusedPrivateFunction` and `UnusedPrivateClass` all declare the alias `unused`; `UnusedParameter` declares `UNUSED_PARAMETER` and `unused`; the `Deprecation` rule declares `DEPRECATION`. So an annotation you added for the compiler often silences the matching detekt rule as a side effect — useful, and occasionally surprising when a rule goes quiet for a reason you did not intend. - **The rule set id** — `complexity`, `style`, `naming`, `potential-bugs`, `exceptions`, `performance`, `coroutines`, `empty-blocks`, `comments`. This silences every rule in that set for the annotated declaration. It is a blunt instrument and rarely the right answer in review. ## Where to put the annotation On the **nearest enclosing declaration** — the function, property, constructor, class, or object that contains the flagged element. Kotlin does not let you annotate an arbitrary statement in the middle of a function body, so the practical granularity is one declaration. If genuinely the whole file is an exception (a generated file, a table of protocol constants), the file-level target `@file:Suppress("MagicNumber")` goes above the `package` line. The common mistake is annotating too high: putting `@Suppress("LongMethod")` on the class silences it for every function in that class, including ones written next month. Push the annotation as far down as the language allows. ## Suppression versus configuration The annotation is for a **deliberate, local, justified** exception — the one place where the idiom really does not apply. Two other tools cover different cases, and mixing them up is what an interviewer listens for: - A rule that is wrong for a *whole category of files* (test sources, generated code) belongs in that rule's `excludes` list of path globs in `detekt.yml`, not in a hundred annotations. - A rule that is right but has *pre-existing* violations you do not want to fix today is what a baseline file is for. Reaching for `@Suppress` where one of those fits produces annotation litter that nobody dares remove. ## Practices that survive review Always pair the annotation with a short comment saying *why*: `@Suppress("MagicNumber") // wire-format field offsets, defined by the spec`. A suppression with a reason is a decision; a bare one is an unexplained hole in the standard. Grep for `@Suppress` occasionally — a growing count in a module usually means a rule is mis-tuned for that code and should be configured properly instead. Finally, remember that a suppressed finding is genuinely gone: it is not reported, not counted, and does not appear in the HTML, XML or SARIF output. Nothing downstream will remind you it exists, which is exactly why the comment matters.

  • Why does @Suppress("unused") sometimes silence a detekt finding you never targeted?
    Several detekt rules declare alias names in their configuration so that familiar IntelliJ suppression strings keep working. `UnusedPrivateProperty`, `UnusedPrivateFunction`, `UnusedPrivateClass`, `UnusedParameter` and `UnusedVariable` all list `unused` as an alias, so one annotation added for the IDE quietly covers those rules too.
  • When is a rule's excludes list the better answer than an annotation?
    When the exception is a whole category of files rather than one decision. Test sources, generated code and script files usually justify a path glob such as `excludes: ['**/test/**']` on the rule in `detekt.yml`. That is one reviewable line instead of dozens of annotations that nobody will ever dare delete.

It is the same key as the compiler's own mute button — detekt just answers to extra names, and you want to mute one instrument, not the orchestra.

saying these in an interview costs you the question

  • Thinking a trailing comment can disable a detekt rule
  • Putting @Suppress on the class instead of the function
  • Using @Suppress for pre-existing debt across the codebase
  • Suppressing a whole rule set id for one finding
  • Adding suppressions with no justification comment

context