consider adding more "[no LineTerminator here]" to avoid problem caused by omit the semicolon

# 程劭非 (6 years ago)

everyone,

During some recent discussion in Chinese JS community, I've noticed several case which JS behave out of user's expect if omitting the semicolon.

See the following code:

var a = this.a //!!!! here no semicolon will be auto inserted [1,2,3].forEach(function(){ // do something })

(function(){ //do something })() //!!!! here no semicolon will be auto inserted (function(){ //do something else })()

I was thinking that if we could change some grammar rules to make it behave as most user's expect. Just adding several [no LineTerminator here] will do so:

CallExpression : MemberExpression [no LineTerminator here] Arguments CallExpression [no LineTerminator here] Arguments CallExpression [no LineTerminator here] [ Expression ] CallExpression . IdentifierName

Though some of you might consider omitting the semicolon as a bad style, but its used by several group and company (including zepto.js and npmjs). I think this change will benefit them a lot with very small side effect.

# Claus Reinke (6 years ago)

var a = this.a //!!!! here no semicolon will be auto inserted [1,2,3].forEach(function(){ // do something })

Consider

arr.map(function(..){..}) // no semicolon wanted here
    [..]

(function(){ //do something })() //!!!! here no semicolon will be auto inserted (function(){ //do something else })()

Consider

curried_async_function(..first argument set..) // no semicolon wanted 

here (function(result){..}) // callback argument

Unfortunately, while ASI creates obvious problems, those do not seem to have obvious solutions. Not to mention that any actual changes to ASI might break existing code.

My own favourite approach would link ASI to layout/indentation, and introduce warnings instead of breaking code:

1 if ASI kicks in, but indentation suggests statement continuation, issue a warning 2 if ASI does not kick in, but indentation suggests new statement, issue a warning

Item 2 would cover your examples, without breaking mine, while item 1 would cover another popular ASI trap:

return // no semicolon intended here
    {..}

while still allowing for

return    // semicolon intended here
dead_code()

Claus

# Claus Reinke (6 years ago)

My own favourite approach would link ASI to layout/indentation, and introduce warnings instead of breaking code:

1 if ASI kicks in, but indentation suggests statement continuation, issue a warning 2 if ASI does not kick in, but indentation suggests new statement, issue a warning

I did once try to implement this, by instrumenting esprima to keep track of indents and ASI and other side-conditions, but got somewhat distracted/disillusioned by the number of special cases that kept popping up. Nevertheless, that sketch is now available as a gist:

gist.github.com/2973296

Perhaps it inspires someone to do it right?-) Claus

// ----------------- output $ node basil.js sample.js ASI at line 5(2) before indented line 6(8) multiline VariableDeclaration 12(2), not indented: line 13(2) multiline VariableDeclaration 21(2), not indented: line 22(2) ASI at line 23(2) before indented line 24(11) multiline IfStatement 33(2), not indented: line 34(2) ASI at line 37(2) before indented line 38(4) multiline ExpressionStatement 40(2), not indented: line 42(2) multiline WhileStatement 44(2), not indented: line 45(2)-line 47(2)

// ----------------- sample.js $ cat sample.js

// examples

function restricted_production() { return //; "hi"; // warn here return //; "hi"; // don't warn here }

function newline_no_asi() { var x = ["hi"] //; [1]; // warn here var y = ["hi"] //; [1]; // don't warn here }

function newline_error_asi(alp,ha) { var a1 = "hi"+ "ho"; // don't warn here var a2 = "hi"+ "ho"; // warn here var b1 = "hi" //; "ho"; // warn here var b2 = "hi" //; "ho"; // don't warn here }

function continued_statements() { if (condition) { } else { // don't warn here } // don't warn here if (condition) f () // warn here if (condition) f () // don't warn here if (condition) return "" // warn here if (condition) return "" // don't warn here // don't warn here (comment/empty multiline) FIXME while (condition) { } // don't warn here while (condition) f () // warn here

function f() { } // don't warn here }

# Jason Orendorff (6 years ago)

On Fri, Jun 22, 2012 at 4:08 AM, 程劭非 <csf178 at gmail.com> wrote:

CallExpression :    MemberExpression [no LineTerminator here] Arguments    CallExpression [no LineTerminator here] Arguments

I'm reminded of fab.js: jed/fab which this would break, but maybe that's a good thing. ;)

# Brendan Eich (6 years ago)

1JS combined with browser game theory means no such breaking change will be made.

I'm still working on paren-free, but I'm not sure how it would happen at this point. First, it needs to handle the issues Waldemar raised.

# Jussi Kalliokoski (6 years ago)

I recently made a blog post concerning this issue [1], and my suggestion is to prefix literals (function, array and friends) with void, as it's almost (if not) designed for this case. So, (function(){}()); becomes void function(){}(), [1,2,3].forEach(...) becomes void [1,2,3].forEach(...). This is more resistant to typos as well, even in semicolon-preferring code.

Since Claus shared his work on the subject, I thought I'd also share a little tool I made [2], called asifier. It's a sort of an education tool that could be employed by editors, IDEs and such to show where semicolons will be inserted. It can also replace a semicolonless file with inserted semicolons so that missing semicolons isn't a problem for projects that otherwise follow explicit semicolon rules.

Cheers, Jussi

[1] blog.avd.io/posts/semicolonoscopy [2] jussi-kalliokoski/asifier

# Brandon Benvie (6 years ago)

I recently started using void for IIFE's with no return value because it's is by far the clearest way to indicate intent up front and helps to turn the expression into something of a unique looking construct that helps clearly identify it. It would be nice to see this practice become more common because it seems to conveniently solve a number of problems at one.

# Claus Reinke (6 years ago)

My own favourite approach would link ASI to layout/indentation, and introduce warnings instead of breaking code:

1 if ASI kicks in, but indentation suggests statement continuation, issue a warning 2 if ASI does not kick in, but indentation suggests new statement, issue a warning

I did once try to implement this, .. gist.github.com/2973296

I've updated the gist slightly: instead of checking for indentation mismatches on every multiline statement, it now only checks at known troublespots.

This means fewer false warnings, but also fewer warnings in general, so the old mode is still available by option. The new mode's output makes it clearer that basil tries to match ASI and layout/indentation:

// $ node basil.js sample.js // ASI at line 5(2), before indented line 6(8) // no ASI at line 12(2), before non-indented line 13(2) // ASI at line 25(2) before indented line 26(11) // no ASI at line 35(2), before non-indented line 36(2) // no ASI at line 39(2), before non-indented line 40(2) // ASI at line 43(2), before indented line 44(4) // no ASI at line 50(2), before non-indented line 51(2)

At the moment, the troublespots are roughly those indicated by the thread starter, ie, in expressions, before arguments (..) and computed properties [..].

Should it also check in infix applications? For instance, what about

var a2 = "hi"+ "ho"; // warn here? var a3 = "hi" +"ho"; // warn here?

Are there other cases where ASI might wrongly be expected which should trigger warnings?

Claus