Skip to content

Is for (using of = obj;;); valid syntax? #248

Open

Description

@arai-a

(discovered in https://bugzilla.mozilla.org/show_bug.cgi?id=1934205 )

In the proposal, ForInOfStatement definition is modified and the using handling is added into ForDeclaration with [Using] parameter, where the parameter is set only after a negative lookahead for for of using of:

https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-for-in-and-for-of-statements&secAll=true

ForInOfStatement[Yield, Await, Return] :
  for ( [lookahead ≠ let [] LeftHandSideExpression[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  for ( var ForBinding[?Yield, ?Await, +Pattern] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  for ( ForDeclaration[?Yield, ?Await, ~Using] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  for ( [lookahead ∉ { let, async of }] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  for ( var ForBinding[?Yield, ?Await, +Pattern] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  for ( [lookahead ≠ using of] ForDeclaration[?Yield, ?Await, +Using] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  [+Await] for await ( [lookahead ≠ let] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  [+Await] for await ( var ForBinding[?Yield, ?Await, +Pattern] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
  [+Await] for await ( [lookahead ≠ using of] ForDeclaration[?Yield, ?Await, +Using] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]

ForDeclaration[Yield, Await, Using] :
  LetOrConst ForBinding[?Yield, ?Await, +Pattern]
  [+Using] using [no LineTerminator here] ForBinding[?Yield, ?Await, ~Pattern]
  [+Using, +Await] await [no LineTerminator here] using [no LineTerminator here] ForBinding[?Yield, +Await, ~Pattern]

So, for (using of cannot become a prefix of the for-in/for-of with using declaration, but it can be a prefix of for-of with the using being an identifier in LeftHandSideExpression.

On the other hand, the proposal doesn't touch the ForStatement definition, and the using handling is added into LexicalDeclaration unconditionally.

https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-for-statement&secAll=true

ForStatement[Yield, Await, Return] :
  ...
  for ( LexicalDeclaration[~In, ?Yield, ?Await] Expression[+In, ?Yield, ?Await]opt ; Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return]

https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-let-const-using-and-await-using-declarations&secAll=true

LexicalDeclaration[In, Yield, Await] :
  LetOrConst BindingList[?In, ?Yield, ?Await, +Pattern] ;
  UsingDeclaration[?In, ?Yield, ?Await]
  [+Await] AwaitUsingDeclaration[?In, ?Yield]

https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-let-const-using-and-await-using-declarations

UsingDeclaration[In, Yield, Await] :
  using [no LineTerminator here] BindingList[?In, ?Yield, ?Await, ~Pattern] ;

AwaitUsingDeclaration[In, Yield] :
  CoverAwaitExpressionAndAwaitUsingDeclarationHead[?Yield] [no LineTerminator here] BindingList[?In, ?Yield, +Await, ~Pattern] ;

This means ForStatement matches for (using of = obj;;);.
Is this expected?

TypeScript doesn't recognize the syntax, and
Babel, esbuild, Chromium, and SpiderMonkey throw syntax error.
Apparently the negative lookahead is applied also to ForStatement in all of them.

If for (using of = obj;;); should throw SyntaxError, the ForStatement syntax should be modified to have a negative lookahead, with propagating the existence of using to LexicalDeclaration, or maybe replacing that part with something similar to ForDeclaration.

Activity

  1. dead-claudia commented on Dec 22, 2024

    @dead-claudia

    It's valid in for statements, and all of those tools supportint using declarations but not in either/both for statement types are wrong. Here's some related PRs showing that it's clearly intended to be supported.

  2. arai-a commented on Dec 22, 2024

    @arai-a
    Author

    Just to make sure, here's my understanding and my intent.

    Those PRs looks related only to the C-style for + using.
    I agree that the for + using is valid in the proposal, and all those implementation parses for (using x = obj;;);.
    This issue is about the of being a binding identifier in the context, so, using being followed by of.

    Is it correct that of can be a binding identifier in C-style for + using declaration, and for (using of can be a prefix for both for (using of obj); (regular for-of without using declaration, where using is an identifier) and for (using of = obj;;); (C-style for with using declaration, where of is an identifier) ?

    If that's the case, I think there should be some explicit note about that, given that all implementation misinterpreted it.

  3. dead-claudia commented on Dec 22, 2024

    @dead-claudia

    @arai-a Oh, sorry, I misunderstood. Here's per the spec as it is:

    Feels like a spec bug, to be honest. The spec should either reject both or allow both.

    IMHO it makes more sense to allow both. Allowing it is as simple as adding explicit productions for for ( using in <expr> ) <stmt>, for ( using of <expr> ) <stmt>, and for ( using of of <expr> ) <stmt> and adding using to the negative lookahead in the for ( <lhs> of <expr> ) <stmt> clause, and then specifying runtime semantics accordingly for each.

    for ( using <ident> in <expr> ) <stmt> should be wholly disallowed IMHO and that's probably also a spec oversight.

  4. arai-a commented on Dec 22, 2024

    @arai-a
    Author

    Allowing for (using of of <expr>) is not possible, in term of tokenization and parsing.

    If for (using of of <expr>) is allowed, it means for (using of of can be a prefix of both of the following:

    • for (using of <expr starts with of>) : for-of without using declaration, where using being LHS, and the expr being iterated
    • for (using of of <expr>) : for-of with using declaration, where the 1st of being a binding identifier and the expr being iterated

    In this case, tokenization (DIV vs RegExp) after the 2nd of conflicts between those cases, and it's not deterministic.

    For example, if we have for (using of of /a/g); source, it matches both of the following:

    • for-of without using, where the "of DIV a DIV g" being the iterated value. This is not SyntaxError in the current spec without this proposal.
    • for-of with using, where a RegExp literal /a/g being the iterated value.

    Thus, if we're to match the behaviors of C-style for and for-of, the only way would be to reject of being a binding identifier of using declaration in both cases.

  5. arai-a commented on Dec 23, 2024

    @arai-a
    Author

    Possible modification would be to first add the Using parameter to LexicalDeclaration, with making both UsingDeclaration and AwaitUsingDeclaration conditional on the parameter:

    LexicalDeclaration[In, Yield, Using, Await] :
      LetOrConst BindingList[?In, ?Yield, ?Await, +Pattern] ;
      [+Using] UsingDeclaration[?In, ?Yield, ?Await]
      [+Using, +Await] AwaitUsingDeclaration[?In, ?Yield]
    

    and then split the ForStatement with LexicalDeclaration case into 2, one without Using parameter, and one with Using parameter but only when not starts with using of:

    ForStatement[Yield, Await, Return] :
      ...
      for ( LexicalDeclaration[~In, ?Yield, ~Using, ?Await]
            Expression[+In, ?Yield, ?Await]opt ;
            Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return]
      for ( [lookahead ≠ using of] LexicalDeclaration[~In, ?Yield, +Using, ?Await]
            Expression[+In, ?Yield, ?Await]opt ;
            Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return]
    

    and then modify the other occurrences of LexicalDeclaration to have +Using parameter.

    Or maybe add another symbol that replaces LexicalDeclaration in ForStatement, so that the other existing LexicalDeclaration occurrences aren't affected by this change.

  6. dead-claudia commented on Dec 23, 2024

    @dead-claudia

    Allowing for (using of of <expr>) is not possible, in term of tokenization and parsing.

    If for (using of of <expr>) is allowed, it means for (using of of can be a prefix of both of the following:

    • for (using of <expr starts with of>) : for-of without using declaration, where using being LHS, and the expr being iterated
    • for (using of of <expr>) : for-of with using declaration, where the 1st of being a binding identifier and the expr being iterated

    Oh, whoops, that was the one case I missed. 🤦‍♀️ I knew it'd be possible with some grammar hackery, just was wrong about the specifics.

    I still stand by my opinion on either allowing both or prohibiting both, but to be clear, that's not what the current spec says.

  7. dead-claudia commented on Dec 23, 2024

    @dead-claudia

    Also, misread the spec (again): for using ... in isn't valid.

  8. JLHwung commented on Apr 23, 2025

    @JLHwung
    Contributor

    I believe the spec is correct: there should be no restriction for the LexicalDeclaration within ForStatement: If for(using of = obj;;;); is invalid while using of = obj is valid, this is a refactoring hazard as moving variable declarations into ForStatement should be generally safe as long as the scope is valid.

    Therefore I think it is an implementation bug: I have proposed a fix for Babel in babel/babel#17254. The cost of the fix should be at most a lookahead when the parser sees using of, which is presumably pretty rare in real-world code base.

  9. rbuckton commented on May 21, 2025

    @rbuckton
    Member

    I've added a test to test262 to verify this: tc39/test262@fc3079a

    This also appears to be a bug in TypeScript:

    Image

  10. rbuckton commented on May 21, 2025

    @rbuckton
    Member

    I can verify this is parsed incorrectly in V8 13.6.233.8:

    Welcome to Node.js v24.0.2.
    Type ".help" for more information.
    > for (using x = null;;) break;
    undefined
    > for (using of = null;;) break;
    for (using of = null;;) break;
                  ^
    
    Uncaught SyntaxError: Unexpected token '='
    > process.versions.v8
    '13.6.233.8-node.10'
    

    @syg

  11. syg commented on May 22, 2025

    @syg

    what the hell

  12. rbuckton commented on May 22, 2025

    @rbuckton
    Member

    It matches var, let, and const in the same position.

  13. syg commented on May 22, 2025

    @syg

    Is the fix equivalent to another lookahead checking for = or ;?

    Edit: Oh I guess just = because it's a const.

  14. added a commit that references this issue on Jun 3, 2025
  15. alexsch01 commented on Jun 14, 2025

    @alexsch01

    why is "of" allowed as a variable name in JavaScript?


    "in" is not allowed

    SyntaxError: Unexpected token 'in'
    
  16. dead-claudia commented on Jun 15, 2025

    @dead-claudia

    why is "of" allowed as a variable name in JavaScript?


    "in" is not allowed

    SyntaxError: Unexpected token 'in'
    

    of was never reserved. Same reason async and let are still allowed. It's just a contextual keyword.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions