Function and Class Rules

These rules analyze the structure of your code through the abstract syntax tree (AST). They catch problems that are invisible at the token level: functions that lose their this binding, fat arrows that do nothing, empty callbacks caused by bad indentation, and functions that have grown too complex.

Rules on this page:

A Quick Refresher on Arrows

In CoffeeScript, -> defines a function whose this depends on how it is called, while => (the fat arrow) binds this to the value it had where the function was defined. Several rules below help you use the right arrow in the right place.

cyclomatic_complexity

Property Value
Default level ignore
Message The cyclomatic complexity is too damn high
Options value: maximum allowed complexity (default 10)

Measures the cyclomatic complexity of each function: the number of independent paths through it. Complexity starts at 1 and increases by:

Construct Adds
if / unless 1
while / until / loop 1
for 1
try 1
and / &&, or / || 1 each
switch 1 per when clause

A function is reported when its complexity exceeds value. Nested functions are measured on their own and do not add to the enclosing function's score. The report spans the whole function, and the context is the computed complexity:

     ⚡ #12-48: The cyclomatic complexity is too damn high. 14.

This rule is a good candidate for warn: it highlights code that deserves refactoring without blocking a build. CoffeeLint's own repository uses:

{
  "cyclomatic_complexity": {
    "value": 11,
    "level": "warn"
  }
}

Note: The project's documentation notes that using IcedCoffeeScript as a custom compiler breaks this rule.

missing_fat_arrows

Property Value
Default level ignore
Message Used this in a function without a fat arrow
Options is_strict (default false)

Warns when a function defined with -> uses this (or @) in its body or parameters. Such functions often lose their binding when passed as callbacks:

class Widget
  bind: ->
    @el.on 'click', ->
      @toggle()          # Reported: inside this callback, @ is not the Widget

  toggle: ->
    @open = not @open    # Not reported: class methods are bound to the instance

The fix is a fat arrow on the callback: @el.on 'click', => @toggle().

The rule skips:

  • methods defined in a class body (unless is_strict is true); functions nested inside a method are still checked, as the example shows,
  • constructors, including any functions nested inside them,
  • assignments to a prototype (for example Foo::bar = -> @baz), including their nested functions,
  • this inside nested classes.

With is_strict: true, class methods are checked too, so a method that uses this must be defined with =>.

Note: Whether a function is called with the right this cannot be determined statically (because of Function.prototype.call, bind, and similar), so this rule can produce false positives. Use an inline directive when a thin arrow is intentional, for example in jQuery handlers that rely on this being the DOM element.

no_empty_functions

Property Value
Default level ignore
Message Empty function
Options none

Reports functions with an empty body. The main goal is to catch callbacks that are empty because the next line was not indented:

someFunctionWithCallback ->
doSomethingSignificant()

Here doSomethingSignificant() runs immediately and unconditionally instead of inside the callback. If you really mean to pass a callback that does nothing, make it explicit:

someFunctionWithCallback ->
  undefined
doSomethingSignificant()

no_private_function_fat_arrows

Property Value
Default level warn
Message Used the fat arrow for a private function
Options none

Inside a class body, a function assigned to a local variable (a "private" function, as opposed to a method on the prototype) gains nothing from a fat arrow: there is no instance this to bind at that point.

class Counter
  helper = =>        # Reported
    42

  helper2 = ->       # Fine
    42

  increment: ->
    @count += helper2()

no_unnecessary_fat_arrows

Property Value
Default level warn
Message Unnecessary fat arrow
Options none

Reports fat-arrow functions that never use this, either directly, in parameters (such as (@name) =>), through super, or through a nested fat-arrow function that needs it. Binding has a small cost and signals intent the code does not have:

double = (x) => x * 2      # Reported: no `this` involved
double = (x) -> x * 2      # Fine

class Repo
  saveAll: (items) ->
    items.forEach (item) =>  # Fine: uses @store
      @store.put(item)

Choosing Between These Rules

missing_fat_arrows and no_unnecessary_fat_arrows work as a pair: one demands => where this is used, the other rejects => where it is not. Enabling both gives you a consistent arrow discipline across the codebase.

See Also