<%- await include("../../common-head.ejs.html") %> JavaScript syntax errors compared (2021)
<%- await include("../../common-nav.ejs.html") %>

JavaScript syntax errors compared (2021)

A JavaScript error beauty contest

Written by strager on

Introduction

Making mistakes is inevitable. Even JavaScript pros write syntax errors. The words unexpected token are familiar to junior and senior engineers alike.

How much time do us programmers waste hunting down syntax errors? I don't know. Probably a lot. Luckily, modern compilers and editors try to help us fix our mistakes.

Let's compare various tools to see how well they communicate JavaScript syntax errors to programmers.

Contestants

To automate the bug-finding process, let's use two kinds of JavaScript tools: tools you have to use (JS engines and transpilers) and static analysis tools designed to find bugs (linters). We'll test the following tools:

Code samples

There are so many syntax errors to choose from! Instead of inventing code for demonstration, let's take JavaScript snippets from real programmers on Stack Overflow:

Tip: Hover over the message on the right to show the error on the left.

To compare tools, I rated each error message on a scale from 1 to 5:

  1. Message suggests the correct solution
  2. Message suggests a solution which is either incorrect, confusingly worded, or in the wrong location
  3. Message explains the problem but does not suggest a solution
  4. Message explains a different problem or the problem in the wrong location
  5. Message suggests an incorrect solution or misleads the user, or there is no message reported at all

; instead of ,

$('.view-content').hoverscroll({
    arrows: true,
    arrowsOpacity: 0.7;
});
Asked by Joe on Stack Overflow
(CC BY-SA 3.0)
<%- codeExampleTableHTML({ "Ba": `Unexpected token, expected ","`, "ESL": `Unexpected token ;`, "JSC": `Unexpected token ';'. Expected '}' to end an object literal.`, "SM": `missing } after property list
note: { opened at line 1, column 31`, "TS": `',' expected.`, "V8": `Unexpected token ';'`, "qljs": `expected ',' between object literal entries`, }) %>

<%-rating(5)%> <%-Ba%>, <%-qljs%>, and <%-TS%> did a great job by suggesting a solution to the problem.

<%-rating(3)%> <%-ESL%> and <%-V8%> reported the problem but did not suggest a solution.

<%-rating(1)%> <%-JSC%> and <%-SM%> suggested incorrect solutions.

Invalid object literal key

var company = new Company({
  name: body.name,
  address: body.address,
  friends.name: body.friendName,
  statuses: { status: "New" },
});
Asked by jcubic on Stack Overflow
(CC BY-SA 3.0)
<%- codeExampleTableHTML({ "Ba": `Unexpected token, expected ","`, "ESL": `Unexpected token .`, "JSC": `Unexpected token '.'. Expected a ':' following the property name 'friends'.`, "SM": `missing : after property id`, "TS": `',' expected.
',' expected.
',' expected.`, "V8": `Unexpected token '.'`, "qljs": `unexpected expression; missing key for object entry
missing comma between object literal entries
token not implemented in parse_object_literal: colon`, }) %>

None of the tools suggested the correct solution to the problem.

<%-rating(4)%> <%-SM%> reported the problem and suggested a possible a solution.

<%-rating(3)%> <%-JSC%> reported the problem and suggested a possible a solution. However, the message is ambiguous because it reports only a line number and because the line contains two '.' characters.

<%-rating(3)%> <%-ESL%> and <%-V8%> reported the problem but did not suggest a solution.

<%-rating(1)%> <%-Ba%>, <%-qljs%>, and <%-TS%> and suggested incorrect solutions. qljs also crashed.

Newline after return

var homeModelTemplate = function(){
    return
    {
        fromDateSearch: new Date(),
        toDateSearch: new Date()
    };
};
Asked anonymously on Stack Overflow
(CC BY-SA 3.0)
<%- codeExampleTableHTML({ "Ba": `Missing semicolon.`, "ESL": `Unexpected token :`, "JSC": `Unexpected token ':'. Parse error.`, "SM": `unexpected token: ':'`, "TS": `';' expected.`, "V8": `Unexpected token ':'`, "qljs": `return statement returns nothing (undefined)
missing semicolon after statement
unexpected token
use of undeclared variable: toDateSearch`, }) %>

<%-rating(5)%> <%-qljs%> did a great job by reporting the real problem and hinting at the solution. qljs also reported unrelated errors.

<%-rating(2)%> <%-ESL%>, <%-JSC%>, <%-SM%>, and <%-V8%> reported a symptom of the problem, but did not guide the programmer toward a fix.

<%-rating(1)%> <%-Ba%> and <%-TS%> suggested incorrect solutions.

Missing ( ) around parameter

colls.map({id, ...other} => {
  return preview({
    key: id,
    ...other
  });
})
Asked by Emile Bergeron
on Stack Overflow

(CC BY-SA 4.0)
<%- codeExampleTableHTML({ "Ba": `Unexpected token, expected ","`, "ESL": `Unexpected token =>`, "JSC": `Unexpected token '=>'. Expected ')' to end an argument list.`, "SM": `invalid arrow-function arguments (parentheses around the arrow-function may help)`, "TS": `',' expected.
':' expected.
',' expected.`, "V8": `Malformed arrow function parameter list`, "qljs": `(no errors)`, }) %>

<%-rating(5)%> <%-SM%> did a great job by suggesting a solution to the problem.

<%-rating(4)%> <%-JSC%> and <%-V8%> hinted at a solution to the problem, but the message isn't as clear as SM's.

<%-rating(3)%> <%-ESL%> reported the problem but did not suggest a solution.

<%-rating(1)%> <%-Ba%> and <%-TS%> suggested incorrect solutions. TS also reported unrelated errors.

<%-rating(1)%> <%-qljs%> reported no error at all.

Keyword variable name

function Classes(class, sched) {
  this.class = class;
  this.scheduled = sched;
}
Asked by anonymous on Stack Overflow
(CC BY-SA 4.0)
<%- codeExampleTableHTML({ "Ba": `Unexpected keyword 'class'.`, "ESL": `Unexpected keyword 'class'`, "JSC": `Cannot use the keyword 'class' as a parameter name.`, "SM": `missing formal parameter`, "TS": `'class' is not allowed as a parameter name.
'{' expected.
Unexpected keyword or identifier.
Declaration or statement expected.
'{' expected.`, "V8": `Unexpected token 'class'`, "qljs": `token not implemented in parse_and_visit_­function_parameters: kw_class`, }) %>

<%-rating(5)%> <%-JSC%> and <%-TS%> clearly reported the problem and hinted at a solution. TS also reported unrelated errors.

<%-rating(3)%> <%-Ba%>, <%-ESL%>, <%-qljs%>, and <%-V8%> reported the problem but did not suggest a solution. qljs also crashed.

<%-rating(1)%> <%-SM%> suggested an incorrect solution.

Missing || between expressions

while( s2[Y]=="#" || s2[Y+1] =="X" s3[Y]=="#" || s3[Y+1] =="X");
Asked by bdukes on Stack Overflow
(CC BY-SA 3.0)
<%- codeExampleTableHTML({ "Ba": `Unexpected token, expected ")"`, "ESL": `Unexpected token s3`, "JSC": `Unexpected identifier 's3'. Expected ')' to end a while loop condition.`, "SM": `missing ) after condition`, "TS": `')' expected.
';' expected.`, "V8": `Unexpected identifier`, "qljs": `while loop is missing ')' around condition
unmatched parenthesis`, }) %>

None of the tools suggested the correct solution to the problem.

<%-rating(3)%> <%-ESL%> and <%-V8%> reported the problem but did not suggest a solution.

<%-rating(1)%> <%-Ba%>, <%-JSC%>, <%-qljs%>, <%-SM%>, and <%-TS%> suggested incorrect solutions to the problem.

elseif instead of else if

var car;
if(input.val() == "Lamborghini") {
    car = 389;
}elseif(input.val() == "Ferrari"){
    car = 349;
}else{
    car = 0;
}
Asked by TooCooL on Stack Overflow
(CC BY-SA 3.0)
<%- codeExampleTableHTML({ "Ba": `Missing semicolon.`, "ESL": `Unexpected token {`, "JSC": `Unexpected token '{'`, "SM": `unexpected token: '{'`, "TS": `';' expected.
Declaration or statement expected.`, "V8": `Unexpected token '{'`, "qljs": `missing semicolon after statement
'else' has no corresponding 'if'`, }) %>

<%-rating(3)%> <%-qljs%> reported that the if keyword was missing or misplaced. However, qljs also suggested an incorrect solution to the problem.

<%-rating(2)%> <%-ESL%>, <%-JSC%>, <%-SM%>, and <%-V8%> reported an unrelated problem but did not suggest a solution.

<%-rating(1)%> <%-Ba%> and <%-TS%> suggested incorrect solutions to the problem.

Missing , between arguments

$.ajax({
  url: "loadcontent1.php",
  data: {
    lastid: 'postitem',
  }
  success: function(html) {
    $("#content").append(html);
  }
});
Asked by Felix Kling on Stack Overflow
(CC BY-SA 3.0)
<%- codeExampleTableHTML({ "Ba": `Unexpected token, expected ","`, "ESL": `Unexpected token success`, "JSC": `Unexpected identifier 'success'. Expected '}' to end an object literal.`, "SM": `missing } after property list`, "TS": `',' expected.`, "V8": `Unexpected identifier`, "qljs": `missing comma between object literal entries`, }) %>

<%-rating(5)%> <%-qljs%> did a great job by suggesting a solution to the problem.

<%-rating(4)%> <%-Ba%> and <%-TS%> also suggested a solution to the problem, but at a worse location than qljs.

<%-rating(3)%> <%-ESL%> and <%-V8%> reported the problem but did not suggest a solution.

<%-rating(1)%> <%-JSC%> and <%-SM%> suggested incorrect solutions.

Summary

<% for (let engine of engines) { %> <% } %> <% for (let codeExampleID in scores) { %> <% for (let engine of engines) { %> <% } %> <% } %> <% for (let engine of engines) { %> <% } %> <% for (let engine of engines) { %> <% } %>
Tool scores (1-5) for code samples
Code sample<%- abbr(engine.name) %>avg
<%- codeExamples[codeExampleID] %> <%= scores[codeExampleID][engine.name] %><%= averageValues(scores[codeExampleID]).toFixed(1) %>
(total)<%= totalScoreForEngine(engine.name) %>
Code sample<%- abbr(engine.name) %>avg

No tool was amazing all finding bugs in all of our code samples. To get the best error experience, use a mix of tools, not just one tool.

<%-ESL%>, <%-qljs%>, and <%-V8%> stand out as good tools for debugging syntax errors. ESL and V8 consistently do a decent job, never scoring 1 in any code sample.

<%-qljs%> and <%-TS%> are volatile, doing a great job (5/5) or a terrible job (1/5) depending on the error in question.

Despite being tied for the worst tool overall, <%-SM%> beat all other tools in two code samples.

<%-qljs%>, <%-TS%>, and <%-V8%> report good-quality error spans. <%-Ba%>, <%-ESL%>, and <%-SM%> report only a single line-column location, not a span. <%-JSC%> only reports line numbers, which makes some messages (such as the one for Invalid object literal key) ambiguous.

Most tools did a bad job in two of the code samples, missing || between expressions and elseif instead of else if. Sometimes, broken code is hard to understand for computers.

<%-qljs%> and <%-TS%> try to recover after errors. Recovering is helpful in an editor, but sometimes it leads to confusing reports. All other tools stop at the first error.