E11065: literal appears multiple times - extract to a named constant

Category: E11xxx Code quality and IR analysis · Reported in phase: PRE_IR_CHECKS · Classification: REPEATED_MAGIC_LITERAL · All error codes

What went wrong

The same literal value appears 3 or more times in this file (or 4+ times across files in the same module). Repeated literals are a maintenance liability - when the value needs to change, every occurrence must be found and updated. Missing one creates a silent bug.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 800 (Why does EK9 reject repeated literal values?)

Rationale

Juergens et al. ("Do Code Clones Matter?", ICSE 2009) found inconsistent changes to duplicated code very frequent - only some copies get updated - and every second unintentional inconsistency a fault.

SonarQube (S1192), PMD, and Checkstyle all flag repeated string/numeric literals at thresholds of 2-4 occurrences. EK9 uses 3 per file and 4 per module - balanced thresholds that minimise false positives while catching real duplication.

For AI, every occurrence of the same literal is a redundant token that must be mentally unified. A named constant is one concept, stated once, referenced everywhere - dramatically reducing the token cost of understanding the module.

How to fix it

If the repeats are ARGUMENTS of a call - a constructor or a function - label the arguments instead: Cookie(name: "session", cookieValue: "abc123"). The label says what the value is, the compiler checks it against the declared parameter (E06250), and a labelled argument is not counted. Label every argument of the call (all or none).

Otherwise - and whenever the value is genuinely shared, such as the same locale tag in several files - extract the repeated literal to a named constant.

With tooling, ek9_extract_constant does it in one call: file, the literal as written (literal:'"java/lang/StringBuilder"') and a name - every occurrence already in the file is replaced and the `defines constant` block is created or extended for you. If a tool edit that added another copy was rolled back for this error, that copy was never written: run ek9_extract_constant on the copies already in the file, then use the name in the edit you resend - or, when none is in the file yet, define the constant first with ek9_new_construct mode:'constant' (a name, the literal as its value). Do not hand-insert a `defines constant` block with a text edit: the block's position and indentation are the compiler's rules, and a misplaced one cascades (measured: 15 new diagnostics from one raw edit).

By hand: declare the value once as a named constant in a constant block of the module and use the name everywhere the literal was. The [EXAMPLE] section below shows "processing" printed three times (and the same literal repeated in constructor and function calls), then a TAX_RATE constant used by three functions.

The constant creates a single point of change and gives the value a name that communicates its purpose.

See also

  • E11064: magic literal in comparison

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E11065: literal appears multiple times - extract to a named constant

#!ek9
defines module error.e11065.bad

  defines function

    testRepeatedString()
      stdout <- Stdout()
      stdout.println("processing")
      stdout.println("processing")
      stdout.println("processing")

    //A constructor call is not exempt (D4): it was once, by the callee's name starting with an uppercase
    //letter. Label the arguments - Cookie(name: "session", cookieValue: ...) - or name a shared value.
    testRepeatedStringInConstructors()
      c1 <- Cookie("session", "abc123")
      c2 <- Cookie("session", "def456")
      c3 <- Cookie("session", "ghi789")
      require c1? and c2? and c3?

    //The same heuristic exempted any FUNCTION whose name starts with an uppercase letter.
    testRepeatedStringToUppercaseFunction()
      first <- Shout("hello")
      second <- Shout("hello")
      third <- Shout("hello")
      require first? and second? and third?

    Shout()
      -> text as String
      <- rtn as String: text.upperCase()

Fixed: Name the literal once, as a constant

#!ek9
defines module error.e11065.good

  defines constant

    TAX_RATE <- 0.2

  defines function

    tax()
      -> amount as Float
      <- rtn as Float: amount * TAX_RATE

    net()
      -> amount as Float
      <- rtn as Float: amount - amount * TAX_RATE

    gross()
      -> amount as Float
      <- rtn as Float: amount + amount * TAX_RATE

Questions and answers

These Q&A entries show E11065 and its fix: