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:
- cyclomatic_complexity
- missing_fat_arrows
- no_empty_functions
- no_private_function_fat_arrows
- no_unnecessary_fat_arrows
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_strictistrue); 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, thisinside 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
thiscannot be determined statically (because ofFunction.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 onthisbeing 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
- Rules Overview for the complete list.
- Writing Custom Rules to build your own AST-based checks.