ASI is a parser repair rule, not "newlines end statements". The spec defines three cases:
- Offending token. The parser reads left to right. If it meets a token the grammar doesn't allow there, it inserts a
;before that token if (a) there's a line terminator between it and the previous token, (b) the token is}, or (c) the previous token is)and the;would end ado…while. - End of input. If the token stream ends and the program isn't complete, a
;is added. - Restricted productions. After a few keywords,
[no LineTerminator here]forbids a newline:return,throw,break,continue,yield, postfix++/--, and the=>of an arrow function (andasyncbeforefunction). A line break there makes the parser insert a;even though the next token would have been legal.
Two overriding limits: a ; is never inserted if it would become an empty statement, or one of the two semicolons in a for(;;) header.
When it bites:
returnfollowed by a newline returnsundefined; the value on the next line is dead code. Keep the value, or an opening(/{, on thereturnline.- A line starting with
(,[, a template literal,+,-, or/is a legal continuation, so no;is inserted and the two lines merge into a call, property access, tagged template, or binary expression. Use a semicolon on the previous line, or in "no-semicolon" code style, a leading;on these lines. anewline++bparses asa; ++b, because postfix++is restricted.
The reverse also exists: ASI can't fix real syntax errors, so for (let i = 0⏎i < 3⏎i++) is a SyntaxError.