2025-10-15 10:12:44 +03:00

6588 lines
259 KiB
Go

package parser
import (
"strings"
"sync"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
)
type ParsingContext int
const (
PCSourceElements ParsingContext = iota // Elements in source file
PCBlockStatements // Statements in block
PCSwitchClauses // Clauses in switch statement
PCSwitchClauseStatements // Statements in switch clause
PCTypeMembers // Members in interface or type literal
PCClassMembers // Members in class declaration
PCEnumMembers // Members in enum declaration
PCHeritageClauseElement // Elements in a heritage clause
PCVariableDeclarations // Variable declarations in variable statement
PCObjectBindingElements // Binding elements in object binding list
PCArrayBindingElements // Binding elements in array binding list
PCArgumentExpressions // Expressions in argument list
PCObjectLiteralMembers // Members in object literal
PCJsxAttributes // Attributes in jsx element
PCJsxChildren // Things between opening and closing JSX tags
PCArrayLiteralMembers // Members in array literal
PCParameters // Parameters in parameter list
PCJSDocParameters // JSDoc parameters in parameter list of JSDoc function type
PCRestProperties // Property names in a rest type list
PCTypeParameters // Type parameters in type parameter list
PCTypeArguments // Type arguments in type argument list
PCTupleElementTypes // Element types in tuple element type list
PCHeritageClauses // Heritage clauses for a class or interface declaration.
PCImportOrExportSpecifiers // Named import clause's import specifier list
PCImportAttributes // Import attributes
PCJSDocComment // Parsing via JSDocParser
PCCount // Number of parsing contexts
)
type ParsingContexts int
type Parser struct {
scanner *scanner.Scanner
factory ast.NodeFactory
opts ast.SourceFileParseOptions
sourceText string
scriptKind core.ScriptKind
languageVariant core.LanguageVariant
diagnostics []*ast.Diagnostic
jsDiagnostics []*ast.Diagnostic
jsdocDiagnostics []*ast.Diagnostic
token ast.Kind
sourceFlags ast.NodeFlags
contextFlags ast.NodeFlags
parsingContexts ParsingContexts
statementHasAwaitIdentifier bool
hasDeprecatedTag bool
hasParseError bool
identifiers map[string]string
identifierCount int
notParenthesizedArrow collections.Set[int]
nodeSlicePool core.Pool[*ast.Node]
stringSlicePool core.Pool[string]
jsdocCache map[*ast.Node][]*ast.Node
possibleAwaitSpans []int
jsdocCommentsSpace []string
jsdocCommentRangesSpace []ast.CommentRange
jsdocTagCommentsSpace []string
jsdocTagCommentsPartsSpace []*ast.Node
reparseList []*ast.Node
commonJSModuleIndicator *ast.Node
currentParent *ast.Node
setParentFromContext ast.Visitor
}
func newParser() *Parser {
res := &Parser{}
res.initializeClosures()
return res
}
var viableKeywordSuggestions = scanner.GetViableKeywordSuggestions()
var parserPool = sync.Pool{
New: func() any {
return newParser()
},
}
func getParser() *Parser {
return parserPool.Get().(*Parser)
}
func putParser(p *Parser) {
*p = Parser{scanner: p.scanner, setParentFromContext: p.setParentFromContext}
parserPool.Put(p)
}
func ParseSourceFile(opts ast.SourceFileParseOptions, sourceText string, scriptKind core.ScriptKind) *ast.SourceFile {
p := getParser()
defer putParser(p)
p.initializeState(opts, sourceText, scriptKind)
p.nextToken()
if p.scriptKind == core.ScriptKindJSON {
return p.parseJSONText()
}
return p.parseSourceFileWorker()
}
func (p *Parser) initializeClosures() {
p.setParentFromContext = func(n *ast.Node) bool {
n.Parent = p.currentParent
return false
}
}
func (p *Parser) parseJSONText() *ast.SourceFile {
pos := p.nodePos()
var statements *ast.NodeList
var eof *ast.TokenNode
if p.token == ast.KindEndOfFile {
statements = p.newNodeList(core.NewTextRange(pos, p.nodePos()), nil)
eof = p.parseTokenNode()
} else {
var expressions any // []*ast.Expression | *ast.Expression
for p.token != ast.KindEndOfFile {
var expression *ast.Expression
switch p.token {
case ast.KindOpenBracketToken:
expression = p.parseArrayLiteralExpression()
case ast.KindTrueKeyword, ast.KindFalseKeyword, ast.KindNullKeyword:
expression = p.parseTokenNode()
case ast.KindMinusToken:
if p.lookAhead(func(p *Parser) bool {
return p.nextToken() == ast.KindNumericLiteral && p.nextToken() != ast.KindColonToken
}) {
expression = p.parsePrefixUnaryExpression()
} else {
expression = p.parseObjectLiteralExpression()
}
case ast.KindNumericLiteral, ast.KindStringLiteral:
if p.lookAhead(func(p *Parser) bool { return p.nextToken() != ast.KindColonToken }) {
expression = p.parseLiteralExpression(false /*intern*/)
break
}
fallthrough
default:
expression = p.parseObjectLiteralExpression()
}
// Error recovery: collect multiple top-level expressions
if expressions != nil {
if es, ok := expressions.([]*ast.Expression); ok {
expressions = append(es, expression)
} else {
expressions = []*ast.Expression{expressions.(*ast.Expression), expression}
}
} else {
expressions = expression
if p.token != ast.KindEndOfFile {
p.parseErrorAtCurrentToken(diagnostics.Unexpected_token)
}
}
}
var expression *ast.Expression
if es, ok := expressions.([]*ast.Expression); ok {
expression = p.factory.NewArrayLiteralExpression(p.newNodeList(core.NewTextRange(pos, p.nodePos()), es), false)
} else {
expression = expressions.(*ast.Expression)
}
statement := p.finishNode(p.factory.NewExpressionStatement(expression), pos)
statements = p.newNodeList(core.NewTextRange(pos, p.nodePos()), []*ast.Node{statement})
eof = p.parseExpectedToken(ast.KindEndOfFile)
}
node := p.finishNode(p.factory.NewSourceFile(p.opts, p.sourceText, statements, eof), pos)
result := node.AsSourceFile()
p.finishSourceFile(result, false)
return result
}
func ParseIsolatedEntityName(text string) *ast.EntityName {
p := getParser()
defer putParser(p)
p.initializeState(ast.SourceFileParseOptions{
JSDocParsingMode: ast.JSDocParsingModeParseAll,
}, text, core.ScriptKindJS)
p.nextToken()
entityName := p.parseEntityName(true, nil)
return core.IfElse(p.token == ast.KindEndOfFile && len(p.diagnostics) == 0, entityName, nil)
}
func (p *Parser) initializeState(opts ast.SourceFileParseOptions, sourceText string, scriptKind core.ScriptKind) {
if scriptKind == core.ScriptKindUnknown {
panic("ScriptKind must be specified when parsing source files.")
}
if p.scanner == nil {
p.scanner = scanner.NewScanner()
} else {
p.scanner.Reset()
}
p.opts = opts
p.sourceText = sourceText
p.scriptKind = scriptKind
p.languageVariant = getLanguageVariant(p.scriptKind)
switch p.scriptKind {
case core.ScriptKindJS, core.ScriptKindJSX:
p.contextFlags = ast.NodeFlagsJavaScriptFile
case core.ScriptKindJSON:
p.contextFlags = ast.NodeFlagsJavaScriptFile | ast.NodeFlagsJsonFile
default:
p.contextFlags = ast.NodeFlagsNone
}
p.scanner.SetText(p.sourceText)
p.scanner.SetOnError(p.scanError)
p.scanner.SetLanguageVariant(p.languageVariant)
p.scanner.SetScriptKind(p.scriptKind)
p.scanner.SetJSDocParsingMode(p.opts.JSDocParsingMode)
}
func (p *Parser) scanError(message *diagnostics.Message, pos int, length int, args ...any) {
p.parseErrorAtRange(core.NewTextRange(pos, pos+length), message, args...)
}
func (p *Parser) parseErrorAt(pos int, end int, message *diagnostics.Message, args ...any) *ast.Diagnostic {
return p.parseErrorAtRange(core.NewTextRange(pos, end), message, args...)
}
func (p *Parser) parseErrorAtCurrentToken(message *diagnostics.Message, args ...any) *ast.Diagnostic {
return p.parseErrorAtRange(p.scanner.TokenRange(), message, args...)
}
func (p *Parser) parseErrorAtRange(loc core.TextRange, message *diagnostics.Message, args ...any) *ast.Diagnostic {
// Don't report another error if it would just be at the same location as the last error
var result *ast.Diagnostic
if len(p.diagnostics) == 0 || p.diagnostics[len(p.diagnostics)-1].Pos() != loc.Pos() {
result = ast.NewDiagnostic(nil, loc, message, args...)
p.diagnostics = append(p.diagnostics, result)
}
p.hasParseError = true
return result
}
type ParserState struct {
scannerState scanner.ScannerState
contextFlags ast.NodeFlags
diagnosticsLen int
jsDiagnosticsLen int
statementHasAwaitIdentifier bool
hasParseError bool
}
func (p *Parser) mark() ParserState {
return ParserState{
scannerState: p.scanner.Mark(),
contextFlags: p.contextFlags,
diagnosticsLen: len(p.diagnostics),
jsDiagnosticsLen: len(p.jsDiagnostics),
statementHasAwaitIdentifier: p.statementHasAwaitIdentifier,
hasParseError: p.hasParseError,
}
}
func (p *Parser) rewind(state ParserState) {
p.scanner.Rewind(state.scannerState)
p.token = p.scanner.Token()
p.contextFlags = state.contextFlags
p.diagnostics = p.diagnostics[0:state.diagnosticsLen]
p.jsDiagnostics = p.jsDiagnostics[0:state.jsDiagnosticsLen]
p.statementHasAwaitIdentifier = state.statementHasAwaitIdentifier
p.hasParseError = state.hasParseError
}
func (p *Parser) lookAhead(callback func(p *Parser) bool) bool {
state := p.mark()
result := callback(p)
p.rewind(state)
return result
}
func (p *Parser) nextToken() ast.Kind {
// if the keyword had an escape
if ast.IsKeyword(p.token) && (p.scanner.HasUnicodeEscape() || p.scanner.HasExtendedUnicodeEscape()) {
// issue a parse error for the escape
p.parseErrorAtCurrentToken(diagnostics.Keywords_cannot_contain_escape_characters)
}
p.token = p.scanner.Scan()
return p.token
}
func (p *Parser) nextTokenWithoutCheck() ast.Kind {
p.token = p.scanner.Scan()
return p.token
}
func (p *Parser) nextTokenJSDoc() ast.Kind {
p.token = p.scanner.ScanJSDocToken()
return p.token
}
func (p *Parser) nextJSDocCommentTextToken(inBackticks bool) ast.Kind {
p.token = p.scanner.ScanJSDocCommentTextToken(inBackticks)
return p.token
}
func (p *Parser) nodePos() int {
return p.scanner.TokenFullStart()
}
func (p *Parser) hasPrecedingLineBreak() bool {
return p.scanner.HasPrecedingLineBreak()
}
func (p *Parser) hasPrecedingJSDocComment() bool {
return p.scanner.HasPrecedingJSDocComment()
}
func (p *Parser) parseSourceFileWorker() *ast.SourceFile {
isDeclarationFile := tspath.IsDeclarationFileName(p.opts.FileName)
if isDeclarationFile {
p.contextFlags |= ast.NodeFlagsAmbient
}
pos := p.nodePos()
statements := p.parseListIndex(PCSourceElements, (*Parser).parseToplevelStatement)
end := p.nodePos()
endHasJSDoc := p.hasPrecedingJSDocComment()
eof := p.parseTokenNode()
p.withJSDoc(eof, endHasJSDoc)
if eof.Kind != ast.KindEndOfFile {
panic("Expected end of file token from scanner.")
}
if len(p.reparseList) > 0 {
statements = append(statements, p.reparseList...)
p.reparseList = nil
}
node := p.finishNode(p.factory.NewSourceFile(p.opts, p.sourceText, p.newNodeList(core.NewTextRange(pos, end), statements), eof), pos)
result := node.AsSourceFile()
p.finishSourceFile(result, isDeclarationFile)
if !result.IsDeclarationFile && result.ExternalModuleIndicator != nil && len(p.possibleAwaitSpans) > 0 {
reparse := p.finishNode(p.reparseTopLevelAwait(result), pos)
if node != reparse {
result = reparse.AsSourceFile()
p.finishSourceFile(result, isDeclarationFile)
}
}
collectExternalModuleReferences(result)
if ast.IsInJSFile(node) {
result.SetJSDiagnostics(attachFileToDiagnostics(p.jsDiagnostics, result))
}
return result
}
func (p *Parser) finishSourceFile(result *ast.SourceFile, isDeclarationFile bool) {
result.CommentDirectives = p.scanner.CommentDirectives()
result.Pragmas = getCommentPragmas(&p.factory, p.sourceText)
p.processPragmasIntoFields(result)
result.SetDiagnostics(attachFileToDiagnostics(p.diagnostics, result))
result.SetJSDocDiagnostics(attachFileToDiagnostics(p.jsdocDiagnostics, result))
result.CommonJSModuleIndicator = p.commonJSModuleIndicator
result.IsDeclarationFile = isDeclarationFile
result.LanguageVariant = p.languageVariant
result.ScriptKind = p.scriptKind
result.Flags |= p.sourceFlags
result.Identifiers = p.identifiers
result.NodeCount = p.factory.NodeCount()
result.TextCount = p.factory.TextCount()
result.IdentifierCount = p.identifierCount
result.SetJSDocCache(p.jsdocCache)
ast.SetExternalModuleIndicator(result, p.opts.ExternalModuleIndicatorOptions)
}
func (p *Parser) parseToplevelStatement(i int) *ast.Node {
p.statementHasAwaitIdentifier = false
statement := p.parseStatement()
if p.statementHasAwaitIdentifier && statement.Flags&ast.NodeFlagsAwaitContext == 0 {
if len(p.possibleAwaitSpans) == 0 || p.possibleAwaitSpans[len(p.possibleAwaitSpans)-1] != i {
p.possibleAwaitSpans = append(p.possibleAwaitSpans, i, i+1)
} else {
p.possibleAwaitSpans[len(p.possibleAwaitSpans)-1] = i + 1
}
}
return statement
}
func (p *Parser) reparseTopLevelAwait(sourceFile *ast.SourceFile) *ast.Node {
if len(p.possibleAwaitSpans)%2 == 1 {
panic("possibleAwaitSpans malformed: odd number of indices, not paired into spans.")
}
statements := []*ast.Statement{}
savedParseDiagnostics := p.diagnostics
p.diagnostics = []*ast.Diagnostic{}
afterAwaitStatement := 0
for i := 0; i < len(p.possibleAwaitSpans); i += 2 {
nextAwaitStatement := p.possibleAwaitSpans[i]
// append all non-await statements between afterAwaitStatement and nextAwaitStatement
prevStatement := sourceFile.Statements.Nodes[afterAwaitStatement]
nextStatement := sourceFile.Statements.Nodes[nextAwaitStatement]
statements = append(statements, sourceFile.Statements.Nodes[afterAwaitStatement:nextAwaitStatement]...)
// append all diagnostics associated with the copied range
diagnosticStart := core.FindIndex(savedParseDiagnostics, func(diagnostic *ast.Diagnostic) bool {
return diagnostic.Pos() >= prevStatement.Pos()
})
var diagnosticEnd int
if diagnosticStart >= 0 {
diagnosticEnd = core.FindIndex(savedParseDiagnostics[:diagnosticStart], func(diagnostic *ast.Diagnostic) bool {
return diagnostic.Pos() >= nextStatement.Pos()
})
} else {
diagnosticEnd = -1
}
if diagnosticStart >= 0 {
var slice []*ast.Diagnostic
if diagnosticEnd >= 0 {
slice = savedParseDiagnostics[diagnosticStart : diagnosticStart+diagnosticEnd]
} else {
slice = savedParseDiagnostics[diagnosticStart:]
}
p.diagnostics = append(p.diagnostics, slice...)
}
state := p.mark()
// reparse all statements between start and pos. We skip existing diagnostics for the same range and allow the parser to generate new ones.
p.contextFlags |= ast.NodeFlagsAwaitContext
p.scanner.ResetPos(nextStatement.Pos())
p.nextToken()
afterAwaitStatement = p.possibleAwaitSpans[i+1]
for p.token != ast.KindEndOfFile {
startPos := p.scanner.TokenFullStart()
statement := p.parseStatement()
statements = append(statements, statement)
if startPos == p.scanner.TokenFullStart() {
p.nextToken()
}
if afterAwaitStatement < len(sourceFile.Statements.Nodes) {
nonAwaitStatement := sourceFile.Statements.Nodes[afterAwaitStatement]
if statement.End() == nonAwaitStatement.Pos() {
// done reparsing this section
break
}
if statement.End() > nonAwaitStatement.Pos() {
// we ate into the next statement, so we must continue reparsing the next span
i += 2
if i < len(p.possibleAwaitSpans) {
afterAwaitStatement = p.possibleAwaitSpans[i+1]
} else {
afterAwaitStatement = len(sourceFile.Statements.Nodes)
}
}
}
}
// Keep diagnostics from the reparse
state.diagnosticsLen = len(p.diagnostics)
p.rewind(state)
}
// append all statements between pos and the end of the list
if afterAwaitStatement < len(sourceFile.Statements.Nodes) {
prevStatement := sourceFile.Statements.Nodes[afterAwaitStatement]
statements = append(statements, sourceFile.Statements.Nodes[afterAwaitStatement:]...)
// append all diagnostics associated with the copied range
diagnosticStart := core.FindIndex(savedParseDiagnostics, func(diagnostic *ast.Diagnostic) bool {
return diagnostic.Pos() >= prevStatement.Pos()
})
if diagnosticStart >= 0 {
p.diagnostics = append(p.diagnostics, savedParseDiagnostics[diagnosticStart:]...)
}
}
result := p.factory.NewSourceFile(sourceFile.ParseOptions(), p.sourceText, p.newNodeList(sourceFile.Statements.Loc, statements), sourceFile.EndOfFileToken)
for _, s := range statements {
s.Parent = result.AsNode() // force (re)set parent to reparsed source file
}
return result
}
func (p *Parser) parseListIndex(kind ParsingContext, parseElement func(p *Parser, index int) *ast.Node) []*ast.Node {
saveParsingContexts := p.parsingContexts
p.parsingContexts |= 1 << kind
list := make([]*ast.Node, 0, 16)
for i := 0; !p.isListTerminator(kind); i++ {
if p.isListElement(kind, false /*inErrorRecovery*/) {
elt := parseElement(p, i)
if len(p.reparseList) > 0 {
list = append(list, p.reparseList...)
p.reparseList = nil
}
list = append(list, elt)
continue
}
if p.abortParsingListOrMoveToNextToken(kind) {
break
}
}
p.parsingContexts = saveParsingContexts
return p.nodeSlicePool.Clone(list)
}
func (p *Parser) parseList(kind ParsingContext, parseElement func(p *Parser) *ast.Node) *ast.NodeList {
pos := p.nodePos()
nodes := p.parseListIndex(kind, func(p *Parser, _ int) *ast.Node { return parseElement(p) })
return p.newNodeList(core.NewTextRange(pos, p.nodePos()), nodes)
}
// Return a non-nil (but possibly empty) slice if parsing was successful, or nil if parseElement returned nil
func (p *Parser) parseDelimitedList(kind ParsingContext, parseElement func(p *Parser) *ast.Node) *ast.NodeList {
pos := p.nodePos()
saveParsingContexts := p.parsingContexts
p.parsingContexts |= 1 << kind
list := make([]*ast.Node, 0, 16)
for {
if p.isListElement(kind, false /*inErrorRecovery*/) {
startPos := p.nodePos()
element := parseElement(p)
if element == nil {
p.parsingContexts = saveParsingContexts
// Return nil to indicate parseElement failed
return nil
}
list = append(list, element)
if p.parseOptional(ast.KindCommaToken) {
// No need to check for a zero length node since we know we parsed a comma
continue
}
if p.isListTerminator(kind) {
break
}
// We didn't get a comma, and the list wasn't terminated, explicitly parse
// out a comma so we give a good error message.
if p.token != ast.KindCommaToken && kind == PCEnumMembers {
p.parseErrorAtCurrentToken(diagnostics.An_enum_member_name_must_be_followed_by_a_or)
} else {
p.parseExpected(ast.KindCommaToken)
}
// If the token was a semicolon, and the caller allows that, then skip it and
// continue. This ensures we get back on track and don't result in tons of
// parse errors. For example, this can happen when people do things like use
// a semicolon to delimit object literal members. Note: we'll have already
// reported an error when we called parseExpected above.
if (kind == PCObjectLiteralMembers || kind == PCImportAttributes) && p.token == ast.KindSemicolonToken && !p.hasPrecedingLineBreak() {
p.nextToken()
}
if startPos == p.nodePos() {
// What we're parsing isn't actually remotely recognizable as a element and we've consumed no tokens whatsoever
// Consume a token to advance the parser in some way and avoid an infinite loop
// This can happen when we're speculatively parsing parenthesized expressions which we think may be arrow functions,
// or when a modifier keyword which is disallowed as a parameter name (ie, `static` in strict mode) is supplied
p.nextToken()
}
continue
}
if p.isListTerminator(kind) {
break
}
if p.abortParsingListOrMoveToNextToken(kind) {
break
}
}
p.parsingContexts = saveParsingContexts
return p.newNodeList(core.NewTextRange(pos, p.nodePos()), p.nodeSlicePool.Clone(list))
}
// Return a non-nil (but possibly empty) NodeList if parsing was successful, or nil if opening token wasn't found
// or parseElement returned nil.
func (p *Parser) parseBracketedList(kind ParsingContext, parseElement func(p *Parser) *ast.Node, opening ast.Kind, closing ast.Kind) *ast.NodeList {
if p.parseExpected(opening) {
result := p.parseDelimitedList(kind, parseElement)
p.parseExpected(closing)
return result
}
return p.parseEmptyNodeList()
}
func (p *Parser) parseEmptyNodeList() *ast.NodeList {
return p.newNodeList(core.NewTextRange(p.nodePos(), p.nodePos()), nil)
}
// Returns true if we should abort parsing.
func (p *Parser) abortParsingListOrMoveToNextToken(kind ParsingContext) bool {
p.parsingContextErrors(kind)
if p.isInSomeParsingContext() {
return true
}
p.nextToken()
return false
}
// True if positioned at element or terminator of the current list or any enclosing list
func (p *Parser) isInSomeParsingContext() bool {
// We should be in at least one parsing context, be it SourceElements while parsing
// a SourceFile, or JSDocComment when lazily parsing JSDoc.
debug.Assert(p.parsingContexts != 0, "Missing parsing context")
for kind := ParsingContext(0); kind < PCCount; kind++ {
if p.parsingContexts&(1<<kind) != 0 {
if p.isListElement(kind, true /*inErrorRecovery*/) || p.isListTerminator(kind) {
return true
}
}
}
return false
}
func (p *Parser) parsingContextErrors(context ParsingContext) {
switch context {
case PCSourceElements:
if p.token == ast.KindDefaultKeyword {
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, "export")
} else {
p.parseErrorAtCurrentToken(diagnostics.Declaration_or_statement_expected)
}
case PCBlockStatements:
p.parseErrorAtCurrentToken(diagnostics.Declaration_or_statement_expected)
case PCSwitchClauses:
p.parseErrorAtCurrentToken(diagnostics.X_case_or_default_expected)
case PCSwitchClauseStatements:
p.parseErrorAtCurrentToken(diagnostics.Statement_expected)
case PCRestProperties, PCTypeMembers:
p.parseErrorAtCurrentToken(diagnostics.Property_or_signature_expected)
case PCClassMembers:
p.parseErrorAtCurrentToken(diagnostics.Unexpected_token_A_constructor_method_accessor_or_property_was_expected)
case PCEnumMembers:
p.parseErrorAtCurrentToken(diagnostics.Enum_member_expected)
case PCHeritageClauseElement:
p.parseErrorAtCurrentToken(diagnostics.Expression_expected)
case PCVariableDeclarations:
if ast.IsKeyword(p.token) {
p.parseErrorAtCurrentToken(diagnostics.X_0_is_not_allowed_as_a_variable_declaration_name, scanner.TokenToString(p.token))
} else {
p.parseErrorAtCurrentToken(diagnostics.Variable_declaration_expected)
}
case PCObjectBindingElements:
p.parseErrorAtCurrentToken(diagnostics.Property_destructuring_pattern_expected)
case PCArrayBindingElements:
p.parseErrorAtCurrentToken(diagnostics.Array_element_destructuring_pattern_expected)
case PCArgumentExpressions:
p.parseErrorAtCurrentToken(diagnostics.Argument_expression_expected)
case PCObjectLiteralMembers:
p.parseErrorAtCurrentToken(diagnostics.Property_assignment_expected)
case PCArrayLiteralMembers:
p.parseErrorAtCurrentToken(diagnostics.Expression_or_comma_expected)
case PCJSDocParameters:
p.parseErrorAtCurrentToken(diagnostics.Parameter_declaration_expected)
case PCParameters:
if ast.IsKeyword(p.token) {
p.parseErrorAtCurrentToken(diagnostics.X_0_is_not_allowed_as_a_parameter_name, scanner.TokenToString(p.token))
} else {
p.parseErrorAtCurrentToken(diagnostics.Parameter_declaration_expected)
}
case PCTypeParameters:
p.parseErrorAtCurrentToken(diagnostics.Type_parameter_declaration_expected)
case PCTypeArguments:
p.parseErrorAtCurrentToken(diagnostics.Type_argument_expected)
case PCTupleElementTypes:
p.parseErrorAtCurrentToken(diagnostics.Type_expected)
case PCHeritageClauses:
p.parseErrorAtCurrentToken(diagnostics.Unexpected_token_expected)
case PCImportOrExportSpecifiers:
if p.token == ast.KindFromKeyword {
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, "}")
} else {
p.parseErrorAtCurrentToken(diagnostics.Identifier_expected)
}
case PCJsxAttributes, PCJsxChildren, PCJSDocComment:
p.parseErrorAtCurrentToken(diagnostics.Identifier_expected)
case PCImportAttributes:
p.parseErrorAtCurrentToken(diagnostics.Identifier_or_string_literal_expected)
default:
panic("Unhandled case in parsingContextErrors")
}
}
func (p *Parser) isListElement(parsingContext ParsingContext, inErrorRecovery bool) bool {
switch parsingContext {
case PCSourceElements, PCBlockStatements, PCSwitchClauseStatements:
// If we're in error recovery, then we don't want to treat ';' as an empty statement.
// The problem is that ';' can show up in far too many contexts, and if we see one
// and assume it's a statement, then we may bail out inappropriately from whatever
// we're parsing. For example, if we have a semicolon in the middle of a class, then
// we really don't want to assume the class is over and we're on a statement in the
// outer module. We just want to consume and move on.
return !(p.token == ast.KindSemicolonToken && inErrorRecovery) && p.isStartOfStatement()
case PCSwitchClauses:
return p.token == ast.KindCaseKeyword || p.token == ast.KindDefaultKeyword
case PCTypeMembers:
return p.lookAhead((*Parser).scanTypeMemberStart)
case PCClassMembers:
// We allow semicolons as class elements (as specified by ES6) as long as we're
// not in error recovery. If we're in error recovery, we don't want an errant
// semicolon to be treated as a class member (since they're almost always used
// for statements.
return p.lookAhead((*Parser).scanClassMemberStart) || p.token == ast.KindSemicolonToken && !inErrorRecovery
case PCEnumMembers:
// Include open bracket computed properties. This technically also lets in indexers,
// which would be a candidate for improved error reporting.
return p.token == ast.KindOpenBracketToken || p.isLiteralPropertyName()
case PCObjectLiteralMembers:
switch p.token {
case ast.KindOpenBracketToken, ast.KindAsteriskToken, ast.KindDotDotDotToken, ast.KindDotToken: // Not an object literal member, but don't want to close the object (see `tests/cases/fourslash/completionsDotInObjectLiteral.ts`)
return true
default:
return p.isLiteralPropertyName()
}
case PCRestProperties:
return p.isLiteralPropertyName()
case PCObjectBindingElements:
return p.token == ast.KindOpenBracketToken || p.token == ast.KindDotDotDotToken || p.isLiteralPropertyName()
case PCImportAttributes:
return p.isImportAttributeName()
case PCHeritageClauseElement:
// If we see `{ ... }` then only consume it as an expression if it is followed by `,` or `{`
// That way we won't consume the body of a class in its heritage clause.
if p.token == ast.KindOpenBraceToken {
return p.isValidHeritageClauseObjectLiteral()
}
if !inErrorRecovery {
return p.isStartOfLeftHandSideExpression() && !p.isHeritageClauseExtendsOrImplementsKeyword()
}
// If we're in error recovery we tighten up what we're willing to match.
// That way we don't treat something like "this" as a valid heritage clause
// element during recovery.
return p.isIdentifier() && !p.isHeritageClauseExtendsOrImplementsKeyword()
case PCVariableDeclarations:
return p.isBindingIdentifierOrPrivateIdentifierOrPattern()
case PCArrayBindingElements:
return p.token == ast.KindCommaToken || p.token == ast.KindDotDotDotToken || p.isBindingIdentifierOrPrivateIdentifierOrPattern()
case PCTypeParameters:
return p.token == ast.KindInKeyword || p.token == ast.KindConstKeyword || p.isIdentifier()
case PCArrayLiteralMembers:
// Not an array literal member, but don't want to close the array (see `tests/cases/fourslash/completionsDotInArrayLiteralInObjectLiteral.ts`)
if p.token == ast.KindCommaToken || p.token == ast.KindDotToken {
return true
}
fallthrough
case PCArgumentExpressions:
return p.token == ast.KindDotDotDotToken || p.isStartOfExpression()
case PCParameters:
return p.isStartOfParameter(false /*isJSDocParameter*/)
case PCJSDocParameters:
return p.isStartOfParameter(true /*isJSDocParameter*/)
case PCTypeArguments, PCTupleElementTypes:
return p.token == ast.KindCommaToken || p.isStartOfType(false /*inStartOfParameter*/)
case PCHeritageClauses:
return p.isHeritageClause()
case PCImportOrExportSpecifiers:
// bail out if the next token is [FromKeyword StringLiteral].
// That means we're in something like `import { from "mod"`. Stop here can give better error message.
if p.token == ast.KindFromKeyword && p.lookAhead((*Parser).nextTokenIsTokenStringLiteral) {
return false
}
if p.token == ast.KindStringLiteral {
return true // For "arbitrary module namespace identifiers"
}
return tokenIsIdentifierOrKeyword(p.token)
case PCJsxAttributes:
return tokenIsIdentifierOrKeyword(p.token) || p.token == ast.KindOpenBraceToken
case PCJsxChildren:
return true
case PCJSDocComment:
return true
}
panic("Unhandled case in isListElement")
}
func (p *Parser) isListTerminator(kind ParsingContext) bool {
if p.token == ast.KindEndOfFile {
return true
}
switch kind {
case PCBlockStatements, PCSwitchClauses, PCTypeMembers, PCClassMembers, PCEnumMembers, PCObjectLiteralMembers,
PCObjectBindingElements, PCImportOrExportSpecifiers, PCImportAttributes:
return p.token == ast.KindCloseBraceToken
case PCSwitchClauseStatements:
return p.token == ast.KindCloseBraceToken || p.token == ast.KindCaseKeyword || p.token == ast.KindDefaultKeyword
case PCHeritageClauseElement:
return p.token == ast.KindOpenBraceToken || p.token == ast.KindExtendsKeyword || p.token == ast.KindImplementsKeyword
case PCVariableDeclarations:
// If we can consume a semicolon (either explicitly, or with ASI), then consider us done
// with parsing the list of variable declarators.
// In the case where we're parsing the variable declarator of a 'for-in' statement, we
// are done if we see an 'in' keyword in front of us. Same with for-of
// ERROR RECOVERY TWEAK:
// For better error recovery, if we see an '=>' then we just stop immediately. We've got an
// arrow function here and it's going to be very unlikely that we'll resynchronize and get
// another variable declaration.
return p.canParseSemicolon() || p.token == ast.KindInKeyword || p.token == ast.KindOfKeyword || p.token == ast.KindEqualsGreaterThanToken
case PCTypeParameters:
// Tokens other than '>' are here for better error recovery
return p.token == ast.KindGreaterThanToken || p.token == ast.KindOpenParenToken || p.token == ast.KindOpenBraceToken || p.token == ast.KindExtendsKeyword || p.token == ast.KindImplementsKeyword
case PCArgumentExpressions:
// Tokens other than ')' are here for better error recovery
return p.token == ast.KindCloseParenToken || p.token == ast.KindSemicolonToken
case PCArrayLiteralMembers, PCTupleElementTypes, PCArrayBindingElements:
return p.token == ast.KindCloseBracketToken
case PCJSDocParameters, PCParameters, PCRestProperties:
// Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery
return p.token == ast.KindCloseParenToken || p.token == ast.KindCloseBracketToken /*|| token == ast.KindOpenBraceToken*/
case PCTypeArguments:
// All other tokens should cause the type-argument to terminate except comma token
return p.token != ast.KindCommaToken
case PCHeritageClauses:
return p.token == ast.KindOpenBraceToken || p.token == ast.KindCloseBraceToken
case PCJsxAttributes:
return p.token == ast.KindGreaterThanToken || p.token == ast.KindSlashToken
case PCJsxChildren:
return p.token == ast.KindLessThanToken && p.lookAhead((*Parser).nextTokenIsSlash)
}
return false
}
func (p *Parser) parseExpectedJSDoc(kind ast.Kind) bool {
if p.token == kind {
p.nextTokenJSDoc()
return true
}
if !isKeywordOrPunctuation(kind) {
panic("Invalid JSDoc kind: expected keyword or punctuation")
}
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(kind))
return false
}
func (p *Parser) parseExpectedMatchingBrackets(openKind ast.Kind, closeKind ast.Kind, openParsed bool, openPosition int) {
if p.token == closeKind {
p.nextToken()
return
}
lastError := p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(closeKind))
if !openParsed {
return
}
if lastError != nil {
related := ast.NewDiagnostic(nil, core.NewTextRange(openPosition, openPosition+1), diagnostics.The_parser_expected_to_find_a_1_to_match_the_0_token_here, scanner.TokenToString(openKind), scanner.TokenToString(closeKind))
lastError.AddRelatedInfo(related)
}
}
func (p *Parser) parseOptional(token ast.Kind) bool {
if p.token == token {
p.nextToken()
return true
}
return false
}
func (p *Parser) parseExpected(kind ast.Kind) bool {
return p.parseExpectedWithDiagnostic(kind, nil, true)
}
func (p *Parser) parseExpectedWithoutAdvancing(kind ast.Kind) bool {
return p.parseExpectedWithDiagnostic(kind, nil, false)
}
func (p *Parser) parseExpectedWithDiagnostic(kind ast.Kind, message *diagnostics.Message, shouldAdvance bool) bool {
if p.token == kind {
if shouldAdvance {
p.nextToken()
}
return true
}
// Report specific message if provided with one. Otherwise, report generic fallback message.
if message != nil {
p.parseErrorAtCurrentToken(message)
} else {
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(kind))
}
return false
}
func (p *Parser) parseTokenNode() *ast.Node {
pos := p.nodePos()
kind := p.token
p.nextToken()
return p.finishNode(p.factory.NewToken(kind), pos)
}
func (p *Parser) parseExpectedToken(kind ast.Kind) *ast.Node {
token := p.parseOptionalToken(kind)
if token == nil {
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(kind))
token = p.finishNode(p.factory.NewToken(kind), p.nodePos())
}
return token
}
func (p *Parser) parseOptionalToken(kind ast.Kind) *ast.Node {
if p.token == kind {
return p.parseTokenNode()
}
return nil
}
func (p *Parser) parseExpectedTokenJSDoc(kind ast.Kind) *ast.Node {
optional := p.parseOptionalTokenJSDoc(kind)
if optional == nil {
if !isKeywordOrPunctuation(kind) {
panic("expected keyword or punctuation")
}
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(kind))
optional = p.finishNode(p.factory.NewToken(kind), p.nodePos())
}
return optional
}
func (p *Parser) parseOptionalTokenJSDoc(kind ast.Kind) *ast.Node {
if p.token == kind {
return p.parseTokenNode()
}
return nil
}
func (p *Parser) parseStatement() *ast.Statement {
switch p.token {
case ast.KindSemicolonToken:
return p.parseEmptyStatement()
case ast.KindOpenBraceToken:
return p.parseBlock(false /*ignoreMissingOpenBrace*/, nil)
case ast.KindVarKeyword:
return p.parseVariableStatement(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/)
case ast.KindLetKeyword:
if p.isLetDeclaration() {
return p.parseVariableStatement(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/)
}
case ast.KindAwaitKeyword:
if p.isAwaitUsingDeclaration() {
return p.parseVariableStatement(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/)
}
case ast.KindUsingKeyword:
if p.isUsingDeclaration() {
return p.parseVariableStatement(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/)
}
case ast.KindFunctionKeyword:
return p.parseFunctionDeclaration(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/)
case ast.KindClassKeyword:
return p.parseClassDeclaration(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/)
case ast.KindIfKeyword:
return p.parseIfStatement()
case ast.KindDoKeyword:
return p.parseDoStatement()
case ast.KindWhileKeyword:
return p.parseWhileStatement()
case ast.KindForKeyword:
return p.parseForOrForInOrForOfStatement()
case ast.KindContinueKeyword:
return p.parseContinueStatement()
case ast.KindBreakKeyword:
return p.parseBreakStatement()
case ast.KindReturnKeyword:
return p.parseReturnStatement()
case ast.KindWithKeyword:
return p.parseWithStatement()
case ast.KindSwitchKeyword:
return p.parseSwitchStatement()
case ast.KindThrowKeyword:
return p.parseThrowStatement()
case ast.KindTryKeyword, ast.KindCatchKeyword, ast.KindFinallyKeyword:
return p.parseTryStatement()
case ast.KindDebuggerKeyword:
return p.parseDebuggerStatement()
case ast.KindAtToken:
return p.parseDeclaration()
case ast.KindAsyncKeyword, ast.KindInterfaceKeyword, ast.KindTypeKeyword, ast.KindModuleKeyword, ast.KindNamespaceKeyword,
ast.KindDeclareKeyword, ast.KindConstKeyword, ast.KindEnumKeyword, ast.KindExportKeyword, ast.KindImportKeyword,
ast.KindPrivateKeyword, ast.KindProtectedKeyword, ast.KindPublicKeyword, ast.KindAbstractKeyword, ast.KindAccessorKeyword,
ast.KindStaticKeyword, ast.KindReadonlyKeyword, ast.KindGlobalKeyword:
if p.isStartOfDeclaration() {
return p.parseDeclaration()
}
}
return p.parseExpressionOrLabeledStatement()
}
func (p *Parser) parseDeclaration() *ast.Statement {
// `parseListElement` attempted to get the reused node at this position,
// but the ambient context flag was not yet set, so the node appeared
// not reusable in that context.
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
modifiers := p.parseModifiersEx( /*allowDecorators*/ true, false /*permitConstAsModifier*/, false /*stopOnStartOfClassStaticBlock*/)
isAmbient := modifiers != nil && core.Some(modifiers.Nodes, isDeclareModifier)
if isAmbient {
// !!! incremental parsing
// node := p.tryReuseAmbientDeclaration(pos)
// if node {
// return node
// }
for _, m := range modifiers.Nodes {
m.Flags |= ast.NodeFlagsAmbient
}
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsAmbient, true)
result := p.parseDeclarationWorker(pos, hasJSDoc, modifiers)
p.contextFlags = saveContextFlags
return result
} else {
return p.parseDeclarationWorker(pos, hasJSDoc, modifiers)
}
}
func (p *Parser) parseDeclarationWorker(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Statement {
switch p.token {
case ast.KindVarKeyword, ast.KindLetKeyword, ast.KindConstKeyword, ast.KindUsingKeyword, ast.KindAwaitKeyword:
return p.parseVariableStatement(pos, hasJSDoc, modifiers)
case ast.KindFunctionKeyword:
return p.parseFunctionDeclaration(pos, hasJSDoc, modifiers)
case ast.KindClassKeyword:
return p.parseClassDeclaration(pos, hasJSDoc, modifiers)
case ast.KindInterfaceKeyword:
return p.parseInterfaceDeclaration(pos, hasJSDoc, modifiers)
case ast.KindTypeKeyword:
return p.parseTypeAliasDeclaration(pos, hasJSDoc, modifiers)
case ast.KindEnumKeyword:
return p.parseEnumDeclaration(pos, hasJSDoc, modifiers)
case ast.KindGlobalKeyword, ast.KindModuleKeyword, ast.KindNamespaceKeyword:
return p.parseModuleDeclaration(pos, hasJSDoc, modifiers)
case ast.KindImportKeyword:
return p.parseImportDeclarationOrImportEqualsDeclaration(pos, hasJSDoc, modifiers)
case ast.KindExportKeyword:
p.nextToken()
switch p.token {
case ast.KindDefaultKeyword, ast.KindEqualsToken:
return p.parseExportAssignment(pos, hasJSDoc, modifiers)
case ast.KindAsKeyword:
return p.parseNamespaceExportDeclaration(pos, hasJSDoc, modifiers)
default:
return p.parseExportDeclaration(pos, hasJSDoc, modifiers)
}
}
if modifiers != nil {
// We reached this point because we encountered decorators and/or modifiers and assumed a declaration
// would follow. For recovery and error reporting purposes, return an incomplete declaration.
p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.Declaration_expected)
return p.finishNode(p.factory.NewMissingDeclaration(modifiers), pos)
}
panic("Unhandled case in parseDeclarationWorker")
}
func isDeclareModifier(modifier *ast.Node) bool {
return modifier.Kind == ast.KindDeclareKeyword
}
func (p *Parser) isLetDeclaration() bool {
// In ES6 'let' always starts a lexical declaration if followed by an identifier or {
// or [.
return p.lookAhead((*Parser).nextTokenIsBindingIdentifierOrStartOfDestructuring)
}
func (p *Parser) nextTokenIsBindingIdentifierOrStartOfDestructuring() bool {
p.nextToken()
return p.isBindingIdentifier() || p.token == ast.KindOpenBraceToken || p.token == ast.KindOpenBracketToken
}
func (p *Parser) parseBlock(ignoreMissingOpenBrace bool, diagnosticMessage *diagnostics.Message) *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
openBracePosition := p.scanner.TokenStart()
openBraceParsed := p.parseExpectedWithDiagnostic(ast.KindOpenBraceToken, diagnosticMessage, true /*shouldAdvance*/)
multiline := false
if openBraceParsed || ignoreMissingOpenBrace {
multiline = p.hasPrecedingLineBreak()
statements := p.parseList(PCBlockStatements, (*Parser).parseStatement)
p.parseExpectedMatchingBrackets(ast.KindOpenBraceToken, ast.KindCloseBraceToken, openBraceParsed, openBracePosition)
result := p.finishNode(p.factory.NewBlock(statements, multiline), pos)
p.withJSDoc(result, hasJSDoc)
if p.token == ast.KindEqualsToken {
p.parseErrorAtCurrentToken(diagnostics.Declaration_or_statement_expected_This_follows_a_block_of_statements_so_if_you_intended_to_write_a_destructuring_assignment_you_might_need_to_wrap_the_whole_assignment_in_parentheses)
p.nextToken()
}
return result
}
result := p.finishNode(p.factory.NewBlock(p.parseEmptyNodeList(), multiline), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseEmptyStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindSemicolonToken)
result := p.finishNode(p.factory.NewEmptyStatement(), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseIfStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindIfKeyword)
openParenPosition := p.scanner.TokenStart()
openParenParsed := p.parseExpected(ast.KindOpenParenToken)
expression := p.parseExpressionAllowIn()
p.parseExpectedMatchingBrackets(ast.KindOpenParenToken, ast.KindCloseParenToken, openParenParsed, openParenPosition)
thenStatement := p.parseStatement()
var elseStatement *ast.Statement
if p.parseOptional(ast.KindElseKeyword) {
elseStatement = p.parseStatement()
}
result := p.finishNode(p.factory.NewIfStatement(expression, thenStatement, elseStatement), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseDoStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindDoKeyword)
statement := p.parseStatement()
p.parseExpected(ast.KindWhileKeyword)
openParenPosition := p.scanner.TokenStart()
openParenParsed := p.parseExpected(ast.KindOpenParenToken)
expression := p.parseExpressionAllowIn()
p.parseExpectedMatchingBrackets(ast.KindOpenParenToken, ast.KindCloseParenToken, openParenParsed, openParenPosition)
// From: https://mail.mozilla.org/pipermail/es-discuss/2011-August/016188.html
// 157 min --- All allen at wirfs-brock.com CONF --- "do{;}while(false)false" prohibited in
// spec but allowed in consensus reality. Approved -- this is the de-facto standard whereby
// do;while(0)x will have a semicolon inserted before x.
p.parseOptional(ast.KindSemicolonToken)
result := p.finishNode(p.factory.NewDoStatement(statement, expression), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseWhileStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindWhileKeyword)
openParenPosition := p.scanner.TokenStart()
openParenParsed := p.parseExpected(ast.KindOpenParenToken)
expression := p.parseExpressionAllowIn()
p.parseExpectedMatchingBrackets(ast.KindOpenParenToken, ast.KindCloseParenToken, openParenParsed, openParenPosition)
statement := p.parseStatement()
result := p.finishNode(p.factory.NewWhileStatement(expression, statement), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseForOrForInOrForOfStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindForKeyword)
awaitToken := p.parseOptionalToken(ast.KindAwaitKeyword)
p.parseExpected(ast.KindOpenParenToken)
var initializer *ast.ForInitializer
if p.token != ast.KindSemicolonToken {
if p.token == ast.KindVarKeyword || p.token == ast.KindLetKeyword || p.token == ast.KindConstKeyword ||
p.token == ast.KindUsingKeyword && p.lookAhead((*Parser).nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLineDisallowOf) ||
// this one is meant to allow of
p.token == ast.KindAwaitKeyword && p.lookAhead((*Parser).nextIsUsingKeywordThenBindingIdentifierOrStartOfObjectDestructuringOnSameLine) {
initializer = p.parseVariableDeclarationList(true /*inForStatementInitializer*/)
} else {
initializer = doInContext(p, ast.NodeFlagsDisallowInContext, true, (*Parser).parseExpression)
}
}
var result *ast.Statement
switch {
case awaitToken != nil && p.parseExpected(ast.KindOfKeyword) || awaitToken == nil && p.parseOptional(ast.KindOfKeyword):
expression := doInContext(p, ast.NodeFlagsDisallowInContext, false, (*Parser).parseAssignmentExpressionOrHigher)
p.parseExpected(ast.KindCloseParenToken)
result = p.factory.NewForInOrOfStatement(ast.KindForOfStatement, awaitToken, initializer, expression, p.parseStatement())
case p.parseOptional(ast.KindInKeyword):
expression := p.parseExpressionAllowIn()
p.parseExpected(ast.KindCloseParenToken)
result = p.factory.NewForInOrOfStatement(ast.KindForInStatement, nil /*awaitToken*/, initializer, expression, p.parseStatement())
default:
p.parseExpected(ast.KindSemicolonToken)
var condition *ast.Expression
if p.token != ast.KindSemicolonToken && p.token != ast.KindCloseParenToken {
condition = p.parseExpressionAllowIn()
}
p.parseExpected(ast.KindSemicolonToken)
var incrementor *ast.Expression
if p.token != ast.KindCloseParenToken {
incrementor = p.parseExpressionAllowIn()
}
p.parseExpected(ast.KindCloseParenToken)
result = p.factory.NewForStatement(initializer, condition, incrementor, p.parseStatement())
}
p.finishNode(result, pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseBreakStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindBreakKeyword)
label := p.parseIdentifierUnlessAtSemicolon()
p.parseSemicolon()
result := p.finishNode(p.factory.NewBreakStatement(label), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseContinueStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindContinueKeyword)
label := p.parseIdentifierUnlessAtSemicolon()
p.parseSemicolon()
result := p.finishNode(p.factory.NewContinueStatement(label), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseIdentifierUnlessAtSemicolon() *ast.Node {
if !p.canParseSemicolon() {
return p.parseIdentifier()
}
return nil
}
func (p *Parser) parseReturnStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindReturnKeyword)
var expression *ast.Expression
if !p.canParseSemicolon() {
expression = p.parseExpressionAllowIn()
}
p.parseSemicolon()
result := p.finishNode(p.factory.NewReturnStatement(expression), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseWithStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindWithKeyword)
openParenPosition := p.scanner.TokenStart()
openParenParsed := p.parseExpected(ast.KindOpenParenToken)
expression := p.parseExpressionAllowIn()
p.parseExpectedMatchingBrackets(ast.KindOpenParenToken, ast.KindCloseParenToken, openParenParsed, openParenPosition)
statement := doInContext(p, ast.NodeFlagsInWithStatement, true, (*Parser).parseStatement)
result := p.finishNode(p.factory.NewWithStatement(expression, statement), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseCaseClause() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindCaseKeyword)
expression := p.parseExpressionAllowIn()
p.parseExpected(ast.KindColonToken)
statements := p.parseList(PCSwitchClauseStatements, (*Parser).parseStatement)
result := p.finishNode(p.factory.NewCaseOrDefaultClause(ast.KindCaseClause, expression, statements), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseDefaultClause() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindDefaultKeyword)
p.parseExpected(ast.KindColonToken)
statements := p.parseList(PCSwitchClauseStatements, (*Parser).parseStatement)
result := p.finishNode(p.factory.NewCaseOrDefaultClause(ast.KindDefaultClause, nil /*expression*/, statements), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseCaseOrDefaultClause() *ast.Node {
if p.token == ast.KindCaseKeyword {
return p.parseCaseClause()
}
return p.parseDefaultClause()
}
func (p *Parser) parseCaseBlock() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindOpenBraceToken)
clauses := p.parseList(PCSwitchClauses, (*Parser).parseCaseOrDefaultClause)
p.parseExpected(ast.KindCloseBraceToken)
result := p.finishNode(p.factory.NewCaseBlock(clauses), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseSwitchStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindSwitchKeyword)
p.parseExpected(ast.KindOpenParenToken)
expression := p.parseExpressionAllowIn()
p.parseExpected(ast.KindCloseParenToken)
caseBlock := p.parseCaseBlock()
result := p.finishNode(p.factory.NewSwitchStatement(expression, caseBlock), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseThrowStatement() *ast.Node {
// ThrowStatement[Yield] :
// throw [no LineTerminator here]Expression[In, ?Yield];
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindThrowKeyword)
// Because of automatic semicolon insertion, we need to report error if this
// throw could be terminated with a semicolon. Note: we can't call 'parseExpression'
// directly as that might consume an expression on the following line.
// Instead, we create a "missing" identifier, but don't report an error. The actual error
// will be reported in the grammar walker.
var expression *ast.Expression
if !p.hasPrecedingLineBreak() {
expression = p.parseExpressionAllowIn()
} else {
expression = p.createMissingIdentifier()
}
if !p.tryParseSemicolon() {
p.parseErrorForMissingSemicolonAfter(expression)
}
result := p.finishNode(p.factory.NewThrowStatement(expression), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
// TODO: Review for error recovery
func (p *Parser) parseTryStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindTryKeyword)
tryBlock := p.parseBlock(false /*ignoreMissingOpenBrace*/, nil)
var catchClause *ast.Node
if p.token == ast.KindCatchKeyword {
catchClause = p.parseCatchClause()
}
// If we don't have a catch clause, then we must have a finally clause. Try to parse
// one out no matter what.
var finallyBlock *ast.Node
if catchClause == nil || p.token == ast.KindFinallyKeyword {
p.parseExpectedWithDiagnostic(ast.KindFinallyKeyword, diagnostics.X_catch_or_finally_expected, true /*shouldAdvance*/)
finallyBlock = p.parseBlock(false /*ignoreMissingOpenBrace*/, nil)
}
result := p.finishNode(p.factory.NewTryStatement(tryBlock, catchClause, finallyBlock), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseCatchClause() *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindCatchKeyword)
var variableDeclaration *ast.Node
if p.parseOptional(ast.KindOpenParenToken) {
variableDeclaration = p.parseVariableDeclaration()
p.parseExpected(ast.KindCloseParenToken)
}
block := p.parseBlock(false /*ignoreMissingOpenBrace*/, nil)
result := p.finishNode(p.factory.NewCatchClause(variableDeclaration, block), pos)
return result
}
func (p *Parser) parseDebuggerStatement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindDebuggerKeyword)
p.parseSemicolon()
result := p.finishNode(p.factory.NewDebuggerStatement(), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseExpressionOrLabeledStatement() *ast.Statement {
// Avoiding having to do the lookahead for a labeled statement by just trying to parse
// out an expression, seeing if it is identifier and then seeing if it is followed by
// a colon.
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
hasParen := p.token == ast.KindOpenParenToken
expression := p.parseExpression()
if expression.Kind == ast.KindIdentifier && p.parseOptional(ast.KindColonToken) {
result := p.finishNode(p.factory.NewLabeledStatement(expression, p.parseStatement()), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
if !p.tryParseSemicolon() {
p.parseErrorForMissingSemicolonAfter(expression)
}
result := p.finishNode(p.factory.NewExpressionStatement(expression), pos)
jsdoc := p.withJSDoc(result, hasJSDoc && !hasParen)
p.reparseCommonJS(result, jsdoc)
return result
}
func (p *Parser) parseVariableStatement(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
declarationList := p.parseVariableDeclarationList(false /*inForStatementInitializer*/)
p.parseSemicolon()
result := p.finishNode(p.factory.NewVariableStatement(modifiers, declarationList), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func (p *Parser) parseVariableDeclarationList(inForStatementInitializer bool) *ast.Node {
pos := p.nodePos()
var flags ast.NodeFlags
switch p.token {
case ast.KindVarKeyword:
flags = ast.NodeFlagsNone
case ast.KindLetKeyword:
flags = ast.NodeFlagsLet
case ast.KindConstKeyword:
flags = ast.NodeFlagsConst
case ast.KindUsingKeyword:
flags = ast.NodeFlagsUsing
case ast.KindAwaitKeyword:
debug.Assert(p.isAwaitUsingDeclaration())
flags = ast.NodeFlagsAwaitUsing
p.nextToken()
default:
panic("Unhandled case in parseVariableDeclarationList")
}
p.nextToken()
// The user may have written the following:
//
// for (let of X) { }
//
// In this case, we want to parse an empty declaration list, and then parse 'of'
// as a keyword. The reason this is not automatic is that 'of' is a valid identifier.
// So we need to look ahead to determine if 'of' should be treated as a keyword in
// this context.
// The checker will then give an error that there is an empty declaration list.
var declarations *ast.NodeList
if p.token == ast.KindOfKeyword && p.lookAhead((*Parser).nextIsIdentifierAndCloseParen) {
declarations = p.parseEmptyNodeList()
} else {
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsDisallowInContext, inForStatementInitializer)
declarations = p.parseDelimitedList(PCVariableDeclarations, core.IfElse(inForStatementInitializer, (*Parser).parseVariableDeclaration, (*Parser).parseVariableDeclarationAllowExclamation))
p.contextFlags = saveContextFlags
}
result := p.finishNode(p.factory.NewVariableDeclarationList(flags, declarations), pos)
return result
}
func (p *Parser) nextIsIdentifierAndCloseParen() bool {
return p.nextTokenIsIdentifier() && p.nextToken() == ast.KindCloseParenToken
}
func (p *Parser) nextTokenIsIdentifier() bool {
p.nextToken()
return p.isIdentifier()
}
func (p *Parser) parseVariableDeclaration() *ast.Node {
return p.parseVariableDeclarationWorker(false /*allowExclamation*/)
}
func (p *Parser) parseVariableDeclarationAllowExclamation() *ast.Node {
return p.parseVariableDeclarationWorker(true /*allowExclamation*/)
}
func (p *Parser) parseVariableDeclarationWorker(allowExclamation bool) *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
name := p.parseIdentifierOrPatternWithDiagnostic(diagnostics.Private_identifiers_are_not_allowed_in_variable_declarations)
var exclamationToken *ast.Node
if allowExclamation && name.Kind == ast.KindIdentifier && p.token == ast.KindExclamationToken && !p.hasPrecedingLineBreak() {
exclamationToken = p.parseTokenNode()
}
typeNode := p.parseTypeAnnotation()
var initializer *ast.Expression
if p.token != ast.KindInKeyword && p.token != ast.KindOfKeyword {
initializer = p.parseInitializer()
}
result := p.finishNode(p.factory.NewVariableDeclaration(name, exclamationToken, typeNode, initializer), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func (p *Parser) parseIdentifierOrPattern() *ast.Node {
return p.parseIdentifierOrPatternWithDiagnostic(nil)
}
func (p *Parser) parseIdentifierOrPatternWithDiagnostic(privateIdentifierDiagnosticMessage *diagnostics.Message) *ast.Node {
if p.token == ast.KindOpenBracketToken {
return p.parseArrayBindingPattern()
}
if p.token == ast.KindOpenBraceToken {
return p.parseObjectBindingPattern()
}
return p.parseBindingIdentifierWithDiagnostic(privateIdentifierDiagnosticMessage)
}
func (p *Parser) parseArrayBindingPattern() *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindOpenBracketToken)
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsDisallowInContext, false)
elements := p.parseDelimitedList(PCArrayBindingElements, (*Parser).parseArrayBindingElement)
p.contextFlags = saveContextFlags
p.parseExpected(ast.KindCloseBracketToken)
return p.finishNode(p.factory.NewBindingPattern(ast.KindArrayBindingPattern, elements), pos)
}
func (p *Parser) parseArrayBindingElement() *ast.Node {
pos := p.nodePos()
var dotDotDotToken *ast.Node
var name *ast.Node
var initializer *ast.Expression
if p.token != ast.KindCommaToken {
// These are all nil for a missing element
dotDotDotToken = p.parseOptionalToken(ast.KindDotDotDotToken)
name = p.parseIdentifierOrPattern()
initializer = p.parseInitializer()
}
return p.finishNode(p.factory.NewBindingElement(dotDotDotToken, nil /*propertyName*/, name, initializer), pos)
}
func (p *Parser) parseObjectBindingPattern() *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindOpenBraceToken)
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsDisallowInContext, false)
elements := p.parseDelimitedList(PCObjectBindingElements, (*Parser).parseObjectBindingElement)
p.contextFlags = saveContextFlags
p.parseExpected(ast.KindCloseBraceToken)
return p.finishNode(p.factory.NewBindingPattern(ast.KindObjectBindingPattern, elements), pos)
}
func (p *Parser) parseObjectBindingElement() *ast.Node {
pos := p.nodePos()
dotDotDotToken := p.parseOptionalToken(ast.KindDotDotDotToken)
tokenIsIdentifier := p.isBindingIdentifier()
propertyName := p.parsePropertyName()
var name *ast.Node
if tokenIsIdentifier && p.token != ast.KindColonToken {
name = propertyName
propertyName = nil
} else {
p.parseExpected(ast.KindColonToken)
name = p.parseIdentifierOrPattern()
}
initializer := p.parseInitializer()
return p.finishNode(p.factory.NewBindingElement(dotDotDotToken, propertyName, name, initializer), pos)
}
func (p *Parser) parseInitializer() *ast.Expression {
if p.parseOptional(ast.KindEqualsToken) {
return p.parseAssignmentExpressionOrHigher()
}
return nil
}
func (p *Parser) parseTypeAnnotation() *ast.TypeNode {
if p.parseOptional(ast.KindColonToken) {
return p.parseType()
}
return nil
}
func (p *Parser) parseFunctionDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
p.parseExpected(ast.KindFunctionKeyword)
asteriskToken := p.parseOptionalToken(ast.KindAsteriskToken)
// We don't parse the name here in await context, instead we will report a grammar error in the checker.
var name *ast.Node
if modifiers == nil || modifiers.ModifierFlags&ast.ModifierFlagsDefault == 0 || p.isBindingIdentifier() {
name = p.parseBindingIdentifier()
}
signatureFlags := core.IfElse(asteriskToken != nil, ParseFlagsYield, ParseFlagsNone) | core.IfElse(modifiers != nil && modifiers.ModifierFlags&ast.ModifierFlagsAsync != 0, ParseFlagsAwait, ParseFlagsNone)
typeParameters := p.parseTypeParameters()
saveContextFlags := p.contextFlags
if modifiers != nil && modifiers.ModifierFlags&ast.ModifierFlagsExport != 0 {
p.setContextFlags(ast.NodeFlagsAwaitContext, true)
}
parameters := p.parseParameters(signatureFlags)
returnType := p.parseReturnType(ast.KindColonToken, false /*isType*/)
body := p.parseFunctionBlockOrSemicolon(signatureFlags, diagnostics.X_or_expected)
p.contextFlags = saveContextFlags
result := p.finishNode(p.factory.NewFunctionDeclaration(modifiers, asteriskToken, name, typeParameters, parameters, returnType, nil /*fullSignature*/, body), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func (p *Parser) parseClassDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
return p.parseClassDeclarationOrExpression(pos, hasJSDoc, modifiers, ast.KindClassDeclaration)
}
func (p *Parser) parseClassExpression() *ast.Node {
return p.parseClassDeclarationOrExpression(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/, ast.KindClassExpression)
}
func (p *Parser) parseClassDeclarationOrExpression(pos int, hasJSDoc bool, modifiers *ast.ModifierList, kind ast.Kind) *ast.Node {
saveContextFlags := p.contextFlags
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
p.parseExpected(ast.KindClassKeyword)
// We don't parse the name here in await context, instead we will report a grammar error in the checker.
name := p.parseNameOfClassDeclarationOrExpression()
typeParameters := p.parseTypeParameters()
if modifiers != nil && core.Some(modifiers.Nodes, isExportModifier) {
p.setContextFlags(ast.NodeFlagsAwaitContext, true /*value*/)
}
heritageClauses := p.parseHeritageClauses()
var members *ast.NodeList
if p.parseExpected(ast.KindOpenBraceToken) {
// ClassTail[Yield,Await] : (Modified) See 14.5
// ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt }
members = p.parseList(PCClassMembers, (*Parser).parseClassElement)
p.parseExpected(ast.KindCloseBraceToken)
} else {
members = p.parseEmptyNodeList()
}
p.contextFlags = saveContextFlags
var result *ast.Node
if modifiers != nil && ast.ModifiersToFlags(modifiers.Nodes)&ast.ModifierFlagsAmbient != 0 {
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
}
if kind == ast.KindClassDeclaration {
result = p.factory.NewClassDeclaration(modifiers, name, typeParameters, heritageClauses, members)
} else {
result = p.factory.NewClassExpression(modifiers, name, typeParameters, heritageClauses, members)
}
p.finishNode(result, pos)
p.withJSDoc(result, hasJSDoc)
if result.Flags&ast.NodeFlagsJavaScriptFile != 0 {
p.checkJSSyntax(result)
if heritageClauses != nil {
for _, clause := range heritageClauses.Nodes {
if clause.AsHeritageClause().Token == ast.KindExtendsKeyword {
for _, expr := range clause.AsHeritageClause().Types.Nodes {
p.checkJSSyntax(expr)
}
}
}
}
}
return result
}
func (p *Parser) parseNameOfClassDeclarationOrExpression() *ast.Node {
// implements is a future reserved word so
// 'class implements' might mean either
// - class expression with omitted name, 'implements' starts heritage clause
// - class with name 'implements'
// 'isImplementsClause' helps to disambiguate between these two cases
if p.isBindingIdentifier() && !p.isImplementsClause() {
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
id := p.createIdentifier(p.isBindingIdentifier())
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
return id
}
return nil
}
func (p *Parser) isImplementsClause() bool {
return p.token == ast.KindImplementsKeyword && p.lookAhead((*Parser).nextTokenIsIdentifierOrKeyword)
}
func isExportModifier(modifier *ast.Node) bool {
return modifier.Kind == ast.KindExportKeyword
}
func isAsyncModifier(modifier *ast.Node) bool {
return modifier.Kind == ast.KindAsyncKeyword
}
func (p *Parser) parseHeritageClauses() *ast.NodeList {
// ClassTail[Yield,Await] : (Modified) See 14.5
// ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt }
if p.isHeritageClause() {
return p.parseList(PCHeritageClauses, (*Parser).parseHeritageClause)
}
return nil
}
func (p *Parser) parseHeritageClause() *ast.Node {
pos := p.nodePos()
kind := p.token
p.nextToken()
types := p.parseDelimitedList(PCHeritageClauseElement, (*Parser).parseExpressionWithTypeArguments)
return p.checkJSSyntax(p.finishNode(p.factory.NewHeritageClause(kind, types), pos))
}
func (p *Parser) parseExpressionWithTypeArguments() *ast.Node {
pos := p.nodePos()
expression := p.parseLeftHandSideExpressionOrHigher()
if ast.IsExpressionWithTypeArguments(expression) {
return expression
}
typeArguments := p.parseTypeArguments()
return p.finishNode(p.factory.NewExpressionWithTypeArguments(expression, typeArguments), pos)
}
func (p *Parser) parseClassElement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
if p.token == ast.KindSemicolonToken {
p.nextToken()
result := p.finishNode(p.factory.NewSemicolonClassElement(), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
modifiers := p.parseModifiersEx(true /*allowDecorators*/, true /*permitConstAsModifier*/, true /*stopOnStartOfClassStaticBlock*/)
if p.token == ast.KindStaticKeyword && p.lookAhead((*Parser).nextTokenIsOpenBrace) {
return p.parseClassStaticBlockDeclaration(pos, hasJSDoc, modifiers)
}
if p.parseContextualModifier(ast.KindGetKeyword) {
return p.parseAccessorDeclaration(pos, hasJSDoc, modifiers, ast.KindGetAccessor, ParseFlagsNone)
}
if p.parseContextualModifier(ast.KindSetKeyword) {
return p.parseAccessorDeclaration(pos, hasJSDoc, modifiers, ast.KindSetAccessor, ParseFlagsNone)
}
if p.token == ast.KindConstructorKeyword || p.token == ast.KindStringLiteral {
constructorDeclaration := p.tryParseConstructorDeclaration(pos, hasJSDoc, modifiers)
if constructorDeclaration != nil {
return constructorDeclaration
}
}
if p.isIndexSignature() {
return p.checkJSSyntax(p.parseIndexSignatureDeclaration(pos, hasJSDoc, modifiers))
}
// It is very important that we check this *after* checking indexers because
// the [ token can start an index signature or a computed property name
if tokenIsIdentifierOrKeyword(p.token) || p.token == ast.KindStringLiteral || p.token == ast.KindNumericLiteral || p.token == ast.KindBigIntLiteral || p.token == ast.KindAsteriskToken || p.token == ast.KindOpenBracketToken {
isAmbient := modifiers != nil && core.Some(modifiers.Nodes, isDeclareModifier)
if isAmbient {
for _, m := range modifiers.Nodes {
m.Flags |= ast.NodeFlagsAmbient
}
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsAmbient, true)
result := p.parsePropertyOrMethodDeclaration(pos, hasJSDoc, modifiers)
p.contextFlags = saveContextFlags
return result
} else {
return p.parsePropertyOrMethodDeclaration(pos, hasJSDoc, modifiers)
}
}
if modifiers != nil {
// treat this as a property declaration with a missing name.
p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.Declaration_expected)
name := p.createMissingIdentifier()
return p.parsePropertyDeclaration(pos, hasJSDoc, modifiers, name, nil /*questionToken*/)
}
// 'isClassMemberStart' should have hinted not to attempt parsing.
panic("Should not have attempted to parse class member declaration.")
}
func (p *Parser) parseClassStaticBlockDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
p.parseExpectedToken(ast.KindStaticKeyword)
body := p.parseClassStaticBlockBody()
result := p.finishNode(p.factory.NewClassStaticBlockDeclaration(modifiers, body), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseClassStaticBlockBody() *ast.Node {
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsYieldContext, false)
p.setContextFlags(ast.NodeFlagsAwaitContext, true)
body := p.parseBlock(false /*ignoreMissingOpenBrace*/, nil /*diagnosticMessage*/)
p.contextFlags = saveContextFlags
return body
}
func (p *Parser) tryParseConstructorDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
state := p.mark()
if p.token == ast.KindConstructorKeyword || p.token == ast.KindStringLiteral && p.scanner.TokenValue() == "constructor" && p.lookAhead((*Parser).nextTokenIsOpenParen) {
p.nextToken()
typeParameters := p.parseTypeParameters()
parameters := p.parseParameters(ParseFlagsNone)
returnType := p.parseReturnType(ast.KindColonToken, false /*isType*/)
body := p.parseFunctionBlockOrSemicolon(ParseFlagsNone, diagnostics.X_or_expected)
result := p.finishNode(p.factory.NewConstructorDeclaration(modifiers, typeParameters, parameters, returnType, nil /*fullSignature*/, body), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
p.rewind(state)
return nil
}
func (p *Parser) nextTokenIsOpenParen() bool {
return p.nextToken() == ast.KindOpenParenToken
}
func (p *Parser) parsePropertyOrMethodDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
asteriskToken := p.parseOptionalToken(ast.KindAsteriskToken)
name := p.parsePropertyName()
// Note: this is not legal as per the grammar. But we allow it in the parser and
// report an error in the grammar checker.
questionToken := p.parseOptionalToken(ast.KindQuestionToken)
if asteriskToken != nil || p.token == ast.KindOpenParenToken || p.token == ast.KindLessThanToken {
return p.parseMethodDeclaration(pos, hasJSDoc, modifiers, asteriskToken, name, questionToken, diagnostics.X_or_expected)
}
return p.parsePropertyDeclaration(pos, hasJSDoc, modifiers, name, questionToken)
}
func (p *Parser) parseMethodDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList, asteriskToken *ast.Node, name *ast.Node, questionToken *ast.Node, diagnosticMessage *diagnostics.Message) *ast.Node {
signatureFlags := core.IfElse(asteriskToken != nil, ParseFlagsYield, ParseFlagsNone) | core.IfElse(modifierListHasAsync(modifiers), ParseFlagsAwait, ParseFlagsNone)
typeParameters := p.parseTypeParameters()
parameters := p.parseParameters(signatureFlags)
typeNode := p.parseReturnType(ast.KindColonToken, false /*isType*/)
body := p.parseFunctionBlockOrSemicolon(signatureFlags, diagnosticMessage)
result := p.finishNode(p.factory.NewMethodDeclaration(modifiers, asteriskToken, name, questionToken, typeParameters, parameters, typeNode, nil /*fullSignature*/, body), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func modifierListHasAsync(modifiers *ast.ModifierList) bool {
return modifiers != nil && core.Some(modifiers.Nodes, isAsyncModifier)
}
func (p *Parser) parsePropertyDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList, name *ast.Node, questionToken *ast.Node) *ast.Node {
postfixToken := questionToken
if postfixToken == nil && !p.hasPrecedingLineBreak() {
postfixToken = p.parseOptionalToken(ast.KindExclamationToken)
}
typeNode := p.parseTypeAnnotation()
initializer := doInContext(p, ast.NodeFlagsYieldContext|ast.NodeFlagsAwaitContext|ast.NodeFlagsDisallowInContext, false, (*Parser).parseInitializer)
p.parseSemicolonAfterPropertyName(name, typeNode, initializer)
result := p.finishNode(p.factory.NewPropertyDeclaration(modifiers, name, postfixToken, typeNode, initializer), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func (p *Parser) parseSemicolonAfterPropertyName(name *ast.Node, typeNode *ast.TypeNode, initializer *ast.Expression) {
if p.token == ast.KindAtToken && !p.hasPrecedingLineBreak() {
p.parseErrorAtCurrentToken(diagnostics.Decorators_must_precede_the_name_and_all_keywords_of_property_declarations)
return
}
if p.token == ast.KindOpenParenToken {
p.parseErrorAtCurrentToken(diagnostics.Cannot_start_a_function_call_in_a_type_annotation)
p.nextToken()
return
}
if typeNode != nil && !p.canParseSemicolon() {
if initializer != nil {
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(ast.KindSemicolonToken))
} else {
p.parseErrorAtCurrentToken(diagnostics.Expected_for_property_initializer)
}
return
}
if p.tryParseSemicolon() {
return
}
if initializer != nil {
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(ast.KindSemicolonToken))
return
}
p.parseErrorForMissingSemicolonAfter(name)
}
func (p *Parser) parseErrorForMissingSemicolonAfter(node *ast.Node) {
// Tagged template literals are sometimes used in places where only simple strings are allowed, i.e.:
// module `M1` {
// ^^^^^^^^^^^ This block is parsed as a template literal like module`M1`.
if node.Kind == ast.KindTaggedTemplateExpression {
p.parseErrorAtRange(p.skipRangeTrivia(node.AsTaggedTemplateExpression().Template.Loc), diagnostics.Module_declaration_names_may_only_use_or_quoted_strings)
return
}
// Otherwise, if this isn't a well-known keyword-like identifier, give the generic fallback message.
var expressionText string
if node.Kind == ast.KindIdentifier {
expressionText = node.AsIdentifier().Text
}
if expressionText == "" {
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(ast.KindSemicolonToken))
return
}
pos := scanner.SkipTrivia(p.sourceText, node.Pos())
// Some known keywords are likely signs of syntax being used improperly.
switch expressionText {
case "const", "let", "var":
p.parseErrorAt(pos, node.End(), diagnostics.Variable_declaration_not_allowed_at_this_location)
return
case "declare":
// If a declared node failed to parse, it would have emitted a diagnostic already.
return
case "interface":
p.parseErrorForInvalidName(diagnostics.Interface_name_cannot_be_0, diagnostics.Interface_must_be_given_a_name, ast.KindOpenBraceToken)
return
case "is":
p.parseErrorAt(pos, p.scanner.TokenStart(), diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods)
return
case "module", "namespace":
p.parseErrorForInvalidName(diagnostics.Namespace_name_cannot_be_0, diagnostics.Namespace_must_be_given_a_name, ast.KindOpenBraceToken)
return
case "type":
p.parseErrorForInvalidName(diagnostics.Type_alias_name_cannot_be_0, diagnostics.Type_alias_must_be_given_a_name, ast.KindEqualsToken)
return
}
// The user alternatively might have misspelled or forgotten to add a space after a common keyword.
suggestion := core.GetSpellingSuggestion(expressionText, viableKeywordSuggestions, func(s string) string { return s })
if suggestion == "" {
suggestion = getSpaceSuggestion(expressionText)
}
if suggestion != "" {
p.parseErrorAt(pos, node.End(), diagnostics.Unknown_keyword_or_identifier_Did_you_mean_0, suggestion)
return
}
// Unknown tokens are handled with their own errors in the scanner
if p.token == ast.KindUnknown {
return
}
// Otherwise, we know this some kind of unknown word, not just a missing expected semicolon.
p.parseErrorAt(pos, node.End(), diagnostics.Unexpected_keyword_or_identifier)
}
func getSpaceSuggestion(expressionText string) string {
for _, keyword := range viableKeywordSuggestions {
if len(expressionText) > len(keyword)+2 && strings.HasPrefix(expressionText, keyword) {
return keyword + " " + expressionText[len(keyword):]
}
}
return ""
}
func (p *Parser) parseErrorForInvalidName(nameDiagnostic *diagnostics.Message, blankDiagnostic *diagnostics.Message, tokenIfBlankName ast.Kind) {
if p.token == tokenIfBlankName {
p.parseErrorAtCurrentToken(blankDiagnostic)
} else {
p.parseErrorAtCurrentToken(nameDiagnostic, p.scanner.TokenValue())
}
}
func (p *Parser) parseInterfaceDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
p.parseExpected(ast.KindInterfaceKeyword)
name := p.parseIdentifier()
typeParameters := p.parseTypeParameters()
heritageClauses := p.parseHeritageClauses()
members := p.parseObjectTypeMembers()
result := p.finishNode(p.factory.NewInterfaceDeclaration(modifiers, name, typeParameters, heritageClauses, members), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func (p *Parser) parseTypeAliasDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
p.parseExpected(ast.KindTypeKeyword)
if p.hasPrecedingLineBreak() {
p.parseErrorAtCurrentToken(diagnostics.Line_break_not_permitted_here)
}
name := p.parseIdentifier()
typeParameters := p.parseTypeParameters()
p.parseExpected(ast.KindEqualsToken)
var typeNode *ast.TypeNode
if p.token == ast.KindIntrinsicKeyword && p.lookAhead((*Parser).nextIsNotDot) {
typeNode = p.parseKeywordTypeNode()
} else {
typeNode = p.parseType()
}
p.parseSemicolon()
result := p.finishNode(p.factory.NewTypeAliasDeclaration(modifiers, name, typeParameters, typeNode), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func (p *Parser) nextIsNotDot() bool {
return p.nextToken() != ast.KindDotToken
}
// In an ambient declaration, the grammar only allows integer literals as initializers.
// In a non-ambient declaration, the grammar allows uninitialized members only in a
// ConstantEnumMemberSection, which starts at the beginning of an enum declaration
// or any time an integer literal initializer is encountered.
func (p *Parser) parseEnumMember() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
name := p.parsePropertyName()
initializer := doInContext(p, ast.NodeFlagsDisallowInContext, false, (*Parser).parseInitializer)
result := p.finishNode(p.factory.NewEnumMember(name, initializer), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseEnumDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
p.parseExpected(ast.KindEnumKeyword)
name := p.parseIdentifier()
var members *ast.NodeList
if p.parseExpected(ast.KindOpenBraceToken) {
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsYieldContext|ast.NodeFlagsAwaitContext, false)
members = p.parseDelimitedList(PCEnumMembers, (*Parser).parseEnumMember)
p.contextFlags = saveContextFlags
p.parseExpected(ast.KindCloseBraceToken)
} else {
members = p.parseEmptyNodeList()
}
result := p.finishNode(p.factory.NewEnumDeclaration(modifiers, name, members), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
return result
}
func (p *Parser) parseModuleDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Statement {
keyword := ast.KindModuleKeyword
if p.token == ast.KindGlobalKeyword {
// global augmentation
return p.parseAmbientExternalModuleDeclaration(pos, hasJSDoc, modifiers)
} else if p.parseOptional(ast.KindNamespaceKeyword) {
keyword = ast.KindNamespaceKeyword
} else {
p.parseExpected(ast.KindModuleKeyword)
if p.token == ast.KindStringLiteral {
return p.parseAmbientExternalModuleDeclaration(pos, hasJSDoc, modifiers)
}
}
return p.parseModuleOrNamespaceDeclaration(pos, hasJSDoc, modifiers, false /*nested*/, keyword)
}
func (p *Parser) parseAmbientExternalModuleDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
var name *ast.Node
keyword := ast.KindModuleKeyword
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
if p.token == ast.KindGlobalKeyword {
// parse 'global' as name of global scope augmentation
name = p.parseIdentifier()
keyword = ast.KindGlobalKeyword
} else {
// parse string literal
name = p.parseLiteralExpression(true /*intern*/)
}
var body *ast.Node
if p.token == ast.KindOpenBraceToken {
body = p.parseModuleBlock()
} else {
p.parseSemicolon()
}
result := p.finishNode(p.factory.NewModuleDeclaration(modifiers, keyword, name, body), pos)
p.withJSDoc(result, hasJSDoc)
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
return result
}
func (p *Parser) parseModuleBlock() *ast.Node {
pos := p.nodePos()
var statements *ast.NodeList
if p.parseExpected(ast.KindOpenBraceToken) {
statements = p.parseList(PCBlockStatements, (*Parser).parseStatement)
p.parseExpected(ast.KindCloseBraceToken)
} else {
statements = p.parseEmptyNodeList()
}
return p.finishNode(p.factory.NewModuleBlock(statements), pos)
}
func (p *Parser) parseModuleOrNamespaceDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList, nested bool, keyword ast.Kind) *ast.Node {
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
var name *ast.Node
if nested {
name = p.parseIdentifierName()
} else {
name = p.parseIdentifier()
}
var body *ast.Node
if p.parseOptional(ast.KindDotToken) {
implicitExport := p.factory.NewModifier(ast.KindExportKeyword)
implicitExport.Loc = core.NewTextRange(p.nodePos(), p.nodePos())
implicitExport.Flags = ast.NodeFlagsReparsed
implicitModifiers := p.newModifierList(implicitExport.Loc, p.nodeSlicePool.NewSlice1(implicitExport))
body = p.parseModuleOrNamespaceDeclaration(p.nodePos(), false /*hasJSDoc*/, implicitModifiers, true /*nested*/, keyword)
} else {
body = p.parseModuleBlock()
}
result := p.finishNode(p.factory.NewModuleDeclaration(modifiers, keyword, name, body), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
return result
}
func (p *Parser) parseImportDeclarationOrImportEqualsDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Statement {
p.parseExpected(ast.KindImportKeyword)
afterImportPos := p.nodePos()
// We don't parse the identifier here in await context, instead we will report a grammar error in the checker.
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
var identifier *ast.Node
if p.isIdentifier() {
identifier = p.parseIdentifier()
}
phaseModifier := ast.KindUnknown
if identifier != nil && identifier.AsIdentifier().Text == "type" &&
(p.token != ast.KindFromKeyword || p.isIdentifier() && p.lookAhead((*Parser).nextTokenIsFromKeywordOrEqualsToken)) &&
(p.isIdentifier() || p.tokenAfterImportDefinitelyProducesImportDeclaration()) {
phaseModifier = ast.KindTypeKeyword
identifier = nil
if p.isIdentifier() {
identifier = p.parseIdentifier()
}
} else if identifier != nil && identifier.AsIdentifier().Text == "defer" {
var shouldParseAsDeferModifier bool
if p.token == ast.KindFromKeyword {
shouldParseAsDeferModifier = !p.lookAhead((*Parser).nextTokenIsTokenStringLiteral)
} else {
shouldParseAsDeferModifier = p.token != ast.KindCommaToken && p.token != ast.KindEqualsToken
}
if shouldParseAsDeferModifier {
phaseModifier = ast.KindDeferKeyword
identifier = nil
if p.isIdentifier() {
identifier = p.parseIdentifier()
}
}
}
if identifier != nil && !p.tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration() && phaseModifier != ast.KindDeferKeyword {
importEquals := p.checkJSSyntax(p.parseImportEqualsDeclaration(pos, hasJSDoc, modifiers, identifier, phaseModifier == ast.KindTypeKeyword))
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier // Import= declaration is always parsed in an Await context, no need to reparse
return importEquals
}
importClause := p.tryParseImportClause(identifier, afterImportPos, phaseModifier, false /*skipJSDocLeadingAsterisks*/)
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier // import clause is always parsed in an Await context
moduleSpecifier := p.parseModuleSpecifier()
attributes := p.tryParseImportAttributes()
p.parseSemicolon()
result := p.finishNode(p.factory.NewImportDeclaration(modifiers, importClause, moduleSpecifier, attributes), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func (p *Parser) nextTokenIsFromKeywordOrEqualsToken() bool {
p.nextToken()
return p.token == ast.KindFromKeyword || p.token == ast.KindEqualsToken
}
func (p *Parser) tokenAfterImportDefinitelyProducesImportDeclaration() bool {
return p.token == ast.KindAsteriskToken || p.token == ast.KindOpenBraceToken
}
func (p *Parser) tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration() bool {
// In `import id ___`, the current token decides whether to produce
// an ImportDeclaration or ImportEqualsDeclaration.
return p.token == ast.KindCommaToken || p.token == ast.KindFromKeyword
}
func (p *Parser) parseImportEqualsDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList, identifier *ast.Node, isTypeOnly bool) *ast.Node {
p.parseExpected(ast.KindEqualsToken)
moduleReference := p.parseModuleReference()
p.parseSemicolon()
result := p.finishNode(p.factory.NewImportEqualsDeclaration(modifiers, isTypeOnly, identifier, moduleReference), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseModuleReference() *ast.Node {
if p.token == ast.KindRequireKeyword && p.lookAhead((*Parser).nextTokenIsOpenParen) {
return p.parseExternalModuleReference()
}
return p.parseEntityName(false /*allowReservedWords*/, nil /*diagnosticMessage*/)
}
func (p *Parser) parseExternalModuleReference() *ast.Node {
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
pos := p.nodePos()
p.parseExpected(ast.KindRequireKeyword)
p.parseExpected(ast.KindOpenParenToken)
expression := p.parseModuleSpecifier()
p.parseExpected(ast.KindCloseParenToken)
result := p.finishNode(p.factory.NewExternalModuleReference(expression), pos)
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
return result
}
func (p *Parser) parseModuleSpecifier() *ast.Expression {
if p.token == ast.KindStringLiteral {
result := p.parseLiteralExpression(true /*intern*/)
return result
}
// We allow arbitrary expressions here, even though the grammar only allows string
// literals. We check to ensure that it is only a string literal later in the grammar
// check pass.
return p.parseExpression()
}
func (p *Parser) tryParseImportClause(identifier *ast.Node, pos int, phaseModifier ast.Kind, skipJSDocLeadingAsterisks bool) *ast.Node {
// ImportDeclaration:
// import ImportClause from ModuleSpecifier ;
// import ModuleSpecifier;
if identifier != nil || p.token == ast.KindAsteriskToken || p.token == ast.KindOpenBraceToken {
importClause := p.parseImportClause(identifier, pos, phaseModifier, skipJSDocLeadingAsterisks)
p.parseExpected(ast.KindFromKeyword)
return importClause
}
return nil
}
func (p *Parser) parseImportClause(identifier *ast.Node, pos int, phaseModifier ast.Kind, skipJSDocLeadingAsterisks bool) *ast.Node {
// ImportClause:
// ImportedDefaultBinding
// NameSpaceImport
// NamedImports
// ImportedDefaultBinding, NameSpaceImport
// ImportedDefaultBinding, NamedImports
// If there was no default import or if there is comma token after default import
// parse namespace or named imports
var namedBindings *ast.Node
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
if identifier == nil || p.parseOptional(ast.KindCommaToken) {
if skipJSDocLeadingAsterisks {
p.scanner.SetSkipJSDocLeadingAsterisks(true)
}
if p.token == ast.KindAsteriskToken {
namedBindings = p.parseNamespaceImport()
} else {
namedBindings = p.parseNamedImports()
}
if skipJSDocLeadingAsterisks {
p.scanner.SetSkipJSDocLeadingAsterisks(false)
}
}
result := p.finishNode(p.factory.NewImportClause(phaseModifier, identifier, namedBindings), pos)
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
return result
}
func (p *Parser) parseNamespaceImport() *ast.Node {
// NameSpaceImport:
// * as ImportedBinding
pos := p.nodePos()
p.parseExpected(ast.KindAsteriskToken)
p.parseExpected(ast.KindAsKeyword)
name := p.parseIdentifier()
return p.finishNode(p.factory.NewNamespaceImport(name), pos)
}
func (p *Parser) parseNamedImports() *ast.Node {
pos := p.nodePos()
// NamedImports:
// { }
// { ImportsList }
// { ImportsList, }
imports := p.parseBracketedList(PCImportOrExportSpecifiers, (*Parser).parseImportSpecifier, ast.KindOpenBraceToken, ast.KindCloseBraceToken)
return p.finishNode(p.factory.NewNamedImports(imports), pos)
}
func (p *Parser) parseImportSpecifier() *ast.Node {
pos := p.nodePos()
isTypeOnly, propertyName, name := p.parseImportOrExportSpecifier(ast.KindImportSpecifier)
var identifierName *ast.Node
if name.Kind == ast.KindIdentifier {
identifierName = name
} else {
p.parseErrorAtRange(p.skipRangeTrivia(name.Loc), diagnostics.Identifier_expected)
identifierName = p.newIdentifier("")
p.finishNode(identifierName, name.Pos())
}
result := p.checkJSSyntax(p.finishNode(p.factory.NewImportSpecifier(isTypeOnly, propertyName, identifierName), pos))
return result
}
func (p *Parser) parseImportOrExportSpecifier(kind ast.Kind) (isTypeOnly bool, propertyName *ast.Node, name *ast.Node) {
// ImportSpecifier:
// BindingIdentifier
// ModuleExportName as BindingIdentifier
// ExportSpecifier:
// ModuleExportName
// ModuleExportName as ModuleExportName
// let checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier();
// let checkIdentifierStart = scanner.getTokenStart();
// let checkIdentifierEnd = scanner.getTokenEnd();
canParseAsKeyword := true
disallowKeywords := kind == ast.KindImportSpecifier
var nameOk bool
name, nameOk = p.parseModuleExportName(disallowKeywords)
if name.Kind == ast.KindIdentifier && name.AsIdentifier().Text == "type" {
// If the first token of an import specifier is 'type', there are a lot of possibilities,
// especially if we see 'as' afterwards:
//
// import { type } from "mod"; - isTypeOnly: false, name: type
// import { type as } from "mod"; - isTypeOnly: true, name: as
// import { type as as } from "mod"; - isTypeOnly: false, name: as, propertyName: type
// import { type as as as } from "mod"; - isTypeOnly: true, name: as, propertyName: as
if p.token == ast.KindAsKeyword {
// { type as ...? }
firstAs := p.parseIdentifierName()
if p.token == ast.KindAsKeyword {
// { type as as ...? }
secondAs := p.parseIdentifierName()
if p.canParseModuleExportName() {
// { type as as something }
// { type as as "something" }
isTypeOnly = true
propertyName = firstAs
name, nameOk = p.parseModuleExportName(disallowKeywords)
canParseAsKeyword = false
} else {
// { type as as }
propertyName = name
name = secondAs
canParseAsKeyword = false
}
} else if p.canParseModuleExportName() {
// { type as something }
// { type as "something" }
propertyName = name
canParseAsKeyword = false
name, nameOk = p.parseModuleExportName(disallowKeywords)
} else {
// { type as }
isTypeOnly = true
name = firstAs
}
} else if p.canParseModuleExportName() {
// { type something ...? }
// { type "something" ...? }
isTypeOnly = true
name, nameOk = p.parseModuleExportName(disallowKeywords)
}
}
if canParseAsKeyword && p.token == ast.KindAsKeyword {
propertyName = name
p.parseExpected(ast.KindAsKeyword)
name, nameOk = p.parseModuleExportName(disallowKeywords)
}
if !nameOk {
p.parseErrorAtRange(p.skipRangeTrivia(name.Loc), diagnostics.Identifier_expected)
}
return isTypeOnly, propertyName, name
}
func (p *Parser) canParseModuleExportName() bool {
return tokenIsIdentifierOrKeyword(p.token) || p.token == ast.KindStringLiteral
}
func (p *Parser) parseModuleExportName(disallowKeywords bool) (node *ast.Node, nameOk bool) {
nameOk = true
if p.token == ast.KindStringLiteral {
return p.parseLiteralExpression(false /*intern*/), nameOk
}
if disallowKeywords && ast.IsKeyword(p.token) && !p.isIdentifier() {
nameOk = false
}
return p.parseIdentifierName(), nameOk
}
func (p *Parser) tryParseImportAttributes() *ast.Node {
if (p.token == ast.KindWithKeyword || p.token == ast.KindAssertKeyword) && !p.hasPrecedingLineBreak() {
return p.parseImportAttributes(p.token, false /*skipKeyword*/)
}
return nil
}
func (p *Parser) parseExportAssignment(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
saveContextFlags := p.contextFlags
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
p.setContextFlags(ast.NodeFlagsAwaitContext, true)
isExportEquals := false
if p.parseOptional(ast.KindEqualsToken) {
isExportEquals = true
} else {
p.parseExpected(ast.KindDefaultKeyword)
}
expression := p.parseAssignmentExpressionOrHigher()
p.parseSemicolon()
p.contextFlags = saveContextFlags
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
result := p.finishNode(p.factory.NewExportAssignment(modifiers, isExportEquals, nil /*typeNode*/, expression), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func (p *Parser) parseNamespaceExportDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
p.parseExpected(ast.KindAsKeyword)
p.parseExpected(ast.KindNamespaceKeyword)
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
name := p.parseIdentifier()
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
p.parseSemicolon()
// NamespaceExportDeclaration nodes cannot have decorators or modifiers, we attach them here so we can report them in the grammar checker
result := p.finishNode(p.factory.NewNamespaceExportDeclaration(modifiers, name), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseExportDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
saveContextFlags := p.contextFlags
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
p.setContextFlags(ast.NodeFlagsAwaitContext, true)
var exportClause *ast.Node
var moduleSpecifier *ast.Expression
var attributes *ast.Node
isTypeOnly := p.parseOptional(ast.KindTypeKeyword)
namespaceExportPos := p.nodePos()
if p.parseOptional(ast.KindAsteriskToken) {
if p.parseOptional(ast.KindAsKeyword) {
exportClause = p.parseNamespaceExport(namespaceExportPos)
}
p.parseExpected(ast.KindFromKeyword)
moduleSpecifier = p.parseModuleSpecifier()
} else {
exportClause = p.parseNamedExports()
// It is not uncommon to accidentally omit the 'from' keyword. Additionally, in editing scenarios,
// the 'from' keyword can be parsed as a named export when the export clause is unterminated (i.e. `export { from "moduleName";`)
// If we don't have a 'from' keyword, see if we have a string literal such that ASI won't take effect.
if p.token == ast.KindFromKeyword || (p.token == ast.KindStringLiteral && !p.hasPrecedingLineBreak()) {
p.parseExpected(ast.KindFromKeyword)
moduleSpecifier = p.parseModuleSpecifier()
}
}
if moduleSpecifier != nil && (p.token == ast.KindWithKeyword || p.token == ast.KindAssertKeyword) && !p.hasPrecedingLineBreak() {
attributes = p.parseImportAttributes(p.token, false /*skipKeyword*/)
}
p.parseSemicolon()
p.contextFlags = saveContextFlags
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
result := p.finishNode(p.factory.NewExportDeclaration(modifiers, isTypeOnly, exportClause, moduleSpecifier, attributes), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func (p *Parser) parseNamespaceExport(pos int) *ast.Node {
exportName, _ := p.parseModuleExportName(false /*disallowKeywords*/)
return p.finishNode(p.factory.NewNamespaceExport(exportName), pos)
}
func (p *Parser) parseNamedExports() *ast.Node {
pos := p.nodePos()
// NamedImports:
// { }
// { ImportsList }
// { ImportsList, }
exports := p.parseBracketedList(PCImportOrExportSpecifiers, (*Parser).parseExportSpecifier, ast.KindOpenBraceToken, ast.KindCloseBraceToken)
return p.finishNode(p.factory.NewNamedExports(exports), pos)
}
func (p *Parser) parseExportSpecifier() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
isTypeOnly, propertyName, name := p.parseImportOrExportSpecifier(ast.KindExportSpecifier)
result := p.finishNode(p.factory.NewExportSpecifier(isTypeOnly, propertyName, name), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
// TYPES
func (p *Parser) parseType() *ast.TypeNode {
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsTypeExcludesFlags, false)
var typeNode *ast.TypeNode
if p.isStartOfFunctionTypeOrConstructorType() {
typeNode = p.parseFunctionOrConstructorType()
} else {
pos := p.nodePos()
typeNode = p.parseUnionTypeOrHigher()
if !p.inDisallowConditionalTypesContext() && !p.hasPrecedingLineBreak() && p.parseOptional(ast.KindExtendsKeyword) {
// The type following 'extends' is not permitted to be another conditional type
extendsType := doInContext(p, ast.NodeFlagsDisallowConditionalTypesContext, true, (*Parser).parseType)
p.parseExpected(ast.KindQuestionToken)
trueType := doInContext(p, ast.NodeFlagsDisallowConditionalTypesContext, false, (*Parser).parseType)
p.parseExpected(ast.KindColonToken)
falseType := doInContext(p, ast.NodeFlagsDisallowConditionalTypesContext, false, (*Parser).parseType)
conditionalType := p.factory.NewConditionalTypeNode(typeNode, extendsType, trueType, falseType)
p.finishNode(conditionalType, pos)
typeNode = conditionalType
}
}
p.contextFlags = saveContextFlags
return typeNode
}
func (p *Parser) parseUnionTypeOrHigher() *ast.TypeNode {
return p.parseUnionOrIntersectionType(ast.KindBarToken, (*Parser).parseIntersectionTypeOrHigher)
}
func (p *Parser) parseIntersectionTypeOrHigher() *ast.TypeNode {
return p.parseUnionOrIntersectionType(ast.KindAmpersandToken, (*Parser).parseTypeOperatorOrHigher)
}
func (p *Parser) parseUnionOrIntersectionType(operator ast.Kind, parseConstituentType func(p *Parser) *ast.TypeNode) *ast.TypeNode {
pos := p.nodePos()
isUnionType := operator == ast.KindBarToken
hasLeadingOperator := p.parseOptional(operator)
var typeNode *ast.TypeNode
if hasLeadingOperator {
typeNode = p.parseFunctionOrConstructorTypeToError(isUnionType, parseConstituentType)
} else {
typeNode = parseConstituentType(p)
}
if p.token == operator || hasLeadingOperator {
types := make([]*ast.Node, 1, 8)
types[0] = typeNode
for p.parseOptional(operator) {
types = append(types, p.parseFunctionOrConstructorTypeToError(isUnionType, parseConstituentType))
}
typeNode = p.createUnionOrIntersectionTypeNode(operator, p.newNodeList(core.NewTextRange(pos, p.nodePos()), p.nodeSlicePool.Clone(types)))
p.finishNode(typeNode, pos)
}
return typeNode
}
func (p *Parser) createUnionOrIntersectionTypeNode(operator ast.Kind, types *ast.NodeList) *ast.Node {
switch operator {
case ast.KindBarToken:
return p.factory.NewUnionTypeNode(types)
case ast.KindAmpersandToken:
return p.factory.NewIntersectionTypeNode(types)
default:
panic("Unhandled case in createUnionOrIntersectionType")
}
}
func (p *Parser) parseTypeOperatorOrHigher() *ast.TypeNode {
operator := p.token
switch operator {
case ast.KindKeyOfKeyword, ast.KindUniqueKeyword, ast.KindReadonlyKeyword:
return p.parseTypeOperator(operator)
case ast.KindInferKeyword:
return p.parseInferType()
}
return doInContext(p, ast.NodeFlagsDisallowConditionalTypesContext, false, (*Parser).parsePostfixTypeOrHigher)
}
func (p *Parser) parseTypeOperator(operator ast.Kind) *ast.Node {
pos := p.nodePos()
p.parseExpected(operator)
return p.finishNode(p.factory.NewTypeOperatorNode(operator, p.parseTypeOperatorOrHigher()), pos)
}
func (p *Parser) parseInferType() *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindInferKeyword)
return p.finishNode(p.factory.NewInferTypeNode(p.parseTypeParameterOfInferType()), pos)
}
func (p *Parser) parseTypeParameterOfInferType() *ast.Node {
pos := p.nodePos()
name := p.parseIdentifier()
constraint := p.tryParseConstraintOfInferType()
return p.finishNode(p.factory.NewTypeParameterDeclaration(nil /*modifiers*/, name, constraint, nil /*defaultType*/), pos)
}
func (p *Parser) tryParseConstraintOfInferType() *ast.Node {
state := p.mark()
if p.parseOptional(ast.KindExtendsKeyword) {
constraint := doInContext(p, ast.NodeFlagsDisallowConditionalTypesContext, true, (*Parser).parseType)
if p.inDisallowConditionalTypesContext() || p.token != ast.KindQuestionToken {
return constraint
}
}
p.rewind(state)
return nil
}
func (p *Parser) parsePostfixTypeOrHigher() *ast.Node {
pos := p.nodePos()
typeNode := p.parseNonArrayType()
for !p.hasPrecedingLineBreak() {
switch p.token {
case ast.KindExclamationToken:
p.nextToken()
typeNode = p.finishNode(p.factory.NewJSDocNonNullableType(typeNode), pos)
case ast.KindQuestionToken:
// If next token is start of a type we have a conditional type
if p.lookAhead((*Parser).nextIsStartOfType) {
return typeNode
}
p.nextToken()
typeNode = p.finishNode(p.factory.NewJSDocNullableType(typeNode), pos)
case ast.KindOpenBracketToken:
p.parseExpected(ast.KindOpenBracketToken)
if p.isStartOfType(false /*isStartOfParameter*/) {
indexType := p.parseType()
p.parseExpected(ast.KindCloseBracketToken)
typeNode = p.finishNode(p.factory.NewIndexedAccessTypeNode(typeNode, indexType), pos)
} else {
p.parseExpected(ast.KindCloseBracketToken)
typeNode = p.finishNode(p.factory.NewArrayTypeNode(typeNode), pos)
}
default:
return typeNode
}
}
return typeNode
}
func (p *Parser) nextIsStartOfType() bool {
p.nextToken()
return p.isStartOfType(false /*inStartOfParameter*/)
}
func (p *Parser) parseNonArrayType() *ast.Node {
switch p.token {
case ast.KindAnyKeyword, ast.KindUnknownKeyword, ast.KindStringKeyword, ast.KindNumberKeyword, ast.KindBigIntKeyword,
ast.KindSymbolKeyword, ast.KindBooleanKeyword, ast.KindUndefinedKeyword, ast.KindNeverKeyword, ast.KindObjectKeyword:
state := p.mark()
keywordTypeNode := p.parseKeywordTypeNode()
// If these are followed by a dot then parse these out as a dotted type reference instead
if p.token != ast.KindDotToken {
return keywordTypeNode
}
p.rewind(state)
return p.parseTypeReference()
case ast.KindAsteriskEqualsToken:
// If there is '*=', treat it as * followed by postfix =
p.scanner.ReScanAsteriskEqualsToken()
fallthrough
case ast.KindAsteriskToken:
return p.parseJSDocAllType()
case ast.KindQuestionQuestionToken:
// If there is '??', treat it as prefix-'?' in JSDoc type.
p.scanner.ReScanQuestionToken()
fallthrough
case ast.KindQuestionToken:
return p.parseJSDocNullableType()
case ast.KindExclamationToken:
return p.parseJSDocNonNullableType()
case ast.KindNoSubstitutionTemplateLiteral, ast.KindStringLiteral, ast.KindNumericLiteral, ast.KindBigIntLiteral, ast.KindTrueKeyword,
ast.KindFalseKeyword, ast.KindNullKeyword:
return p.parseLiteralTypeNode(false /*negative*/)
case ast.KindMinusToken:
if p.lookAhead((*Parser).nextTokenIsNumericOrBigIntLiteral) {
return p.parseLiteralTypeNode(true /*negative*/)
}
return p.parseTypeReference()
case ast.KindVoidKeyword:
return p.parseKeywordTypeNode()
case ast.KindThisKeyword:
thisKeyword := p.parseThisTypeNode()
if p.token == ast.KindIsKeyword && !p.hasPrecedingLineBreak() {
return p.parseThisTypePredicate(thisKeyword)
}
return thisKeyword
case ast.KindTypeOfKeyword:
if p.lookAhead((*Parser).nextIsStartOfTypeOfImportType) {
return p.parseImportType()
}
return p.parseTypeQuery()
case ast.KindOpenBraceToken:
if p.lookAhead((*Parser).nextIsStartOfMappedType) {
return p.parseMappedType()
}
return p.parseTypeLiteral()
case ast.KindOpenBracketToken:
return p.parseTupleType()
case ast.KindOpenParenToken:
return p.parseParenthesizedType()
case ast.KindImportKeyword:
return p.parseImportType()
case ast.KindAssertsKeyword:
if p.lookAhead((*Parser).nextTokenIsIdentifierOrKeywordOnSameLine) {
return p.parseAssertsTypePredicate()
}
return p.parseTypeReference()
case ast.KindTemplateHead:
return p.parseTemplateType()
default:
return p.parseTypeReference()
}
}
func (p *Parser) parseKeywordTypeNode() *ast.Node {
pos := p.nodePos()
result := p.factory.NewKeywordTypeNode(p.token)
p.nextToken()
return p.finishNode(result, pos)
}
func (p *Parser) parseThisTypeNode() *ast.Node {
pos := p.nodePos()
p.nextToken()
return p.finishNode(p.factory.NewThisTypeNode(), pos)
}
func (p *Parser) parseThisTypePredicate(lhs *ast.Node) *ast.Node {
p.nextToken()
return p.finishNode(p.factory.NewTypePredicateNode(nil /*assertsModifier*/, lhs, p.parseType()), lhs.Pos())
}
func (p *Parser) parseJSDocAllType() *ast.Node {
pos := p.nodePos()
p.nextToken()
return p.finishNode(p.factory.NewJSDocAllType(), pos)
}
func (p *Parser) parseJSDocNonNullableType() *ast.TypeNode {
pos := p.nodePos()
p.nextToken()
return p.finishNode(p.factory.NewJSDocNonNullableType(p.parseNonArrayType()), pos)
}
func (p *Parser) parseJSDocNullableType() *ast.Node {
pos := p.nodePos()
// skip the ?
p.nextToken()
return p.finishNode(p.factory.NewJSDocNullableType(p.parseType()), pos)
}
func (p *Parser) parseJSDocType() *ast.TypeNode {
p.scanner.SetSkipJSDocLeadingAsterisks(true)
pos := p.nodePos()
hasDotDotDot := p.parseOptional(ast.KindDotDotDotToken)
t := p.parseTypeOrTypePredicate()
p.scanner.SetSkipJSDocLeadingAsterisks(false)
if hasDotDotDot {
t = p.finishNode(p.factory.NewJSDocVariadicType(t), pos)
}
if p.token == ast.KindEqualsToken {
p.nextToken()
return p.finishNode(p.factory.NewJSDocOptionalType(t), pos)
}
return t
}
func (p *Parser) parseLiteralTypeNode(negative bool) *ast.Node {
pos := p.nodePos()
if negative {
p.nextToken()
}
var expression *ast.Expression
if p.token == ast.KindTrueKeyword || p.token == ast.KindFalseKeyword || p.token == ast.KindNullKeyword {
expression = p.parseKeywordExpression()
} else {
expression = p.parseLiteralExpression(false /*intern*/)
}
if negative {
expression = p.finishNode(p.factory.NewPrefixUnaryExpression(ast.KindMinusToken, expression), pos)
}
return p.finishNode(p.factory.NewLiteralTypeNode(expression), pos)
}
func (p *Parser) parseTypeReference() *ast.Node {
pos := p.nodePos()
return p.finishNode(p.factory.NewTypeReferenceNode(p.parseEntityNameOfTypeReference(), p.parseTypeArgumentsOfTypeReference()), pos)
}
func (p *Parser) parseEntityNameOfTypeReference() *ast.Node {
return p.parseEntityName(true /*allowReservedWords*/, diagnostics.Type_expected)
}
func (p *Parser) parseEntityName(allowReservedWords bool, diagnosticMessage *diagnostics.Message) *ast.Node {
pos := p.nodePos()
var entity *ast.Node
if allowReservedWords {
entity = p.parseIdentifierNameWithDiagnostic(diagnosticMessage)
} else {
entity = p.parseIdentifierWithDiagnostic(diagnosticMessage, nil)
}
for p.parseOptional(ast.KindDotToken) {
if p.token == ast.KindLessThanToken {
// The entity is part of a JSDoc-style generic. We will use the gap between `typeName` and
// `typeArguments` to report it as a grammar error in the checker.
break
}
entity = p.finishNode(p.factory.NewQualifiedName(entity, p.parseRightSideOfDot(allowReservedWords, false /*allowPrivateIdentifiers*/, true /*allowUnicodeEscapeSequenceInIdentifierName*/)), pos)
}
return entity
}
func (p *Parser) parseRightSideOfDot(allowIdentifierNames bool, allowPrivateIdentifiers bool, allowUnicodeEscapeSequenceInIdentifierName bool) *ast.Node {
// Technically a keyword is valid here as all identifiers and keywords are identifier names.
// However, often we'll encounter this in error situations when the identifier or keyword
// is actually starting another valid construct.
//
// So, we check for the following specific case:
//
// name.
// identifierOrKeyword identifierNameOrKeyword
//
// Note: the newlines are important here. For example, if that above code
// were rewritten into:
//
// name.identifierOrKeyword
// identifierNameOrKeyword
//
// Then we would consider it valid. That's because ASI would take effect and
// the code would be implicitly: "name.identifierOrKeyword; identifierNameOrKeyword".
// In the first case though, ASI will not take effect because there is not a
// line terminator after the identifier or keyword.
if p.hasPrecedingLineBreak() && tokenIsIdentifierOrKeyword(p.token) && p.lookAhead((*Parser).nextTokenIsIdentifierOrKeywordOnSameLine) {
// Report that we need an identifier. However, report it right after the dot,
// and not on the next token. This is because the next token might actually
// be an identifier and the error would be quite confusing.
p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.Identifier_expected)
return p.createMissingIdentifier()
}
if p.token == ast.KindPrivateIdentifier {
node := p.parsePrivateIdentifier()
if allowPrivateIdentifiers {
return node
}
p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.Identifier_expected)
return p.createMissingIdentifier()
}
if allowIdentifierNames {
if allowUnicodeEscapeSequenceInIdentifierName {
return p.parseIdentifierName()
}
return p.parseIdentifierNameErrorOnUnicodeEscapeSequence()
}
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
id := p.parseIdentifier()
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
return id
}
func (p *Parser) newIdentifier(text string) *ast.Node {
p.identifierCount++
id := p.factory.NewIdentifier(text)
if text == "await" {
p.statementHasAwaitIdentifier = true
}
return id
}
func (p *Parser) createMissingIdentifier() *ast.Node {
return p.finishNode(p.newIdentifier(""), p.nodePos())
}
func (p *Parser) parsePrivateIdentifier() *ast.Node {
pos := p.nodePos()
text := p.scanner.TokenValue()
p.nextToken()
return p.finishNode(p.factory.NewPrivateIdentifier(p.internIdentifier(text)), pos)
}
func (p *Parser) reScanLessThanToken() ast.Kind {
p.token = p.scanner.ReScanLessThanToken()
return p.token
}
func (p *Parser) reScanGreaterThanToken() ast.Kind {
p.token = p.scanner.ReScanGreaterThanToken()
return p.token
}
func (p *Parser) reScanSlashToken() ast.Kind {
p.token = p.scanner.ReScanSlashToken()
return p.token
}
func (p *Parser) reScanTemplateToken(isTaggedTemplate bool) ast.Kind {
p.token = p.scanner.ReScanTemplateToken(isTaggedTemplate)
return p.token
}
func (p *Parser) parseTypeArgumentsOfTypeReference() *ast.NodeList {
if !p.hasPrecedingLineBreak() && p.reScanLessThanToken() == ast.KindLessThanToken {
return p.parseTypeArguments()
}
return nil
}
func (p *Parser) parseTypeArguments() *ast.NodeList {
if p.token == ast.KindLessThanToken {
return p.parseBracketedList(PCTypeArguments, (*Parser).parseType, ast.KindLessThanToken, ast.KindGreaterThanToken)
}
return nil
}
func (p *Parser) nextIsStartOfTypeOfImportType() bool {
p.nextToken()
return p.token == ast.KindImportKeyword
}
func (p *Parser) parseImportType() *ast.Node {
p.sourceFlags |= ast.NodeFlagsPossiblyContainsDynamicImport
pos := p.nodePos()
isTypeOf := p.parseOptional(ast.KindTypeOfKeyword)
p.parseExpected(ast.KindImportKeyword)
p.parseExpected(ast.KindOpenParenToken)
typeNode := p.parseType()
var attributes *ast.Node
if p.parseOptional(ast.KindCommaToken) {
openBracePosition := p.scanner.TokenStart()
p.parseExpected(ast.KindOpenBraceToken)
currentToken := p.token
if currentToken == ast.KindWithKeyword || currentToken == ast.KindAssertKeyword {
p.nextToken()
} else {
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(ast.KindWithKeyword))
}
p.parseExpected(ast.KindColonToken)
attributes = p.parseImportAttributes(currentToken, true /*skipKeyword*/)
p.parseOptional(ast.KindCommaToken)
if !p.parseExpected(ast.KindCloseBraceToken) {
if len(p.diagnostics) != 0 {
lastDiagnostic := p.diagnostics[len(p.diagnostics)-1]
if lastDiagnostic.Code() == diagnostics.X_0_expected.Code() {
related := ast.NewDiagnostic(nil, core.NewTextRange(openBracePosition, openBracePosition+1), diagnostics.The_parser_expected_to_find_a_1_to_match_the_0_token_here, "{", "}")
lastDiagnostic.AddRelatedInfo(related)
}
}
}
}
p.parseExpected(ast.KindCloseParenToken)
var qualifier *ast.Node
if p.parseOptional(ast.KindDotToken) {
qualifier = p.parseEntityNameOfTypeReference()
}
typeArguments := p.parseTypeArgumentsOfTypeReference()
return p.finishNode(p.factory.NewImportTypeNode(isTypeOf, typeNode, attributes, qualifier, typeArguments), pos)
}
func (p *Parser) parseImportAttribute() *ast.Node {
pos := p.nodePos()
var name *ast.Node
if tokenIsIdentifierOrKeyword(p.token) {
name = p.parseIdentifierName()
} else if p.token == ast.KindStringLiteral {
name = p.parseLiteralExpression(false /*intern*/)
}
if name != nil {
p.parseExpected(ast.KindColonToken)
} else {
p.parseErrorAtCurrentToken(diagnostics.Identifier_or_string_literal_expected)
}
value := p.parseAssignmentExpressionOrHigher()
return p.finishNode(p.factory.NewImportAttribute(name, value), pos)
}
func (p *Parser) parseImportAttributes(token ast.Kind, skipKeyword bool) *ast.Node {
pos := p.nodePos()
if !skipKeyword {
p.parseExpected(token)
}
var elements *ast.NodeList
var multiLine bool
openBracePosition := p.scanner.TokenStart()
if p.parseExpected(ast.KindOpenBraceToken) {
multiLine = p.hasPrecedingLineBreak()
elements = p.parseDelimitedList(PCImportAttributes, (*Parser).parseImportAttribute)
if !p.parseExpected(ast.KindCloseBraceToken) {
if len(p.diagnostics) != 0 {
lastDiagnostic := p.diagnostics[len(p.diagnostics)-1]
if lastDiagnostic.Code() == diagnostics.X_0_expected.Code() {
related := ast.NewDiagnostic(nil, core.NewTextRange(openBracePosition, openBracePosition+1), diagnostics.The_parser_expected_to_find_a_1_to_match_the_0_token_here, "{", "}")
lastDiagnostic.AddRelatedInfo(related)
}
}
}
} else {
elements = p.parseEmptyNodeList()
}
return p.finishNode(p.factory.NewImportAttributes(token, elements, multiLine), pos)
}
func (p *Parser) parseTypeQuery() *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindTypeOfKeyword)
entityName := p.parseEntityName(true /*allowReservedWords*/, nil)
// Make sure we perform ASI to prevent parsing the next line's type arguments as part of an instantiation expression
var typeArguments *ast.NodeList
if !p.hasPrecedingLineBreak() {
typeArguments = p.parseTypeArguments()
}
return p.finishNode(p.factory.NewTypeQueryNode(entityName, typeArguments), pos)
}
func (p *Parser) nextIsStartOfMappedType() bool {
p.nextToken()
if p.token == ast.KindPlusToken || p.token == ast.KindMinusToken {
return p.nextToken() == ast.KindReadonlyKeyword
}
if p.token == ast.KindReadonlyKeyword {
p.nextToken()
}
return p.token == ast.KindOpenBracketToken && p.nextTokenIsIdentifier() && p.nextToken() == ast.KindInKeyword
}
func (p *Parser) parseMappedType() *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindOpenBraceToken)
var readonlyToken *ast.Node // ReadonlyKeyword | PlusToken | MinusToken
if p.token == ast.KindReadonlyKeyword || p.token == ast.KindPlusToken || p.token == ast.KindMinusToken {
readonlyToken = p.parseTokenNode()
if readonlyToken.Kind != ast.KindReadonlyKeyword {
p.parseExpected(ast.KindReadonlyKeyword)
}
}
p.parseExpected(ast.KindOpenBracketToken)
typeParameter := p.parseMappedTypeParameter()
var nameType *ast.TypeNode
if p.parseOptional(ast.KindAsKeyword) {
nameType = p.parseType()
}
p.parseExpected(ast.KindCloseBracketToken)
var questionToken *ast.Node // QuestionToken | PlusToken | MinusToken
if p.token == ast.KindQuestionToken || p.token == ast.KindPlusToken || p.token == ast.KindMinusToken {
questionToken = p.parseTokenNode()
if questionToken.Kind != ast.KindQuestionToken {
p.parseExpected(ast.KindQuestionToken)
}
}
typeNode := p.parseTypeAnnotation()
p.parseSemicolon()
members := p.parseList(PCTypeMembers, (*Parser).parseTypeMember)
p.parseExpected(ast.KindCloseBraceToken)
return p.finishNode(p.factory.NewMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, typeNode, members), pos)
}
func (p *Parser) parseMappedTypeParameter() *ast.Node {
pos := p.nodePos()
name := p.parseIdentifierName()
p.parseExpected(ast.KindInKeyword)
typeNode := p.parseType()
return p.finishNode(p.factory.NewTypeParameterDeclaration(nil /*modifiers*/, name, typeNode, nil /*defaultType*/), pos)
}
func (p *Parser) parseTypeMember() *ast.Node {
if p.token == ast.KindOpenParenToken || p.token == ast.KindLessThanToken {
return p.parseSignatureMember(ast.KindCallSignature)
}
if p.token == ast.KindNewKeyword && p.lookAhead((*Parser).nextTokenIsOpenParenOrLessThan) {
return p.parseSignatureMember(ast.KindConstructSignature)
}
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
modifiers := p.parseModifiers()
if p.parseContextualModifier(ast.KindGetKeyword) {
return p.parseAccessorDeclaration(pos, hasJSDoc, modifiers, ast.KindGetAccessor, ParseFlagsType)
}
if p.parseContextualModifier(ast.KindSetKeyword) {
return p.parseAccessorDeclaration(pos, hasJSDoc, modifiers, ast.KindSetAccessor, ParseFlagsType)
}
if p.isIndexSignature() {
return p.parseIndexSignatureDeclaration(pos, hasJSDoc, modifiers)
}
return p.parsePropertyOrMethodSignature(pos, hasJSDoc, modifiers)
}
func (p *Parser) nextTokenIsOpenParenOrLessThan() bool {
p.nextToken()
return p.token == ast.KindOpenParenToken || p.token == ast.KindLessThanToken
}
func (p *Parser) parseSignatureMember(kind ast.Kind) *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
if kind == ast.KindConstructSignature {
p.parseExpected(ast.KindNewKeyword)
}
typeParameters := p.parseTypeParameters()
parameters := p.parseParameters(ParseFlagsType)
typeNode := p.parseReturnType(ast.KindColonToken /*isType*/, true)
p.parseTypeMemberSemicolon()
var result *ast.Node
if kind == ast.KindCallSignature {
result = p.factory.NewCallSignatureDeclaration(typeParameters, parameters, typeNode)
} else {
result = p.factory.NewConstructSignatureDeclaration(typeParameters, parameters, typeNode)
}
p.finishNode(result, pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseTypeParameters() *ast.NodeList {
if p.token == ast.KindLessThanToken {
return p.parseBracketedList(PCTypeParameters, (*Parser).parseTypeParameter, ast.KindLessThanToken, ast.KindGreaterThanToken)
}
return nil
}
func (p *Parser) parseTypeParameter() *ast.Node {
pos := p.nodePos()
modifiers := p.parseModifiersEx(false /*allowDecorators*/, true /*permitConstAsModifier*/, false /*stopOnStartOfClassStaticBlock*/)
name := p.parseIdentifier()
var constraint *ast.TypeNode
var expression *ast.Expression
if p.parseOptional(ast.KindExtendsKeyword) {
// It's not uncommon for people to write improper constraints to a generic. If the
// user writes a constraint that is an expression and not an actual type, then parse
// it out as an expression (so we can recover well), but report that a type is needed
// instead.
if p.isStartOfType(false /*inStartOfParameter*/) || !p.isStartOfExpression() {
constraint = p.parseType()
} else {
// It was not a type, and it looked like an expression. Parse out an expression
// here so we recover well. Note: it is important that we call parseUnaryExpression
// and not parseExpression here. If the user has:
//
// <T extends "">
//
// We do *not* want to consume the `>` as we're consuming the expression for "".
expression = p.parseUnaryExpressionOrHigher()
}
}
var defaultType *ast.TypeNode
if p.parseOptional(ast.KindEqualsToken) {
defaultType = p.parseType()
}
result := p.finishNode(p.factory.NewTypeParameterDeclaration(modifiers, name, constraint, defaultType), pos)
result.AsTypeParameter().Expression = expression
return result
}
func (p *Parser) parseParameters(flags ParseFlags) *ast.NodeList {
// FormalParameters [Yield,Await]: (modified)
// [empty]
// FormalParameterList[?Yield,Await]
//
// FormalParameter[Yield,Await]: (modified)
// BindingElement[?Yield,Await]
//
// BindingElement [Yield,Await]: (modified)
// SingleNameBinding[?Yield,?Await]
// BindingPattern[?Yield,?Await]Initializer [In, ?Yield,?Await] opt
//
// SingleNameBinding [Yield,Await]:
// BindingIdentifier[?Yield,?Await]Initializer [In, ?Yield,?Await] opt
if p.parseExpected(ast.KindOpenParenToken) {
parameters := p.parseParametersWorker(flags, true /*allowAmbiguity*/)
p.parseExpected(ast.KindCloseParenToken)
return parameters
}
return p.parseEmptyNodeList()
}
func (p *Parser) parseParametersWorker(flags ParseFlags, allowAmbiguity bool) *ast.NodeList {
// FormalParameters [Yield,Await]: (modified)
// [empty]
// FormalParameterList[?Yield,Await]
//
// FormalParameter[Yield,Await]: (modified)
// BindingElement[?Yield,Await]
//
// BindingElement [Yield,Await]: (modified)
// SingleNameBinding[?Yield,?Await]
// BindingPattern[?Yield,?Await]Initializer [In, ?Yield,?Await] opt
//
// SingleNameBinding [Yield,Await]:
// BindingIdentifier[?Yield,?Await]Initializer [In, ?Yield,?Await] opt
inAwaitContext := p.contextFlags&ast.NodeFlagsAwaitContext != 0
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsYieldContext, flags&ParseFlagsYield != 0)
p.setContextFlags(ast.NodeFlagsAwaitContext, flags&ParseFlagsAwait != 0)
parameters := p.parseDelimitedList(PCParameters, func(p *Parser) *ast.Node {
parameter := p.parseParameterEx(inAwaitContext, allowAmbiguity)
if parameter != nil && flags&ParseFlagsType == 0 {
p.checkJSSyntax(parameter)
}
return parameter
})
p.contextFlags = saveContextFlags
return parameters
}
func (p *Parser) parseParameter() *ast.Node {
return p.parseParameterEx(false /*inOuterAwaitContext*/, true /*allowAmbiguity*/)
}
func (p *Parser) parseParameterEx(inOuterAwaitContext bool, allowAmbiguity bool) *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
// FormalParameter [Yield,Await]:
// BindingElement[?Yield,?Await]
// Decorators are parsed in the outer [Await] context, the rest of the parameter is parsed in the function's [Await] context.
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsAwaitContext, inOuterAwaitContext)
modifiers := p.parseModifiersEx(true /*allowDecorators*/, false /*permitConstAsModifier*/, false /*stopOnStartOfClassStaticBlock*/)
p.contextFlags = saveContextFlags
if p.token == ast.KindThisKeyword {
result := p.factory.NewParameterDeclaration(
modifiers,
nil, /*dotDotDotToken*/
p.createIdentifier(true /*isIdentifier*/),
nil, /*questionToken*/
p.parseTypeAnnotation(),
nil /*initializer*/)
if modifiers != nil {
p.parseErrorAtRange(modifiers.Nodes[0].Loc, diagnostics.Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters)
}
p.withJSDoc(p.finishNode(result, pos), hasJSDoc)
return result
}
dotDotDotToken := p.parseOptionalToken(ast.KindDotDotDotToken)
if !allowAmbiguity && !p.isParameterNameStart() {
return nil
}
result := p.factory.NewParameterDeclaration(
modifiers,
dotDotDotToken,
p.parseNameOfParameter(modifiers),
p.parseOptionalToken(ast.KindQuestionToken),
p.parseTypeAnnotation(),
p.parseInitializer())
p.withJSDoc(p.finishNode(result, pos), hasJSDoc)
return result
}
func (p *Parser) isParameterNameStart() bool {
// Be permissive about await and yield by calling isBindingIdentifier instead of isIdentifier; disallowing
// them during a speculative parse leads to many more follow-on errors than allowing the function to parse then later
// complaining about the use of the keywords.
return p.isBindingIdentifier() || p.token == ast.KindOpenBracketToken || p.token == ast.KindOpenBraceToken
}
func (p *Parser) parseNameOfParameter(modifiers *ast.ModifierList) *ast.Node {
// FormalParameter [Yield,Await]:
// BindingElement[?Yield,?Await]
name := p.parseIdentifierOrPatternWithDiagnostic(diagnostics.Private_identifiers_cannot_be_used_as_parameters)
if name.Loc.Len() == 0 && modifiers == nil && ast.IsModifierKind(p.token) {
// in cases like
// 'use strict'
// function foo(static)
// isParameter('static') == true, because of isModifier('static')
// however 'static' is not a legal identifier in a strict mode.
// so result of this function will be ParameterDeclaration (flags = 0, name = missing, type = undefined, initializer = undefined)
// and current token will not change => parsing of the enclosing parameter list will last till the end of time (or OOM)
// to avoid this we'll advance cursor to the next token.
p.nextToken()
}
return name
}
func (p *Parser) parseReturnType(returnToken ast.Kind, isType bool) *ast.TypeNode {
if p.shouldParseReturnType(returnToken, isType) {
return doInContext(p, ast.NodeFlagsDisallowConditionalTypesContext, false, (*Parser).parseTypeOrTypePredicate)
}
return nil
}
func (p *Parser) shouldParseReturnType(returnToken ast.Kind, isType bool) bool {
if returnToken == ast.KindEqualsGreaterThanToken {
p.parseExpected(returnToken)
return true
} else if p.parseOptional(ast.KindColonToken) {
return true
} else if isType && p.token == ast.KindEqualsGreaterThanToken {
// This is easy to get backward, especially in type contexts, so parse the type anyway
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(ast.KindColonToken))
p.nextToken()
return true
}
return false
}
func (p *Parser) parseTypeOrTypePredicate() *ast.TypeNode {
if p.isIdentifier() {
state := p.mark()
pos := p.nodePos()
id := p.parseIdentifier()
if p.token == ast.KindIsKeyword && !p.hasPrecedingLineBreak() {
p.nextToken()
return p.finishNode(p.factory.NewTypePredicateNode(nil /*assertsModifier*/, id, p.parseType()), pos)
}
p.rewind(state)
}
return p.parseType()
}
func (p *Parser) parseTypeMemberSemicolon() {
// We allow type members to be separated by commas or (possibly ASI) semicolons.
// First check if it was a comma. If so, we're done with the member.
if p.parseOptional(ast.KindCommaToken) {
return
}
// Didn't have a comma. We must have a (possible ASI) semicolon.
p.parseSemicolon()
}
func (p *Parser) parseAccessorDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList, kind ast.Kind, flags ParseFlags) *ast.Node {
name := p.parsePropertyName()
typeParameters := p.parseTypeParameters()
parameters := p.parseParameters(ParseFlagsNone)
returnType := p.parseReturnType(ast.KindColonToken, false /*isType*/)
body := p.parseFunctionBlockOrSemicolon(flags, nil /*diagnosticMessage*/)
var result *ast.Node
// Keep track of `typeParameters` (for both) and `type` (for setters) if they were parsed those indicate grammar errors
if kind == ast.KindGetAccessor {
result = p.factory.NewGetAccessorDeclaration(modifiers, name, typeParameters, parameters, returnType, nil /*fullSignature*/, body)
} else {
result = p.factory.NewSetAccessorDeclaration(modifiers, name, typeParameters, parameters, returnType, nil /*fullSignature*/, body)
}
p.withJSDoc(p.finishNode(result, pos), hasJSDoc)
if flags&ParseFlagsType == 0 {
p.checkJSSyntax(result)
}
return result
}
func (p *Parser) parsePropertyName() *ast.Node {
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
prop := p.parsePropertyNameWorker(true /*allowComputedPropertyNames*/)
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
return prop
}
func (p *Parser) parsePropertyNameWorker(allowComputedPropertyNames bool) *ast.Node {
if p.token == ast.KindStringLiteral || p.token == ast.KindNumericLiteral || p.token == ast.KindBigIntLiteral {
literal := p.parseLiteralExpression(true /*intern*/)
return literal
}
if allowComputedPropertyNames && p.token == ast.KindOpenBracketToken {
return p.parseComputedPropertyName()
}
if p.token == ast.KindPrivateIdentifier {
return p.parsePrivateIdentifier()
}
return p.parseIdentifierName()
}
func (p *Parser) parseComputedPropertyName() *ast.Node {
// PropertyName [Yield]:
// LiteralPropertyName
// ComputedPropertyName[?Yield]
pos := p.nodePos()
p.parseExpected(ast.KindOpenBracketToken)
// We parse any expression (including a comma expression). But the grammar
// says that only an assignment expression is allowed, so the grammar checker
// will error if it sees a comma expression.
expression := p.parseExpressionAllowIn()
p.parseExpected(ast.KindCloseBracketToken)
return p.finishNode(p.factory.NewComputedPropertyName(expression), pos)
}
func (p *Parser) parseFunctionBlockOrSemicolon(flags ParseFlags, diagnosticMessage *diagnostics.Message) *ast.Node {
if p.token != ast.KindOpenBraceToken {
if flags&ParseFlagsType != 0 {
p.parseTypeMemberSemicolon()
return nil
}
if p.canParseSemicolon() {
p.parseSemicolon()
return nil
}
}
return p.parseFunctionBlock(flags, diagnosticMessage)
}
func (p *Parser) parseFunctionBlock(flags ParseFlags, diagnosticMessage *diagnostics.Message) *ast.Node {
saveContextFlags := p.contextFlags
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
p.setContextFlags(ast.NodeFlagsYieldContext, flags&ParseFlagsYield != 0)
p.setContextFlags(ast.NodeFlagsAwaitContext, flags&ParseFlagsAwait != 0)
// We may be in a [Decorator] context when parsing a function expression or
// arrow function. The body of the function is not in [Decorator] context.
p.setContextFlags(ast.NodeFlagsDecoratorContext, false)
block := p.parseBlock(flags&ParseFlagsIgnoreMissingOpenBrace != 0, diagnosticMessage)
p.contextFlags = saveContextFlags
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
return block
}
func (p *Parser) isIndexSignature() bool {
return p.token == ast.KindOpenBracketToken && p.lookAhead((*Parser).nextIsUnambiguouslyIndexSignature)
}
func (p *Parser) nextIsUnambiguouslyIndexSignature() bool {
// The only allowed sequence is:
//
// [id:
//
// However, for error recovery, we also check the following cases:
//
// [...
// [id,
// [id?,
// [id?:
// [id?]
// [public id
// [private id
// [protected id
// []
//
p.nextToken()
if p.token == ast.KindDotDotDotToken || p.token == ast.KindCloseBracketToken {
return true
}
if ast.IsModifierKind(p.token) {
p.nextToken()
if p.isIdentifier() {
return true
}
} else if !p.isIdentifier() {
return false
} else {
// Skip the identifier
p.nextToken()
}
// A colon signifies a well formed indexer
// A comma should be a badly formed indexer because comma expressions are not allowed
// in computed properties.
if p.token == ast.KindColonToken || p.token == ast.KindCommaToken {
return true
}
// Question mark could be an indexer with an optional property,
// or it could be a conditional expression in a computed property.
if p.token != ast.KindQuestionToken {
return false
}
// If any of the following tokens are after the question mark, it cannot
// be a conditional expression, so treat it as an indexer.
p.nextToken()
return p.token == ast.KindColonToken || p.token == ast.KindCommaToken || p.token == ast.KindCloseBracketToken
}
func (p *Parser) parseIndexSignatureDeclaration(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
parameters := p.parseBracketedList(PCParameters, (*Parser).parseParameter, ast.KindOpenBracketToken, ast.KindCloseBracketToken)
typeNode := p.parseTypeAnnotation()
p.parseTypeMemberSemicolon()
result := p.finishNode(p.factory.NewIndexSignatureDeclaration(modifiers, parameters, typeNode), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parsePropertyOrMethodSignature(pos int, hasJSDoc bool, modifiers *ast.ModifierList) *ast.Node {
name := p.parsePropertyName()
questionToken := p.parseOptionalToken(ast.KindQuestionToken)
var result *ast.Node
if p.token == ast.KindOpenParenToken || p.token == ast.KindLessThanToken {
// Method signatures don't exist in expression contexts. So they have neither
// [Yield] nor [Await]
typeParameters := p.parseTypeParameters()
parameters := p.parseParameters(ParseFlagsType)
returnType := p.parseReturnType(ast.KindColonToken /*isType*/, true)
result = p.factory.NewMethodSignatureDeclaration(modifiers, name, questionToken, typeParameters, parameters, returnType)
} else {
typeNode := p.parseTypeAnnotation()
// Although type literal properties cannot not have initializers, we attempt
// to parse an initializer so we can report in the checker that an interface
// property or type literal property cannot have an initializer.
var initializer *ast.Expression
if p.token == ast.KindEqualsToken {
initializer = p.parseInitializer()
}
result = p.factory.NewPropertySignatureDeclaration(modifiers, name, questionToken, typeNode, initializer)
}
p.parseTypeMemberSemicolon()
p.withJSDoc(p.finishNode(result, pos), hasJSDoc)
return result
}
func (p *Parser) parseTypeLiteral() *ast.Node {
pos := p.nodePos()
result := p.finishNode(p.factory.NewTypeLiteralNode(p.parseObjectTypeMembers()), pos)
return result
}
func (p *Parser) parseObjectTypeMembers() *ast.NodeList {
if p.parseExpected(ast.KindOpenBraceToken) {
members := p.parseList(PCTypeMembers, (*Parser).parseTypeMember)
p.parseExpected(ast.KindCloseBraceToken)
return members
}
return p.parseEmptyNodeList()
}
func (p *Parser) parseTupleType() *ast.Node {
pos := p.nodePos()
return p.finishNode(p.factory.NewTupleTypeNode(p.parseBracketedList(PCTupleElementTypes, (*Parser).parseTupleElementNameOrTupleElementType, ast.KindOpenBracketToken, ast.KindCloseBracketToken)), pos)
}
func (p *Parser) parseTupleElementNameOrTupleElementType() *ast.Node {
if p.lookAhead((*Parser).scanStartOfNamedTupleElement) {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
dotDotDotToken := p.parseOptionalToken(ast.KindDotDotDotToken)
name := p.parseIdentifierName()
questionToken := p.parseOptionalToken(ast.KindQuestionToken)
p.parseExpected(ast.KindColonToken)
typeNode := p.parseTupleElementType()
result := p.finishNode(p.factory.NewNamedTupleMember(dotDotDotToken, name, questionToken, typeNode), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
return p.parseTupleElementType()
}
func (p *Parser) scanStartOfNamedTupleElement() bool {
if p.token == ast.KindDotDotDotToken {
return tokenIsIdentifierOrKeyword(p.nextToken()) && p.nextTokenIsColonOrQuestionColon()
}
return tokenIsIdentifierOrKeyword(p.token) && p.nextTokenIsColonOrQuestionColon()
}
func (p *Parser) nextTokenIsColonOrQuestionColon() bool {
return p.nextToken() == ast.KindColonToken || p.token == ast.KindQuestionToken && p.nextToken() == ast.KindColonToken
}
func (p *Parser) parseTupleElementType() *ast.TypeNode {
pos := p.nodePos()
if p.parseOptional(ast.KindDotDotDotToken) {
return p.finishNode(p.factory.NewRestTypeNode(p.parseType()), pos)
}
typeNode := p.parseType()
if ast.IsJSDocNullableType(typeNode) && typeNode.Pos() == typeNode.Type().Pos() {
node := p.factory.NewOptionalTypeNode(typeNode.Type())
node.Flags = typeNode.Flags
node.Loc = typeNode.Loc
typeNode.Type().Parent = node
return node
}
return typeNode
}
func (p *Parser) parseParenthesizedType() *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindOpenParenToken)
typeNode := p.parseType()
p.parseExpected(ast.KindCloseParenToken)
return p.finishNode(p.factory.NewParenthesizedTypeNode(typeNode), pos)
}
func (p *Parser) parseAssertsTypePredicate() *ast.TypeNode {
pos := p.nodePos()
assertsModifier := p.parseExpectedToken(ast.KindAssertsKeyword)
var parameterName *ast.Node
if p.token == ast.KindThisKeyword {
parameterName = p.parseThisTypeNode()
} else {
parameterName = p.parseIdentifier()
}
var typeNode *ast.TypeNode
if p.parseOptional(ast.KindIsKeyword) {
typeNode = p.parseType()
}
return p.finishNode(p.factory.NewTypePredicateNode(assertsModifier, parameterName, typeNode), pos)
}
func (p *Parser) parseTemplateType() *ast.Node {
pos := p.nodePos()
return p.finishNode(p.factory.NewTemplateLiteralTypeNode(p.parseTemplateHead(false /*isTaggedTemplate*/), p.parseTemplateTypeSpans()), pos)
}
func (p *Parser) parseTemplateHead(isTaggedTemplate bool) *ast.Node {
if !isTaggedTemplate && p.scanner.TokenFlags()&ast.TokenFlagsIsInvalid != 0 {
p.reScanTemplateToken(false /*isTaggedTemplate*/)
}
pos := p.nodePos()
result := p.factory.NewTemplateHead(p.scanner.TokenValue(), p.getTemplateLiteralRawText(2 /*endLength*/), p.scanner.TokenFlags()&ast.TokenFlagsTemplateLiteralLikeFlags)
p.nextToken()
return p.finishNode(result, pos)
}
func (p *Parser) getTemplateLiteralRawText(endLength int) string {
tokenText := p.scanner.TokenText()
if p.scanner.TokenFlags()&ast.TokenFlagsUnterminated != 0 {
endLength = 0
}
return tokenText[1 : len(tokenText)-endLength]
}
func (p *Parser) parseTemplateTypeSpans() *ast.NodeList {
pos := p.nodePos()
var list []*ast.Node
for {
span := p.parseTemplateTypeSpan()
list = append(list, span)
if span.AsTemplateLiteralTypeSpan().Literal.Kind != ast.KindTemplateMiddle {
break
}
}
return p.newNodeList(core.NewTextRange(pos, p.nodePos()), list)
}
func (p *Parser) parseTemplateTypeSpan() *ast.Node {
pos := p.nodePos()
return p.finishNode(p.factory.NewTemplateLiteralTypeSpan(p.parseType(), p.parseLiteralOfTemplateSpan(false /*isTaggedTemplate*/)), pos)
}
func (p *Parser) parseLiteralOfTemplateSpan(isTaggedTemplate bool) *ast.Node {
if p.token == ast.KindCloseBraceToken {
p.reScanTemplateToken(isTaggedTemplate)
return p.parseTemplateMiddleOrTail()
}
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(ast.KindCloseBraceToken))
return p.finishNode(p.factory.NewTemplateTail("", "", ast.TokenFlagsNone), p.nodePos())
}
func (p *Parser) parseTemplateMiddleOrTail() *ast.Node {
pos := p.nodePos()
var result *ast.Node
if p.token == ast.KindTemplateMiddle {
result = p.factory.NewTemplateMiddle(p.scanner.TokenValue(), p.getTemplateLiteralRawText(2 /*endLength*/), p.scanner.TokenFlags()&ast.TokenFlagsTemplateLiteralLikeFlags)
} else {
result = p.factory.NewTemplateTail(p.scanner.TokenValue(), p.getTemplateLiteralRawText(1 /*endLength*/), p.scanner.TokenFlags()&ast.TokenFlagsTemplateLiteralLikeFlags)
}
p.nextToken()
return p.finishNode(result, pos)
}
func (p *Parser) parseFunctionOrConstructorTypeToError(isInUnionType bool, parseConstituentType func(p *Parser) *ast.TypeNode) *ast.TypeNode {
// the function type and constructor type shorthand notation
// are not allowed directly in unions and intersections, but we'll
// try to parse them gracefully and issue a helpful message.
if p.isStartOfFunctionTypeOrConstructorType() {
typeNode := p.parseFunctionOrConstructorType()
var diagnostic *diagnostics.Message
if typeNode.Kind == ast.KindFunctionType {
diagnostic = core.IfElse(isInUnionType,
diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_a_union_type,
diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type)
} else {
diagnostic = core.IfElse(isInUnionType,
diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type,
diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type)
}
p.parseErrorAtRange(typeNode.Loc, diagnostic)
return typeNode
}
return parseConstituentType(p)
}
func (p *Parser) isStartOfFunctionTypeOrConstructorType() bool {
return p.token == ast.KindLessThanToken ||
p.token == ast.KindOpenParenToken && p.lookAhead((*Parser).nextIsUnambiguouslyStartOfFunctionType) ||
p.token == ast.KindNewKeyword ||
p.token == ast.KindAbstractKeyword && p.lookAhead((*Parser).nextTokenIsNewKeyword)
}
func (p *Parser) parseFunctionOrConstructorType() *ast.TypeNode {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
modifiers := p.parseModifiersForConstructorType()
isConstructorType := p.parseOptional(ast.KindNewKeyword)
debug.Assert(modifiers == nil || isConstructorType, "Per isStartOfFunctionOrConstructorType, a function type cannot have modifiers.")
typeParameters := p.parseTypeParameters()
parameters := p.parseParameters(ParseFlagsType)
returnType := p.parseReturnType(ast.KindEqualsGreaterThanToken, false /*isType*/)
var result *ast.TypeNode
if isConstructorType {
result = p.factory.NewConstructorTypeNode(modifiers, typeParameters, parameters, returnType)
} else {
result = p.factory.NewFunctionTypeNode(typeParameters, parameters, returnType)
}
p.finishNode(result, pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseModifiersForConstructorType() *ast.ModifierList {
if p.token == ast.KindAbstractKeyword {
pos := p.nodePos()
modifier := p.factory.NewModifier(p.token)
p.nextToken()
p.finishNode(modifier, pos)
return p.newModifierList(modifier.Loc, p.nodeSlicePool.NewSlice1(modifier))
}
return nil
}
func (p *Parser) nextTokenIsNewKeyword() bool {
return p.nextToken() == ast.KindNewKeyword
}
func (p *Parser) nextIsUnambiguouslyStartOfFunctionType() bool {
p.nextToken()
if p.token == ast.KindCloseParenToken || p.token == ast.KindDotDotDotToken {
// ( )
// ( ...
return true
}
if p.skipParameterStart() {
// We successfully skipped modifiers (if any) and an identifier or binding pattern,
// now see if we have something that indicates a parameter declaration
if p.token == ast.KindColonToken || p.token == ast.KindCommaToken || p.token == ast.KindQuestionToken || p.token == ast.KindEqualsToken {
// ( xxx :
// ( xxx ,
// ( xxx ?
// ( xxx =
return true
}
if p.token == ast.KindCloseParenToken && p.nextToken() == ast.KindEqualsGreaterThanToken {
// ( xxx ) =>
return true
}
}
return false
}
func (p *Parser) skipParameterStart() bool {
if ast.IsModifierKind(p.token) {
// Skip modifiers
p.parseModifiers()
}
p.parseOptional(ast.KindDotDotDotToken)
if p.isIdentifier() || p.token == ast.KindThisKeyword {
p.nextToken()
return true
}
if p.token == ast.KindOpenBracketToken || p.token == ast.KindOpenBraceToken {
// Return true if we can parse an array or object binding pattern with no errors
previousErrorCount := len(p.diagnostics)
p.parseIdentifierOrPattern()
return previousErrorCount == len(p.diagnostics)
}
return false
}
func (p *Parser) parseModifiers() *ast.ModifierList {
return p.parseModifiersEx(false, false, false)
}
func (p *Parser) parseModifiersEx(allowDecorators bool, permitConstAsModifier bool, stopOnStartOfClassStaticBlock bool) *ast.ModifierList {
var hasLeadingModifier bool
var hasTrailingDecorator bool
var hasTrailingModifier bool
var hasStaticModifier bool
// Decorators should be contiguous in a list of modifiers but can potentially appear in two places (i.e., `[...leadingDecorators, ...leadingModifiers, ...trailingDecorators, ...trailingModifiers]`).
// The leading modifiers *should* only contain `export` and `default` when trailingDecorators are present, but we'll handle errors for any other leading modifiers in the checker.
// It is illegal to have both leadingDecorators and trailingDecorators, but we will report that as a grammar check in the checker.
// parse leading decorators
pos := p.nodePos()
list := make([]*ast.Node, 0, 16)
for {
if allowDecorators && p.token == ast.KindAtToken && !hasTrailingModifier {
decorator := p.parseDecorator()
list = append(list, decorator)
if hasLeadingModifier {
hasTrailingDecorator = true
}
} else {
modifier := p.tryParseModifier(hasStaticModifier, permitConstAsModifier, stopOnStartOfClassStaticBlock)
if modifier == nil {
break
}
if modifier.Kind == ast.KindStaticKeyword {
hasStaticModifier = true
}
list = append(list, modifier)
if hasTrailingDecorator {
hasTrailingModifier = true
} else {
hasLeadingModifier = true
}
}
}
if len(list) != 0 {
return p.newModifierList(core.NewTextRange(pos, p.nodePos()), p.nodeSlicePool.Clone(list))
}
return nil
}
func (p *Parser) parseDecorator() *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindAtToken)
expression := doInContext(p, ast.NodeFlagsDecoratorContext, true, (*Parser).parseDecoratorExpression)
return p.finishNode(p.factory.NewDecorator(expression), pos)
}
func (p *Parser) parseDecoratorExpression() *ast.Expression {
if p.inAwaitContext() && p.token == ast.KindAwaitKeyword {
// `@await` is disallowed in an [Await] context, but can cause parsing to go off the rails
// This simply parses the missing identifier and moves on.
pos := p.nodePos()
awaitExpression := p.parseIdentifierWithDiagnostic(diagnostics.Expression_expected, nil)
p.nextToken()
memberExpression := p.parseMemberExpressionRest(pos, awaitExpression /*allowOptionalChain*/, true)
return p.parseCallExpressionRest(pos, memberExpression)
}
return p.parseLeftHandSideExpressionOrHigher()
}
func (p *Parser) tryParseModifier(hasSeenStaticModifier bool, permitConstAsModifier bool, stopOnStartOfClassStaticBlock bool) *ast.Node {
pos := p.nodePos()
kind := p.token
if p.token == ast.KindConstKeyword && permitConstAsModifier {
// We need to ensure that any subsequent modifiers appear on the same line
// so that when 'const' is a standalone declaration, we don't issue an error.
if !p.lookAhead((*Parser).nextTokenIsOnSameLineAndCanFollowModifier) {
return nil
} else {
p.nextToken()
}
} else if stopOnStartOfClassStaticBlock && p.token == ast.KindStaticKeyword && p.lookAhead((*Parser).nextTokenIsOpenBrace) {
return nil
} else if hasSeenStaticModifier && p.token == ast.KindStaticKeyword {
return nil
} else {
if !p.parseAnyContextualModifier() {
return nil
}
}
return p.finishNode(p.factory.NewModifier(kind), pos)
}
func (p *Parser) parseContextualModifier(t ast.Kind) bool {
state := p.mark()
if p.token == t && p.nextTokenCanFollowModifier() {
return true
}
p.rewind(state)
return false
}
func (p *Parser) parseAnyContextualModifier() bool {
state := p.mark()
if ast.IsModifierKind(p.token) && p.nextTokenCanFollowModifier() {
return true
}
p.rewind(state)
return false
}
func (p *Parser) nextTokenCanFollowModifier() bool {
switch p.token {
case ast.KindConstKeyword:
// 'const' is only a modifier if followed by 'enum'.
return p.nextToken() == ast.KindEnumKeyword
case ast.KindExportKeyword:
p.nextToken()
if p.token == ast.KindDefaultKeyword {
return p.lookAhead((*Parser).nextTokenCanFollowDefaultKeyword)
}
if p.token == ast.KindTypeKeyword {
return p.lookAhead((*Parser).nextTokenCanFollowExportModifier)
}
return p.canFollowExportModifier()
case ast.KindDefaultKeyword:
return p.nextTokenCanFollowDefaultKeyword()
case ast.KindStaticKeyword:
p.nextToken()
return p.canFollowModifier()
case ast.KindGetKeyword, ast.KindSetKeyword:
p.nextToken()
return p.canFollowGetOrSetKeyword()
default:
return p.nextTokenIsOnSameLineAndCanFollowModifier()
}
}
func (p *Parser) nextTokenCanFollowDefaultKeyword() bool {
switch p.nextToken() {
case ast.KindClassKeyword, ast.KindFunctionKeyword, ast.KindInterfaceKeyword, ast.KindAtToken:
return true
case ast.KindAbstractKeyword:
return p.lookAhead((*Parser).nextTokenIsClassKeywordOnSameLine)
case ast.KindAsyncKeyword:
return p.lookAhead((*Parser).nextTokenIsFunctionKeywordOnSameLine)
}
return false
}
func (p *Parser) nextTokenIsIdentifierOrKeyword() bool {
return tokenIsIdentifierOrKeyword(p.nextToken())
}
func (p *Parser) nextTokenIsIdentifierOrKeywordOrGreaterThan() bool {
return tokenIsIdentifierOrKeywordOrGreaterThan(p.nextToken())
}
func (p *Parser) nextTokenIsIdentifierOrKeywordOnSameLine() bool {
return p.nextTokenIsIdentifierOrKeyword() && !p.hasPrecedingLineBreak()
}
func (p *Parser) nextTokenIsIdentifierOrKeywordOrLiteralOnSameLine() bool {
return (p.nextTokenIsIdentifierOrKeyword() || p.token == ast.KindNumericLiteral || p.token == ast.KindBigIntLiteral || p.token == ast.KindStringLiteral) && !p.hasPrecedingLineBreak()
}
func (p *Parser) nextTokenIsClassKeywordOnSameLine() bool {
return p.nextToken() == ast.KindClassKeyword && !p.hasPrecedingLineBreak()
}
func (p *Parser) nextTokenIsFunctionKeywordOnSameLine() bool {
return p.nextToken() == ast.KindFunctionKeyword && !p.hasPrecedingLineBreak()
}
func (p *Parser) nextTokenCanFollowExportModifier() bool {
p.nextToken()
return p.canFollowExportModifier()
}
func (p *Parser) canFollowExportModifier() bool {
return p.token == ast.KindAtToken || p.token != ast.KindAsteriskToken && p.token != ast.KindAsKeyword && p.token != ast.KindOpenBraceToken && p.canFollowModifier()
}
func (p *Parser) canFollowModifier() bool {
return p.token == ast.KindOpenBracketToken || p.token == ast.KindOpenBraceToken || p.token == ast.KindAsteriskToken || p.token == ast.KindDotDotDotToken || p.isLiteralPropertyName()
}
func (p *Parser) canFollowGetOrSetKeyword() bool {
return p.token == ast.KindOpenBracketToken || p.isLiteralPropertyName()
}
func (p *Parser) nextTokenIsOnSameLineAndCanFollowModifier() bool {
p.nextToken()
if p.hasPrecedingLineBreak() {
return false
}
return p.canFollowModifier()
}
func (p *Parser) nextTokenIsOpenBrace() bool {
return p.nextToken() == ast.KindOpenBraceToken
}
func (p *Parser) parseExpression() *ast.Expression {
// Expression[in]:
// AssignmentExpression[in]
// Expression[in] , AssignmentExpression[in]
// clear the decorator context when parsing Expression, as it should be unambiguous when parsing a decorator
saveContextFlags := p.contextFlags
p.contextFlags &= ^ast.NodeFlagsDecoratorContext
pos := p.nodePos()
expr := p.parseAssignmentExpressionOrHigher()
for {
operatorToken := p.parseOptionalToken(ast.KindCommaToken)
if operatorToken == nil {
break
}
expr = p.makeBinaryExpression(expr, operatorToken, p.parseAssignmentExpressionOrHigher(), pos)
}
p.contextFlags = saveContextFlags
return expr
}
func (p *Parser) parseExpressionAllowIn() *ast.Expression {
return doInContext(p, ast.NodeFlagsDisallowInContext, false, (*Parser).parseExpression)
}
func (p *Parser) parseAssignmentExpressionOrHigher() *ast.Expression {
return p.parseAssignmentExpressionOrHigherWorker(true /*allowReturnTypeInArrowFunction*/)
}
func (p *Parser) parseAssignmentExpressionOrHigherWorker(allowReturnTypeInArrowFunction bool) *ast.Expression {
// AssignmentExpression[in,yield]:
// 1) ConditionalExpression[?in,?yield]
// 2) LeftHandSideExpression = AssignmentExpression[?in,?yield]
// 3) LeftHandSideExpression AssignmentOperator AssignmentExpression[?in,?yield]
// 4) ArrowFunctionExpression[?in,?yield]
// 5) AsyncArrowFunctionExpression[in,yield,await]
// 6) [+Yield] YieldExpression[?In]
//
// Note: for ease of implementation we treat productions '2' and '3' as the same thing.
// (i.e. they're both BinaryExpressions with an assignment operator in it).
// First, do the simple check if we have a YieldExpression (production '6').
if p.isYieldExpression() {
return p.parseYieldExpression()
}
// Then, check if we have an arrow function (production '4' and '5') that starts with a parenthesized
// parameter list or is an async arrow function.
// AsyncArrowFunctionExpression:
// 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In]
// 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In]
// Production (1) of AsyncArrowFunctionExpression is parsed in "tryParseAsyncSimpleArrowFunctionExpression".
// And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression".
//
// If we do successfully parse arrow-function, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is
// not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done
// with AssignmentExpression if we see one.
arrowExpression := p.tryParseParenthesizedArrowFunctionExpression(allowReturnTypeInArrowFunction)
if arrowExpression != nil {
return arrowExpression
}
arrowExpression = p.tryParseAsyncSimpleArrowFunctionExpression(allowReturnTypeInArrowFunction)
if arrowExpression != nil {
return arrowExpression
}
// arrowExpression2 := p.tryParseAsyncSimpleArrowFunctionExpression(allowReturnTypeInArrowFunction)
// if arrowExpression2 != nil {
// return arrowExpression2
// }
// Now try to see if we're in production '1', '2' or '3'. A conditional expression can
// start with a LogicalOrExpression, while the assignment productions can only start with
// LeftHandSideExpressions.
//
// So, first, we try to just parse out a BinaryExpression. If we get something that is a
// LeftHandSide or higher, then we can try to parse out the assignment expression part.
// Otherwise, we try to parse out the conditional expression bit. We want to allow any
// binary expression here, so we pass in the 'lowest' precedence here so that it matches
// and consumes anything.
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
expr := p.parseBinaryExpressionOrHigher(ast.OperatorPrecedenceLowest)
// To avoid a look-ahead, we did not handle the case of an arrow function with a single un-parenthesized
// parameter ('x => ...') above. We handle it here by checking if the parsed expression was a single
// identifier and the current token is an arrow.
if expr.Kind == ast.KindIdentifier && p.token == ast.KindEqualsGreaterThanToken {
return p.parseSimpleArrowFunctionExpression(pos, expr, allowReturnTypeInArrowFunction, hasJSDoc, nil /*asyncModifier*/)
}
// Now see if we might be in cases '2' or '3'.
// If the expression was a LHS expression, and we have an assignment operator, then
// we're in '2' or '3'. Consume the assignment and return.
//
// Note: we call reScanGreaterToken so that we get an appropriately merged token
// for cases like `> > =` becoming `>>=`
if ast.IsLeftHandSideExpression(expr) && ast.IsAssignmentOperator(p.reScanGreaterThanToken()) {
return p.makeBinaryExpression(expr, p.parseTokenNode(), p.parseAssignmentExpressionOrHigherWorker(allowReturnTypeInArrowFunction), pos)
}
// It wasn't an assignment or a lambda. This is a conditional expression:
return p.parseConditionalExpressionRest(expr, pos, allowReturnTypeInArrowFunction)
}
func (p *Parser) isYieldExpression() bool {
if p.token == ast.KindYieldKeyword {
// If we have a 'yield' keyword, and this is a context where yield expressions are
// allowed, then definitely parse out a yield expression.
if p.inYieldContext() {
return true
}
// We're in a context where 'yield expr' is not allowed. However, if we can
// definitely tell that the user was trying to parse a 'yield expr' and not
// just a normal expr that start with a 'yield' identifier, then parse out
// a 'yield expr'. We can then report an error later that they are only
// allowed in generator expressions.
//
// for example, if we see 'yield(foo)', then we'll have to treat that as an
// invocation expression of something called 'yield'. However, if we have
// 'yield foo' then that is not legal as a normal expression, so we can
// definitely recognize this as a yield expression.
//
// for now we just check if the next token is an identifier. More heuristics
// can be added here later as necessary. We just need to make sure that we
// don't accidentally consume something legal.
return p.lookAhead((*Parser).nextTokenIsIdentifierOrKeywordOrLiteralOnSameLine)
}
return false
}
func (p *Parser) parseYieldExpression() *ast.Node {
pos := p.nodePos()
// YieldExpression[In] :
// yield
// yield [no LineTerminator here] [Lexical goal InputElementRegExp]AssignmentExpression[?In, Yield]
// yield [no LineTerminator here] * [Lexical goal InputElementRegExp]AssignmentExpression[?In, Yield]
p.nextToken()
var result *ast.Node
if !p.hasPrecedingLineBreak() && (p.token == ast.KindAsteriskToken || p.isStartOfExpression()) {
result = p.factory.NewYieldExpression(p.parseOptionalToken(ast.KindAsteriskToken), p.parseAssignmentExpressionOrHigher())
} else {
// if the next token is not on the same line as yield. or we don't have an '*' or
// the start of an expression, then this is just a simple "yield" expression.
result = p.factory.NewYieldExpression(nil /*asteriskToken*/, nil /*expression*/)
}
return p.finishNode(result, pos)
}
func (p *Parser) isParenthesizedArrowFunctionExpression() core.Tristate {
if p.token == ast.KindOpenParenToken || p.token == ast.KindLessThanToken || p.token == ast.KindAsyncKeyword {
state := p.mark()
result := p.nextIsParenthesizedArrowFunctionExpression()
p.rewind(state)
return result
}
if p.token == ast.KindEqualsGreaterThanToken {
// ERROR RECOVERY TWEAK:
// If we see a standalone => try to parse it as an arrow function expression as that's
// likely what the user intended to write.
return core.TSTrue
}
// Definitely not a parenthesized arrow function.
return core.TSFalse
}
func (p *Parser) nextIsParenthesizedArrowFunctionExpression() core.Tristate {
if p.token == ast.KindAsyncKeyword {
p.nextToken()
if p.hasPrecedingLineBreak() {
return core.TSFalse
}
if p.token != ast.KindOpenParenToken && p.token != ast.KindLessThanToken {
return core.TSFalse
}
}
first := p.token
second := p.nextToken()
if first == ast.KindOpenParenToken {
if second == ast.KindCloseParenToken {
// Simple cases: "() =>", "(): ", and "() {".
// This is an arrow function with no parameters.
// The last one is not actually an arrow function,
// but this is probably what the user intended.
third := p.nextToken()
switch third {
case ast.KindEqualsGreaterThanToken, ast.KindColonToken, ast.KindOpenBraceToken:
return core.TSTrue
}
return core.TSFalse
}
// If encounter "([" or "({", this could be the start of a binding pattern.
// Examples:
// ([ x ]) => { }
// ({ x }) => { }
// ([ x ])
// ({ x })
if second == ast.KindOpenBracketToken || second == ast.KindOpenBraceToken {
return core.TSUnknown
}
// Simple case: "(..."
// This is an arrow function with a rest parameter.
if second == ast.KindDotDotDotToken {
return core.TSTrue
}
// Check for "(xxx yyy", where xxx is a modifier and yyy is an identifier. This
// isn't actually allowed, but we want to treat it as a lambda so we can provide
// a good error message.
if ast.IsModifierKind(second) && second != ast.KindAsyncKeyword && p.lookAhead((*Parser).nextTokenIsIdentifier) {
if p.nextToken() == ast.KindAsKeyword {
// https://github.com/microsoft/TypeScript/issues/44466
return core.TSFalse
}
return core.TSTrue
}
// If we had "(" followed by something that's not an identifier,
// then this definitely doesn't look like a lambda. "this" is not
// valid, but we want to parse it and then give a semantic error.
if !p.isIdentifier() && second != ast.KindThisKeyword {
return core.TSFalse
}
switch p.nextToken() {
case ast.KindColonToken:
// If we have something like "(a:", then we must have a
// type-annotated parameter in an arrow function expression.
return core.TSTrue
case ast.KindQuestionToken:
p.nextToken()
// If we have "(a?:" or "(a?," or "(a?=" or "(a?)" then it is definitely a lambda.
if p.token == ast.KindColonToken || p.token == ast.KindCommaToken || p.token == ast.KindEqualsToken || p.token == ast.KindCloseParenToken {
return core.TSTrue
}
// Otherwise it is definitely not a lambda.
return core.TSFalse
case ast.KindCommaToken, ast.KindEqualsToken, ast.KindCloseParenToken:
// If we have "(a," or "(a=" or "(a)" this *could* be an arrow function
return core.TSUnknown
}
// It is definitely not an arrow function
return core.TSFalse
} else {
debug.Assert(first == ast.KindLessThanToken)
// If we have "<" not followed by an identifier,
// then this definitely is not an arrow function.
if !p.isIdentifier() && p.token != ast.KindConstKeyword {
return core.TSFalse
}
// JSX overrides
if p.languageVariant == core.LanguageVariantJSX {
isArrowFunctionInJsx := p.lookAhead(func(p *Parser) bool {
p.parseOptional(ast.KindConstKeyword)
third := p.nextToken()
if third == ast.KindExtendsKeyword {
fourth := p.nextToken()
switch fourth {
case ast.KindEqualsToken, ast.KindGreaterThanToken, ast.KindSlashToken:
return false
}
return true
} else if third == ast.KindCommaToken || third == ast.KindEqualsToken {
return true
}
return false
})
if isArrowFunctionInJsx {
return core.TSTrue
}
return core.TSFalse
}
// This *could* be a parenthesized arrow function.
return core.TSUnknown
}
}
func (p *Parser) tryParseParenthesizedArrowFunctionExpression(allowReturnTypeInArrowFunction bool) *ast.Node {
tristate := p.isParenthesizedArrowFunctionExpression()
if tristate == core.TSFalse {
// It's definitely not a parenthesized arrow function expression.
return nil
}
// If we definitely have an arrow function, then we can just parse one, not requiring a
// following => or { token. Otherwise, we *might* have an arrow function. Try to parse
// it out, but don't allow any ambiguity, and return 'undefined' if this could be an
// expression instead.
if tristate == core.TSTrue {
return p.parseParenthesizedArrowFunctionExpression(true /*allowAmbiguity*/, true /*allowReturnTypeInArrowFunction*/)
}
state := p.mark()
result := p.parsePossibleParenthesizedArrowFunctionExpression(allowReturnTypeInArrowFunction)
if result == nil {
p.rewind(state)
}
return result
}
func (p *Parser) parseParenthesizedArrowFunctionExpression(allowAmbiguity bool, allowReturnTypeInArrowFunction bool) *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
modifiers := p.parseModifiersForArrowFunction()
isAsync := modifierListHasAsync(modifiers)
signatureFlags := core.IfElse(isAsync, ParseFlagsAwait, ParseFlagsNone)
// Arrow functions are never generators.
//
// If we're speculatively parsing a signature for a parenthesized arrow function, then
// we have to have a complete parameter list. Otherwise we might see something like
// a => (b => c)
// And think that "(b =>" was actually a parenthesized arrow function with a missing
// close paren.
typeParameters := p.parseTypeParameters()
var parameters *ast.NodeList
if !p.parseExpected(ast.KindOpenParenToken) {
if !allowAmbiguity {
return nil
}
parameters = p.parseEmptyNodeList()
} else {
if !allowAmbiguity {
maybeParameters := p.parseParametersWorker(signatureFlags, allowAmbiguity)
if maybeParameters == nil {
return nil
}
parameters = maybeParameters
} else {
parameters = p.parseParametersWorker(signatureFlags, allowAmbiguity)
}
if !p.parseExpected(ast.KindCloseParenToken) && !allowAmbiguity {
return nil
}
}
hasReturnColon := p.token == ast.KindColonToken
returnType := p.parseReturnType(ast.KindColonToken /*isType*/, false)
if returnType != nil && !allowAmbiguity && typeHasArrowFunctionBlockingParseError(returnType) {
return nil
}
// Parsing a signature isn't enough.
// Parenthesized arrow signatures often look like other valid expressions.
// For instance:
// - "(x = 10)" is an assignment expression parsed as a signature with a default parameter value.
// - "(x,y)" is a comma expression parsed as a signature with two parameters.
// - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation.
// - "a ? (b): function() {}" will too, since function() is a valid JSDoc function type.
// - "a ? (b): (function() {})" as well, but inside of a parenthesized type with an arbitrary amount of nesting.
//
// So we need just a bit of lookahead to ensure that it can only be a signature.
unwrappedType := returnType
for unwrappedType != nil && unwrappedType.Kind == ast.KindParenthesizedType {
unwrappedType = unwrappedType.AsParenthesizedTypeNode().Type // Skip parens if need be
}
if !allowAmbiguity && p.token != ast.KindEqualsGreaterThanToken && p.token != ast.KindOpenBraceToken {
// Returning undefined here will cause our caller to rewind to where we started from.
return nil
}
// If we have an arrow, then try to parse the body. Even if not, try to parse if we
// have an opening brace, just in case we're in an error state.
lastToken := p.token
equalsGreaterThanToken := p.parseExpectedToken(ast.KindEqualsGreaterThanToken)
var body *ast.Node
if lastToken == ast.KindEqualsGreaterThanToken || lastToken == ast.KindOpenBraceToken {
body = p.parseArrowFunctionExpressionBody(isAsync, allowReturnTypeInArrowFunction)
} else {
body = p.parseIdentifier()
}
// Given:
// x ? y => ({ y }) : z => ({ z })
// We try to parse the body of the first arrow function by looking at:
// ({ y }) : z => ({ z })
// This is a valid arrow function with "z" as the return type.
//
// But, if we're in the true side of a conditional expression, this colon
// terminates the expression, so we cannot allow a return type if we aren't
// certain whether or not the preceding text was parsed as a parameter list.
//
// For example,
// a() ? (b: number, c?: string): void => d() : e
// is determined by isParenthesizedArrowFunctionExpression to unambiguously
// be an arrow expression, so we allow a return type.
if !allowReturnTypeInArrowFunction && hasReturnColon {
// However, if the arrow function we were able to parse is followed by another colon
// as in:
// a ? (x): string => x : null
// Then allow the arrow function, and treat the second colon as terminating
// the conditional expression. It's okay to do this because this code would
// be a syntax error in JavaScript (as the second colon shouldn't be there).
if p.token != ast.KindColonToken {
return nil
}
}
result := p.finishNode(p.factory.NewArrowFunction(modifiers, typeParameters, parameters, returnType, nil /*fullSignature*/, equalsGreaterThanToken, body), pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func (p *Parser) parseModifiersForArrowFunction() *ast.ModifierList {
if p.token == ast.KindAsyncKeyword {
pos := p.nodePos()
p.nextToken()
modifier := p.finishNode(p.factory.NewModifier(ast.KindAsyncKeyword), pos)
return p.newModifierList(modifier.Loc, p.nodeSlicePool.NewSlice1(modifier))
}
return nil
}
// If true, we should abort parsing an error function.
func typeHasArrowFunctionBlockingParseError(node *ast.TypeNode) bool {
switch node.Kind {
case ast.KindTypeReference:
return ast.NodeIsMissing(node.AsTypeReference().TypeName)
case ast.KindFunctionType, ast.KindConstructorType:
return typeHasArrowFunctionBlockingParseError(node.Type())
case ast.KindParenthesizedType:
return typeHasArrowFunctionBlockingParseError(node.AsParenthesizedTypeNode().Type)
}
return false
}
func (p *Parser) parseArrowFunctionExpressionBody(isAsync bool, allowReturnTypeInArrowFunction bool) *ast.Node {
if p.token == ast.KindOpenBraceToken {
return p.parseFunctionBlock(core.IfElse(isAsync, ParseFlagsAwait, ParseFlagsNone), nil /*diagnosticMessage*/)
}
if p.token != ast.KindSemicolonToken && p.token != ast.KindFunctionKeyword && p.token != ast.KindClassKeyword && p.isStartOfStatement() && !p.isStartOfExpressionStatement() {
// Check if we got a plain statement (i.e. no expression-statements, no function/class expressions/declarations)
//
// Here we try to recover from a potential error situation in the case where the
// user meant to supply a block. For example, if the user wrote:
//
// a =>
// let v = 0;
// }
//
// they may be missing an open brace. Check to see if that's the case so we can
// try to recover better. If we don't do this, then the next close curly we see may end
// up preemptively closing the containing construct.
//
// Note: even when 'IgnoreMissingOpenBrace' is passed, parseBody will still error.
return p.parseFunctionBlock(ParseFlagsIgnoreMissingOpenBrace|core.IfElse(isAsync, ParseFlagsAwait, ParseFlagsNone), nil /*diagnosticMessage*/)
}
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsAwaitContext, isAsync)
p.setContextFlags(ast.NodeFlagsYieldContext, false)
node := p.parseAssignmentExpressionOrHigherWorker(allowReturnTypeInArrowFunction)
p.contextFlags = saveContextFlags
return node
}
func (p *Parser) isStartOfExpressionStatement() bool {
// As per the grammar, none of '{' or 'function' or 'class' can start an expression statement.
return p.token != ast.KindOpenBraceToken && p.token != ast.KindFunctionKeyword && p.token != ast.KindClassKeyword && p.token != ast.KindAtToken && p.isStartOfExpression()
}
func (p *Parser) parsePossibleParenthesizedArrowFunctionExpression(allowReturnTypeInArrowFunction bool) *ast.Node {
tokenPos := p.scanner.TokenStart()
if p.notParenthesizedArrow.Has(tokenPos) {
return nil
}
result := p.parseParenthesizedArrowFunctionExpression(false /*allowAmbiguity*/, allowReturnTypeInArrowFunction)
if result == nil {
p.notParenthesizedArrow.Add(tokenPos)
}
return result
}
func (p *Parser) tryParseAsyncSimpleArrowFunctionExpression(allowReturnTypeInArrowFunction bool) *ast.Node {
// We do a check here so that we won't be doing unnecessarily call to "lookAhead"
if p.token == ast.KindAsyncKeyword && p.lookAhead((*Parser).nextIsUnParenthesizedAsyncArrowFunction) {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
asyncModifier := p.parseModifiersForArrowFunction()
expr := p.parseBinaryExpressionOrHigher(ast.OperatorPrecedenceLowest)
return p.parseSimpleArrowFunctionExpression(pos, expr, allowReturnTypeInArrowFunction, hasJSDoc, asyncModifier)
}
return nil
}
func (p *Parser) nextIsUnParenthesizedAsyncArrowFunction() bool {
// AsyncArrowFunctionExpression:
// 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In]
// 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In]
if p.token == ast.KindAsyncKeyword {
p.nextToken()
// If the "async" is followed by "=>" token then it is not a beginning of an async arrow-function
// but instead a simple arrow-function which will be parsed inside "parseAssignmentExpressionOrHigher"
if p.hasPrecedingLineBreak() || p.token == ast.KindEqualsGreaterThanToken {
return false
}
// Check for un-parenthesized AsyncArrowFunction
expr := p.parseBinaryExpressionOrHigher(ast.OperatorPrecedenceLowest)
if !p.hasPrecedingLineBreak() && expr.Kind == ast.KindIdentifier && p.token == ast.KindEqualsGreaterThanToken {
return true
}
}
return false
}
func (p *Parser) parseSimpleArrowFunctionExpression(pos int, identifier *ast.Node, allowReturnTypeInArrowFunction bool, hasJSDoc bool, asyncModifier *ast.ModifierList) *ast.Node {
debug.Assert(p.token == ast.KindEqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>")
parameter := p.finishNode(p.factory.NewParameterDeclaration(nil /*modifiers*/, nil /*dotDotDotToken*/, identifier, nil /*questionToken*/, nil /*typeNode*/, nil /*initializer*/), identifier.Pos())
parameters := p.newNodeList(parameter.Loc, []*ast.Node{parameter})
equalsGreaterThanToken := p.parseExpectedToken(ast.KindEqualsGreaterThanToken)
body := p.parseArrowFunctionExpressionBody(asyncModifier != nil /*isAsync*/, allowReturnTypeInArrowFunction)
result := p.finishNode(p.factory.NewArrowFunction(asyncModifier, nil /*typeParameters*/, parameters, nil /*returnType*/, nil /*fullSignature*/, equalsGreaterThanToken, body), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseConditionalExpressionRest(leftOperand *ast.Expression, pos int, allowReturnTypeInArrowFunction bool) *ast.Expression {
// Note: we are passed in an expression which was produced from parseBinaryExpressionOrHigher.
questionToken := p.parseOptionalToken(ast.KindQuestionToken)
if questionToken == nil {
return leftOperand
}
// Note: we explicitly 'allowIn' in the whenTrue part of the condition expression, and
// we do not that for the 'whenFalse' part.
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsDisallowInContext, false)
trueExpression := p.parseAssignmentExpressionOrHigherWorker(false /*allowReturnTypeInArrowFunction*/)
p.contextFlags = saveContextFlags
colonToken := p.parseExpectedToken(ast.KindColonToken)
var falseExpression *ast.Expression
if colonToken != nil {
falseExpression = p.parseAssignmentExpressionOrHigherWorker(allowReturnTypeInArrowFunction)
} else {
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(ast.KindColonToken))
falseExpression = p.createMissingIdentifier()
}
return p.finishNode(p.factory.NewConditionalExpression(leftOperand, questionToken, trueExpression, colonToken, falseExpression), pos)
}
func (p *Parser) parseBinaryExpressionOrHigher(precedence ast.OperatorPrecedence) *ast.Expression {
pos := p.nodePos()
leftOperand := p.parseUnaryExpressionOrHigher()
return p.parseBinaryExpressionRest(precedence, leftOperand, pos)
}
func (p *Parser) parseBinaryExpressionRest(precedence ast.OperatorPrecedence, leftOperand *ast.Expression, pos int) *ast.Expression {
for {
// We either have a binary operator here, or we're finished. We call
// reScanGreaterToken so that we merge token sequences like > and = into >=
p.reScanGreaterThanToken()
newPrecedence := ast.GetBinaryOperatorPrecedence(p.token)
// Check the precedence to see if we should "take" this operator
// - For left associative operator (all operator but **), consume the operator,
// recursively call the function below, and parse binaryExpression as a rightOperand
// of the caller if the new precedence of the operator is greater then or equal to the current precedence.
// For example:
// a - b - c;
// ^token; leftOperand = b. Return b to the caller as a rightOperand
// a * b - c
// ^token; leftOperand = b. Return b to the caller as a rightOperand
// a - b * c;
// ^token; leftOperand = b. Return b * c to the caller as a rightOperand
// - For right associative operator (**), consume the operator, recursively call the function
// and parse binaryExpression as a rightOperand of the caller if the new precedence of
// the operator is strictly grater than the current precedence
// For example:
// a ** b ** c;
// ^^token; leftOperand = b. Return b ** c to the caller as a rightOperand
// a - b ** c;
// ^^token; leftOperand = b. Return b ** c to the caller as a rightOperand
// a ** b - c
// ^token; leftOperand = b. Return b to the caller as a rightOperand
var consumeCurrentOperator bool
if p.token == ast.KindAsteriskAsteriskToken {
consumeCurrentOperator = newPrecedence >= precedence
} else {
consumeCurrentOperator = newPrecedence > precedence
}
if !consumeCurrentOperator {
break
}
if p.token == ast.KindInKeyword && p.inDisallowInContext() {
break
}
if p.token == ast.KindAsKeyword || p.token == ast.KindSatisfiesKeyword {
// Make sure we *do* perform ASI for constructs like this:
// var x = foo
// as (Bar)
// This should be parsed as an initialized variable, followed
// by a function call to 'as' with the argument 'Bar'
if p.hasPrecedingLineBreak() {
break
} else {
keywordKind := p.token
p.nextToken()
if keywordKind == ast.KindSatisfiesKeyword {
leftOperand = p.makeSatisfiesExpression(leftOperand, p.parseType())
} else {
leftOperand = p.makeAsExpression(leftOperand, p.parseType())
}
}
} else {
leftOperand = p.makeBinaryExpression(leftOperand, p.parseTokenNode(), p.parseBinaryExpressionOrHigher(newPrecedence), pos)
}
}
return leftOperand
}
func (p *Parser) makeSatisfiesExpression(expression *ast.Expression, typeNode *ast.TypeNode) *ast.Node {
return p.checkJSSyntax(p.finishNode(p.factory.NewSatisfiesExpression(expression, typeNode), expression.Pos()))
}
func (p *Parser) makeAsExpression(left *ast.Expression, right *ast.TypeNode) *ast.Node {
return p.checkJSSyntax(p.finishNode(p.factory.NewAsExpression(left, right), left.Pos()))
}
func (p *Parser) makeBinaryExpression(left *ast.Expression, operatorToken *ast.Node, right *ast.Expression, pos int) *ast.Node {
return p.finishNode(p.factory.NewBinaryExpression(nil /*modifiers*/, left, nil /*typeNode*/, operatorToken, right), pos)
}
func (p *Parser) parseUnaryExpressionOrHigher() *ast.Expression {
// ES7 UpdateExpression:
// 1) LeftHandSideExpression[?Yield]
// 2) LeftHandSideExpression[?Yield][no LineTerminator here]++
// 3) LeftHandSideExpression[?Yield][no LineTerminator here]--
// 4) ++UnaryExpression[?Yield]
// 5) --UnaryExpression[?Yield]
if p.isUpdateExpression() {
pos := p.nodePos()
updateExpression := p.parseUpdateExpression()
if p.token == ast.KindAsteriskAsteriskToken {
return p.parseBinaryExpressionRest(ast.GetBinaryOperatorPrecedence(p.token), updateExpression, pos)
}
return updateExpression
}
// ES7 UnaryExpression:
// 1) UpdateExpression[?yield]
// 2) delete UpdateExpression[?yield]
// 3) void UpdateExpression[?yield]
// 4) typeof UpdateExpression[?yield]
// 5) + UpdateExpression[?yield]
// 6) - UpdateExpression[?yield]
// 7) ~ UpdateExpression[?yield]
// 8) ! UpdateExpression[?yield]
unaryOperator := p.token
simpleUnaryExpression := p.parseSimpleUnaryExpression()
if p.token == ast.KindAsteriskAsteriskToken {
pos := scanner.SkipTrivia(p.sourceText, simpleUnaryExpression.Pos())
end := simpleUnaryExpression.End()
if simpleUnaryExpression.Kind == ast.KindTypeAssertionExpression {
p.parseErrorAt(pos, end, diagnostics.A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses)
} else {
debug.Assert(isKeywordOrPunctuation(unaryOperator))
p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, scanner.TokenToString(unaryOperator))
}
}
return simpleUnaryExpression
}
func (p *Parser) isUpdateExpression() bool {
switch p.token {
case ast.KindPlusToken, ast.KindMinusToken, ast.KindTildeToken, ast.KindExclamationToken, ast.KindDeleteKeyword, ast.KindTypeOfKeyword, ast.KindVoidKeyword, ast.KindAwaitKeyword:
return false
case ast.KindLessThanToken:
return p.languageVariant == core.LanguageVariantJSX
}
return true
}
func (p *Parser) parseUpdateExpression() *ast.Expression {
pos := p.nodePos()
if p.token == ast.KindPlusPlusToken || p.token == ast.KindMinusMinusToken {
operator := p.token
p.nextToken()
return p.finishNode(p.factory.NewPrefixUnaryExpression(operator, p.parseLeftHandSideExpressionOrHigher()), pos)
} else if p.languageVariant == core.LanguageVariantJSX && p.token == ast.KindLessThanToken && p.lookAhead((*Parser).nextTokenIsIdentifierOrKeywordOrGreaterThan) {
// JSXElement is part of primaryExpression
return p.parseJsxElementOrSelfClosingElementOrFragment(true /*inExpressionContext*/, -1 /*topInvalidNodePosition*/, nil /*openingTag*/, false /*mustBeUnary*/)
}
expression := p.parseLeftHandSideExpressionOrHigher()
if (p.token == ast.KindPlusPlusToken || p.token == ast.KindMinusMinusToken) && !p.hasPrecedingLineBreak() {
operator := p.token
p.nextToken()
return p.finishNode(p.factory.NewPostfixUnaryExpression(expression, operator), pos)
}
return expression
}
func (p *Parser) parseJsxElementOrSelfClosingElementOrFragment(inExpressionContext bool, topInvalidNodePosition int, openingTag *ast.Node, mustBeUnary bool) *ast.Expression {
pos := p.nodePos()
opening := p.parseJsxOpeningOrSelfClosingElementOrOpeningFragment(inExpressionContext)
var result *ast.Expression
switch opening.Kind {
case ast.KindJsxOpeningElement:
children := p.parseJsxChildren(opening)
var closingElement *ast.Node
lastChild := core.LastOrNil(children.Nodes)
if lastChild != nil && lastChild.Kind == ast.KindJsxElement &&
!tagNamesAreEquivalent(lastChild.AsJsxElement().OpeningElement.AsJsxOpeningElement().TagName, lastChild.AsJsxElement().ClosingElement.AsJsxClosingElement().TagName) &&
tagNamesAreEquivalent(opening.AsJsxOpeningElement().TagName, lastChild.AsJsxElement().ClosingElement.AsJsxClosingElement().TagName) {
// when an unclosed JsxOpeningElement incorrectly parses its parent's JsxClosingElement,
// restructure (<div>(...<span>...</div>)) --> (<div>(...<span>...</>)</div>)
// (no need to error; the parent will error)
end := lastChild.AsJsxElement().OpeningElement.End()
missingIdentifier := p.finishNodeWithEnd(p.newIdentifier(""), end, end)
newClosingElement := p.finishNodeWithEnd(p.factory.NewJsxClosingElement(missingIdentifier), end, end)
newLast := p.finishNodeWithEnd(
p.factory.NewJsxElement(lastChild.AsJsxElement().OpeningElement, lastChild.AsJsxElement().Children, newClosingElement),
lastChild.AsJsxElement().OpeningElement.Pos(),
end,
)
// force reset parent pointers from discarded parse result
if lastChild.AsJsxElement().OpeningElement != nil {
lastChild.AsJsxElement().OpeningElement.Parent = newLast
}
if lastChild.AsJsxElement().Children != nil {
for _, c := range lastChild.AsJsxElement().Children.Nodes {
c.Parent = newLast
}
}
newClosingElement.Parent = newLast
children = p.newNodeList(core.NewTextRange(children.Pos(), newLast.End()), append(children.Nodes[0:len(children.Nodes)-1], newLast))
closingElement = lastChild.AsJsxElement().ClosingElement
} else {
closingElement = p.parseJsxClosingElement(opening, inExpressionContext)
if !tagNamesAreEquivalent(opening.AsJsxOpeningElement().TagName, closingElement.AsJsxClosingElement().TagName) {
if openingTag != nil && ast.IsJsxOpeningElement(openingTag) && tagNamesAreEquivalent(closingElement.AsJsxClosingElement().TagName, openingTag.AsJsxOpeningElement().TagName) {
// opening incorrectly matched with its parent's closing -- put error on opening
p.parseErrorAtRange(opening.AsJsxOpeningElement().TagName.Loc, diagnostics.JSX_element_0_has_no_corresponding_closing_tag, scanner.GetTextOfNodeFromSourceText(p.sourceText, opening.AsJsxOpeningElement().TagName, false /*includeTrivia*/))
} else {
// other opening/closing mismatches -- put error on closing
p.parseErrorAtRange(closingElement.AsJsxClosingElement().TagName.Loc, diagnostics.Expected_corresponding_JSX_closing_tag_for_0, scanner.GetTextOfNodeFromSourceText(p.sourceText, opening.AsJsxOpeningElement().TagName, false /*includeTrivia*/))
}
}
}
result = p.finishNode(p.factory.NewJsxElement(opening, children, closingElement), pos)
closingElement.Parent = result // force reset parent pointers from possibly discarded parse result
case ast.KindJsxOpeningFragment:
result = p.finishNode(p.factory.NewJsxFragment(opening, p.parseJsxChildren(opening), p.parseJsxClosingFragment(inExpressionContext)), pos)
case ast.KindJsxSelfClosingElement:
// Nothing else to do for self-closing elements
result = opening
default:
panic("Unhandled case in parseJsxElementOrSelfClosingElementOrFragment")
}
// If the user writes the invalid code '<div></div><div></div>' in an expression context (i.e. not wrapped in
// an enclosing tag), we'll naively try to parse ^ this as a 'less than' operator and the remainder of the tag
// as garbage, which will cause the formatter to badly mangle the JSX. Perform a speculative parse of a JSX
// element if we see a < token so that we can wrap it in a synthetic binary expression so the formatter
// does less damage and we can report a better error.
// Since JSX elements are invalid < operands anyway, this lookahead parse will only occur in error scenarios
// of one sort or another.
// If we are in a unary context, we can't do this recovery; the binary expression we return here is not
// a valid UnaryExpression and will cause problems later.
if !mustBeUnary && inExpressionContext && p.token == ast.KindLessThanToken {
topBadPos := topInvalidNodePosition
if topBadPos < 0 {
topBadPos = result.Pos()
}
invalidElement := p.parseJsxElementOrSelfClosingElementOrFragment( /*inExpressionContext*/ true, topBadPos, nil, false)
operatorToken := p.factory.NewToken(ast.KindCommaToken)
operatorToken.Loc = core.NewTextRange(invalidElement.Pos(), invalidElement.Pos())
p.parseErrorAt(scanner.SkipTrivia(p.sourceText, topBadPos), invalidElement.End(), diagnostics.JSX_expressions_must_have_one_parent_element)
result = p.finishNode(p.factory.NewBinaryExpression(nil /*modifiers*/, result, nil /*typeNode*/, operatorToken, invalidElement), pos)
}
return result
}
func (p *Parser) parseJsxChildren(openingTag *ast.Expression) *ast.NodeList {
pos := p.nodePos()
saveParsingContexts := p.parsingContexts
p.parsingContexts |= 1 << PCJsxChildren
var list []*ast.Node
for {
currentToken := p.scanner.ReScanJsxToken(true /*allowMultilineJsxText*/)
child := p.parseJsxChild(openingTag, currentToken)
if child == nil {
break
}
list = append(list, child)
if ast.IsJsxOpeningElement(openingTag) && child.Kind == ast.KindJsxElement &&
!tagNamesAreEquivalent(child.AsJsxElement().OpeningElement.AsJsxOpeningElement().TagName, child.AsJsxElement().ClosingElement.AsJsxClosingElement().TagName) &&
tagNamesAreEquivalent(openingTag.AsJsxOpeningElement().TagName, child.AsJsxElement().ClosingElement.AsJsxClosingElement().TagName) {
// stop after parsing a mismatched child like <div>...(<span></div>) in order to reattach the </div> higher
break
}
}
p.parsingContexts = saveParsingContexts
return p.newNodeList(core.NewTextRange(pos, p.nodePos()), list)
}
func (p *Parser) parseJsxChild(openingTag *ast.Node, token ast.Kind) *ast.Expression {
switch token {
case ast.KindEndOfFile:
// If we hit EOF, issue the error at the tag that lacks the closing element
// rather than at the end of the file (which is useless)
if ast.IsJsxOpeningFragment(openingTag) {
p.parseErrorAtRange(openingTag.Loc, diagnostics.JSX_fragment_has_no_corresponding_closing_tag)
} else {
// We want the error span to cover only 'Foo.Bar' in < Foo.Bar >
// or to cover only 'Foo' in < Foo >
tag := openingTag.AsJsxOpeningElement().TagName
start := min(scanner.SkipTrivia(p.sourceText, tag.Pos()), tag.End())
p.parseErrorAt(start, tag.End(), diagnostics.JSX_element_0_has_no_corresponding_closing_tag,
scanner.GetTextOfNodeFromSourceText(p.sourceText, openingTag.AsJsxOpeningElement().TagName, false /*includeTrivia*/))
}
return nil
case ast.KindLessThanSlashToken, ast.KindConflictMarkerTrivia:
return nil
case ast.KindJsxText, ast.KindJsxTextAllWhiteSpaces:
return p.parseJsxText()
case ast.KindOpenBraceToken:
return p.parseJsxExpression(false /*inExpressionContext*/)
case ast.KindLessThanToken:
return p.parseJsxElementOrSelfClosingElementOrFragment(false /*inExpressionContext*/, -1 /*topInvalidNodePosition*/, openingTag, false)
}
panic("Unhandled case in parseJsxChild")
}
func (p *Parser) parseJsxText() *ast.Node {
pos := p.nodePos()
result := p.factory.NewJsxText(p.scanner.TokenValue(), p.token == ast.KindJsxTextAllWhiteSpaces)
p.scanJsxText()
return p.finishNode(result, pos)
}
func (p *Parser) parseJsxExpression(inExpressionContext bool) *ast.Node {
pos := p.nodePos()
if !p.parseExpected(ast.KindOpenBraceToken) {
return nil
}
var dotDotDotToken *ast.Node
var expression *ast.Expression
if p.token != ast.KindCloseBraceToken {
if !inExpressionContext {
dotDotDotToken = p.parseOptionalToken(ast.KindDotDotDotToken)
}
// Only an AssignmentExpression is valid here per the JSX spec,
// but we can unambiguously parse a comma sequence and provide
// a better error message in grammar checking.
expression = p.parseExpression()
}
if inExpressionContext {
p.parseExpected(ast.KindCloseBraceToken)
} else if p.parseExpectedWithoutAdvancing(ast.KindCloseBraceToken) {
p.scanJsxText()
}
return p.finishNode(p.factory.NewJsxExpression(dotDotDotToken, expression), pos)
}
func (p *Parser) scanJsxText() ast.Kind {
p.token = p.scanner.ScanJsxToken()
return p.token
}
func (p *Parser) scanJsxIdentifier() ast.Kind {
p.token = p.scanner.ScanJsxIdentifier()
return p.token
}
func (p *Parser) scanJsxAttributeValue() ast.Kind {
p.token = p.scanner.ScanJsxAttributeValue()
return p.token
}
func (p *Parser) parseJsxClosingElement(open *ast.Node, inExpressionContext bool) *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindLessThanSlashToken)
tagName := p.parseJsxElementName()
if p.parseExpectedWithDiagnostic(ast.KindGreaterThanToken, nil /*diagnosticMessage*/, false /*shouldAdvance*/) {
// manually advance the scanner in order to look for jsx text inside jsx
if inExpressionContext || !tagNamesAreEquivalent(open.AsJsxOpeningElement().TagName, tagName) {
p.nextToken()
} else {
p.scanJsxText()
}
}
return p.finishNode(p.factory.NewJsxClosingElement(tagName), pos)
}
func (p *Parser) parseJsxOpeningOrSelfClosingElementOrOpeningFragment(inExpressionContext bool) *ast.Expression {
pos := p.nodePos()
p.parseExpected(ast.KindLessThanToken)
if p.token == ast.KindGreaterThanToken {
// See below for explanation of scanJsxText
p.scanJsxText()
return p.finishNode(p.factory.NewJsxOpeningFragment(), pos)
}
tagName := p.parseJsxElementName()
var typeArguments *ast.NodeList
if p.contextFlags&ast.NodeFlagsJavaScriptFile == 0 {
typeArguments = p.parseTypeArguments()
}
attributes := p.parseJsxAttributes()
var result *ast.Expression
if p.token == ast.KindGreaterThanToken {
// Closing tag, so scan the immediately-following text with the JSX scanning instead
// of regular scanning to avoid treating illegal characters (e.g. '#') as immediate
// scanning errors
p.scanJsxText()
result = p.factory.NewJsxOpeningElement(tagName, typeArguments, attributes)
} else {
p.parseExpected(ast.KindSlashToken)
if p.parseExpectedWithoutAdvancing(ast.KindGreaterThanToken) {
if inExpressionContext {
p.nextToken()
} else {
p.scanJsxText()
}
}
result = p.factory.NewJsxSelfClosingElement(tagName, typeArguments, attributes)
}
return p.finishNode(result, pos)
}
func (p *Parser) parseJsxElementName() *ast.Expression {
pos := p.nodePos()
// JsxElement can have name in the form of
// propertyAccessExpression
// primaryExpression in the form of an identifier and "this" keyword
// We can't just simply use parseLeftHandSideExpressionOrHigher because then we will start consider class,function etc as a keyword
// We only want to consider "this" as a primaryExpression
initialExpression := p.parseJsxTagName()
if ast.IsJsxNamespacedName(initialExpression) {
return initialExpression // `a:b.c` is invalid syntax, don't even look for the `.` if we parse `a:b`, and let `parseAttribute` report "unexpected :" instead.
}
expression := initialExpression
for p.parseOptional(ast.KindDotToken) {
expression = p.finishNode(p.factory.NewPropertyAccessExpression(expression, nil, p.parseRightSideOfDot(true /*allowIdentifierNames*/, false /*allowPrivateIdentifiers*/, false /*allowUnicodeEscapeSequenceInIdentifierName*/), ast.NodeFlagsNone), pos)
}
return expression
}
func (p *Parser) parseJsxTagName() *ast.Expression {
pos := p.nodePos()
p.scanJsxIdentifier()
isThis := p.token == ast.KindThisKeyword
tagName := p.parseIdentifierNameErrorOnUnicodeEscapeSequence()
if p.parseOptional(ast.KindColonToken) {
p.scanJsxIdentifier()
return p.finishNode(p.factory.NewJsxNamespacedName(tagName, p.parseIdentifierNameErrorOnUnicodeEscapeSequence()), pos)
}
if isThis {
result := p.factory.NewKeywordExpression(ast.KindThisKeyword)
return p.finishNode(result, pos)
}
return tagName
}
func (p *Parser) parseJsxAttributes() *ast.Node {
pos := p.nodePos()
return p.finishNode(p.factory.NewJsxAttributes(p.parseList(PCJsxAttributes, (*Parser).parseJsxAttribute)), pos)
}
func (p *Parser) parseJsxAttribute() *ast.Node {
if p.token == ast.KindOpenBraceToken {
return p.parseJsxSpreadAttribute()
}
pos := p.nodePos()
return p.finishNode(p.factory.NewJsxAttribute(p.parseJsxAttributeName(), p.parseJsxAttributeValue()), pos)
}
func (p *Parser) parseJsxSpreadAttribute() *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindOpenBraceToken)
p.parseExpected(ast.KindDotDotDotToken)
expression := p.parseExpression()
p.parseExpected(ast.KindCloseBraceToken)
return p.finishNode(p.factory.NewJsxSpreadAttribute(expression), pos)
}
func (p *Parser) parseJsxAttributeName() *ast.Node {
pos := p.nodePos()
p.scanJsxIdentifier()
attrName := p.parseIdentifierNameErrorOnUnicodeEscapeSequence()
if p.parseOptional(ast.KindColonToken) {
p.scanJsxIdentifier()
return p.finishNode(p.factory.NewJsxNamespacedName(attrName, p.parseIdentifierNameErrorOnUnicodeEscapeSequence()), pos)
}
return attrName
}
func (p *Parser) parseJsxAttributeValue() *ast.Expression {
if p.token == ast.KindEqualsToken {
if p.scanJsxAttributeValue() == ast.KindStringLiteral {
return p.parseLiteralExpression(false /*intern*/)
}
if p.token == ast.KindOpenBraceToken {
return p.parseJsxExpression( /*inExpressionContext*/ true)
}
if p.token == ast.KindLessThanToken {
return p.parseJsxElementOrSelfClosingElementOrFragment(true /*inExpressionContext*/, -1, nil, false)
}
p.parseErrorAtCurrentToken(diagnostics.X_or_JSX_element_expected)
}
return nil
}
func (p *Parser) parseJsxClosingFragment(inExpressionContext bool) *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindLessThanSlashToken)
if p.parseExpectedWithDiagnostic(ast.KindGreaterThanToken, diagnostics.Expected_corresponding_closing_tag_for_JSX_fragment, false /*shouldAdvance*/) {
// manually advance the scanner in order to look for jsx text inside jsx
if inExpressionContext {
p.nextToken()
} else {
p.scanJsxText()
}
}
return p.finishNode(p.factory.NewJsxClosingFragment(), pos)
}
func (p *Parser) parseSimpleUnaryExpression() *ast.Expression {
switch p.token {
case ast.KindPlusToken, ast.KindMinusToken, ast.KindTildeToken, ast.KindExclamationToken:
return p.parsePrefixUnaryExpression()
case ast.KindDeleteKeyword:
return p.parseDeleteExpression()
case ast.KindTypeOfKeyword:
return p.parseTypeOfExpression()
case ast.KindVoidKeyword:
return p.parseVoidExpression()
case ast.KindLessThanToken:
// Just like in parseUpdateExpression, we need to avoid parsing type assertions when
// in JSX and we see an expression like "+ <foo> bar".
if p.languageVariant == core.LanguageVariantJSX {
return p.parseJsxElementOrSelfClosingElementOrFragment(true /*inExpressionContext*/, -1 /*topInvalidNodePosition*/, nil /*openingTag*/, true /*mustBeUnary*/)
}
// // This is modified UnaryExpression grammar in TypeScript
// // UnaryExpression (modified):
// // < type > UnaryExpression
return p.parseTypeAssertion()
case ast.KindAwaitKeyword:
if p.isAwaitExpression() {
return p.parseAwaitExpression()
}
fallthrough
default:
return p.parseUpdateExpression()
}
}
func (p *Parser) parsePrefixUnaryExpression() *ast.Node {
pos := p.nodePos()
operator := p.token
p.nextToken()
return p.finishNode(p.factory.NewPrefixUnaryExpression(operator, p.parseSimpleUnaryExpression()), pos)
}
func (p *Parser) parseDeleteExpression() *ast.Node {
pos := p.nodePos()
p.nextToken()
return p.finishNode(p.factory.NewDeleteExpression(p.parseSimpleUnaryExpression()), pos)
}
func (p *Parser) parseTypeOfExpression() *ast.Node {
pos := p.nodePos()
p.nextToken()
return p.finishNode(p.factory.NewTypeOfExpression(p.parseSimpleUnaryExpression()), pos)
}
func (p *Parser) parseVoidExpression() *ast.Node {
pos := p.nodePos()
p.nextToken()
return p.finishNode(p.factory.NewVoidExpression(p.parseSimpleUnaryExpression()), pos)
}
func (p *Parser) isAwaitExpression() bool {
if p.token == ast.KindAwaitKeyword {
if p.inAwaitContext() {
return true
}
// here we are using similar heuristics as 'isYieldExpression'
return p.lookAhead((*Parser).nextTokenIsIdentifierOrKeywordOrLiteralOnSameLine)
}
return false
}
func (p *Parser) parseAwaitExpression() *ast.Node {
pos := p.nodePos()
p.nextToken()
return p.finishNode(p.factory.NewAwaitExpression(p.parseSimpleUnaryExpression()), pos)
}
func (p *Parser) parseTypeAssertion() *ast.Node {
debug.Assert(p.languageVariant != core.LanguageVariantJSX, "Type assertions should never be parsed in JSX; they should be parsed as comparisons or JSX elements/fragments.")
pos := p.nodePos()
p.parseExpected(ast.KindLessThanToken)
typeNode := p.parseType()
p.parseExpected(ast.KindGreaterThanToken)
expression := p.parseSimpleUnaryExpression()
return p.finishNode(p.factory.NewTypeAssertion(typeNode, expression), pos)
}
func (p *Parser) parseLeftHandSideExpressionOrHigher() *ast.Expression {
// Original Ecma:
// LeftHandSideExpression: See 11.2
// NewExpression
// CallExpression
//
// Our simplification:
//
// LeftHandSideExpression: See 11.2
// MemberExpression
// CallExpression
//
// See comment in parseMemberExpressionOrHigher on how we replaced NewExpression with
// MemberExpression to make our lives easier.
//
// to best understand the below code, it's important to see how CallExpression expands
// out into its own productions:
//
// CallExpression:
// MemberExpression Arguments
// CallExpression Arguments
// CallExpression[Expression]
// CallExpression.IdentifierName
// import (AssignmentExpression)
// super Arguments
// super.IdentifierName
//
// Because of the recursion in these calls, we need to bottom out first. There are three
// bottom out states we can run into: 1) We see 'super' which must start either of
// the last two CallExpression productions. 2) We see 'import' which must start import call.
// 3)we have a MemberExpression which either completes the LeftHandSideExpression,
// or starts the beginning of the first four CallExpression productions.
pos := p.nodePos()
var expression *ast.Expression
if p.token == ast.KindImportKeyword {
if p.lookAhead((*Parser).nextTokenIsOpenParenOrLessThan) {
// We don't want to eagerly consume all import keyword as import call expression so we look ahead to find "("
// For example:
// var foo3 = require("subfolder
// import * as foo1 from "module-from-node
// We want this import to be a statement rather than import call expression
p.sourceFlags |= ast.NodeFlagsPossiblyContainsDynamicImport
expression = p.parseKeywordExpression()
} else if p.lookAhead((*Parser).nextTokenIsDot) {
// This is an 'import.*' metaproperty (i.e. 'import.meta')
p.nextToken() // advance past the 'import'
p.nextToken() // advance past the dot
expression = p.finishNode(p.factory.NewMetaProperty(ast.KindImportKeyword, p.parseIdentifierName()), pos)
if expression.Text() == "defer" {
if p.token == ast.KindOpenParenToken || p.token == ast.KindLessThanToken {
p.sourceFlags |= ast.NodeFlagsPossiblyContainsDynamicImport
}
} else {
p.sourceFlags |= ast.NodeFlagsPossiblyContainsImportMeta
}
} else {
expression = p.parseMemberExpressionOrHigher()
}
} else if p.token == ast.KindSuperKeyword {
expression = p.parseSuperExpression()
} else {
expression = p.parseMemberExpressionOrHigher()
}
// Now, we *may* be complete. However, we might have consumed the start of a
// CallExpression or OptionalExpression. As such, we need to consume the rest
// of it here to be complete.
return p.parseCallExpressionRest(pos, expression)
}
func (p *Parser) nextTokenIsDot() bool {
return p.nextToken() == ast.KindDotToken
}
func (p *Parser) parseSuperExpression() *ast.Expression {
pos := p.nodePos()
expression := p.parseKeywordExpression()
if p.token == ast.KindLessThanToken {
startPos := p.nodePos()
typeArguments := p.tryParseTypeArgumentsInExpression()
if typeArguments != nil {
p.parseErrorAt(startPos, p.nodePos(), diagnostics.X_super_may_not_use_type_arguments)
if !p.isTemplateStartOfTaggedTemplate() {
expression = p.finishNode(p.factory.NewExpressionWithTypeArguments(expression, typeArguments), pos)
}
}
}
if p.token == ast.KindOpenParenToken || p.token == ast.KindDotToken || p.token == ast.KindOpenBracketToken {
return expression
}
// If we have seen "super" it must be followed by '(' or '.'.
// If it wasn't then just try to parse out a '.' and report an error.
p.parseErrorAtCurrentToken(diagnostics.X_super_must_be_followed_by_an_argument_list_or_member_access)
// private names will never work with `super` (`super.#foo`), but that's a semantic error, not syntactic
return p.finishNode(p.factory.NewPropertyAccessExpression(expression, nil /*questionDotToken*/, p.parseRightSideOfDot(true /*allowIdentifierNames*/, true /*allowPrivateIdentifiers*/, true /*allowUnicodeEscapeSequenceInIdentifierName*/), ast.NodeFlagsNone), pos)
}
func (p *Parser) isTemplateStartOfTaggedTemplate() bool {
return p.token == ast.KindNoSubstitutionTemplateLiteral || p.token == ast.KindTemplateHead
}
func (p *Parser) tryParseTypeArgumentsInExpression() *ast.NodeList {
// TypeArguments must not be parsed in JavaScript files to avoid ambiguity with binary operators.
state := p.mark()
if p.contextFlags&ast.NodeFlagsJavaScriptFile == 0 && p.reScanLessThanToken() == ast.KindLessThanToken {
p.nextToken()
typeArguments := p.parseDelimitedList(PCTypeArguments, (*Parser).parseType)
// If it doesn't have the closing `>` then it's definitely not an type argument list.
if p.reScanGreaterThanToken() == ast.KindGreaterThanToken {
p.nextToken()
// We successfully parsed a type argument list. The next token determines whether we want to
// treat it as such. If the type argument list is followed by `(` or a template literal, as in
// `f<number>(42)`, we favor the type argument interpretation even though JavaScript would view
// it as a relational expression.
if p.canFollowTypeArgumentsInExpression() {
return typeArguments
}
}
}
p.rewind(state)
return nil
}
func (p *Parser) canFollowTypeArgumentsInExpression() bool {
switch p.token {
// These tokens can follow a type argument list in a call expression:
// foo<x>(
// foo<T> `...`
// foo<T> `...${100}...`
case ast.KindOpenParenToken, ast.KindNoSubstitutionTemplateLiteral, ast.KindTemplateHead:
return true
// A type argument list followed by `<` never makes sense, and a type argument list followed
// by `>` is ambiguous with a (re-scanned) `>>` operator, so we disqualify both. Also, in
// this context, `+` and `-` are unary operators, not binary operators.
case ast.KindLessThanToken, ast.KindGreaterThanToken, ast.KindPlusToken, ast.KindMinusToken:
return false
}
// We favor the type argument list interpretation when it is immediately followed by
// a line break, a binary operator, or something that can't start an expression.
return p.hasPrecedingLineBreak() || p.isBinaryOperator() || !p.isStartOfExpression()
}
func (p *Parser) parseMemberExpressionOrHigher() *ast.Node {
// Note: to make our lives simpler, we decompose the NewExpression productions and
// place ObjectCreationExpression and FunctionExpression into PrimaryExpression.
// like so:
//
// PrimaryExpression : See 11.1
// this
// Identifier
// Literal
// ArrayLiteral
// ObjectLiteral
// (Expression)
// FunctionExpression
// new MemberExpression Arguments?
//
// MemberExpression : See 11.2
// PrimaryExpression
// MemberExpression[Expression]
// MemberExpression.IdentifierName
//
// CallExpression : See 11.2
// MemberExpression
// CallExpression Arguments
// CallExpression[Expression]
// CallExpression.IdentifierName
//
// Technically this is ambiguous. i.e. CallExpression defines:
//
// CallExpression:
// CallExpression Arguments
//
// If you see: "new Foo()"
//
// Then that could be treated as a single ObjectCreationExpression, or it could be
// treated as the invocation of "new Foo". We disambiguate that in code (to match
// the original grammar) by making sure that if we see an ObjectCreationExpression
// we always consume arguments if they are there. So we treat "new Foo()" as an
// object creation only, and not at all as an invocation. Another way to think
// about this is that for every "new" that we see, we will consume an argument list if
// it is there as part of the *associated* object creation node. Any additional
// argument lists we see, will become invocation expressions.
//
// Because there are no other places in the grammar now that refer to FunctionExpression
// or ObjectCreationExpression, it is safe to push down into the PrimaryExpression
// production.
//
// Because CallExpression and MemberExpression are left recursive, we need to bottom out
// of the recursion immediately. So we parse out a primary expression to start with.
pos := p.nodePos()
expression := p.parsePrimaryExpression()
return p.parseMemberExpressionRest(pos, expression, true /*allowOptionalChain*/)
}
func (p *Parser) parseMemberExpressionRest(pos int, expression *ast.Expression, allowOptionalChain bool) *ast.Expression {
for {
var questionDotToken *ast.Node
isPropertyAccess := false
if allowOptionalChain && p.isStartOfOptionalPropertyOrElementAccessChain() {
questionDotToken = p.parseExpectedToken(ast.KindQuestionDotToken)
isPropertyAccess = tokenIsIdentifierOrKeyword(p.token)
} else {
isPropertyAccess = p.parseOptional(ast.KindDotToken)
}
if isPropertyAccess {
expression = p.parsePropertyAccessExpressionRest(pos, expression, questionDotToken)
continue
}
// when in the [Decorator] context, we do not parse ElementAccess as it could be part of a ComputedPropertyName
if (questionDotToken != nil || !p.inDecoratorContext()) && p.parseOptional(ast.KindOpenBracketToken) {
expression = p.parseElementAccessExpressionRest(pos, expression, questionDotToken)
continue
}
if p.isTemplateStartOfTaggedTemplate() {
// Absorb type arguments into TemplateExpression when preceding expression is ExpressionWithTypeArguments
if questionDotToken == nil && ast.IsExpressionWithTypeArguments(expression) {
original := expression.AsExpressionWithTypeArguments()
expression = p.parseTaggedTemplateRest(pos, original.Expression, questionDotToken, original.TypeArguments)
p.unparseExpressionWithTypeArguments(original.Expression, original.TypeArguments, expression)
} else {
expression = p.parseTaggedTemplateRest(pos, expression, questionDotToken, nil /*typeArguments*/)
}
continue
}
if questionDotToken == nil {
if p.token == ast.KindExclamationToken && !p.hasPrecedingLineBreak() {
p.nextToken()
expression = p.checkJSSyntax(p.finishNode(p.factory.NewNonNullExpression(expression, ast.NodeFlagsNone), pos))
continue
}
typeArguments := p.tryParseTypeArgumentsInExpression()
if typeArguments != nil {
expression = p.finishNode(p.factory.NewExpressionWithTypeArguments(expression, typeArguments), pos)
continue
}
}
return expression
}
}
func (p *Parser) isStartOfOptionalPropertyOrElementAccessChain() bool {
return p.token == ast.KindQuestionDotToken && p.lookAhead((*Parser).nextTokenIsIdentifierOrKeywordOrOpenBracketOrTemplate)
}
func (p *Parser) nextTokenIsIdentifierOrKeywordOrOpenBracketOrTemplate() bool {
p.nextToken()
return tokenIsIdentifierOrKeyword(p.token) || p.token == ast.KindOpenBracketToken || p.isTemplateStartOfTaggedTemplate()
}
func (p *Parser) parsePropertyAccessExpressionRest(pos int, expression *ast.Expression, questionDotToken *ast.Node) *ast.Node {
name := p.parseRightSideOfDot(true /*allowIdentifierNames*/, true /*allowPrivateIdentifiers*/, true /*allowUnicodeEscapeSequenceInIdentifierName*/)
isOptionalChain := questionDotToken != nil || p.tryReparseOptionalChain(expression)
propertyAccess := p.factory.NewPropertyAccessExpression(expression, questionDotToken, name, core.IfElse(isOptionalChain, ast.NodeFlagsOptionalChain, ast.NodeFlagsNone))
if isOptionalChain && ast.IsPrivateIdentifier(name) {
p.parseErrorAtRange(p.skipRangeTrivia(name.Loc), diagnostics.An_optional_chain_cannot_contain_private_identifiers)
}
if ast.IsExpressionWithTypeArguments(expression) {
typeArguments := expression.AsExpressionWithTypeArguments().TypeArguments
if typeArguments != nil {
loc := core.NewTextRange(typeArguments.Pos()-1, scanner.SkipTrivia(p.sourceText, typeArguments.End())+1)
p.parseErrorAtRange(loc, diagnostics.An_instantiation_expression_cannot_be_followed_by_a_property_access)
}
}
return p.finishNode(propertyAccess, pos)
}
func (p *Parser) tryReparseOptionalChain(node *ast.Expression) bool {
if node.Flags&ast.NodeFlagsOptionalChain != 0 {
return true
}
// check for an optional chain in a non-null expression
if ast.IsNonNullExpression(node) {
expr := node.AsNonNullExpression().Expression
for ast.IsNonNullExpression(expr) && expr.Flags&ast.NodeFlagsOptionalChain == 0 {
expr = expr.AsNonNullExpression().Expression
}
if expr.Flags&ast.NodeFlagsOptionalChain != 0 {
// this is part of an optional chain. Walk down from `node` to `expression` and set the flag.
for ast.IsNonNullExpression(node) {
node.Flags |= ast.NodeFlagsOptionalChain
node = node.AsNonNullExpression().Expression
}
return true
}
}
return false
}
func (p *Parser) parseElementAccessExpressionRest(pos int, expression *ast.Expression, questionDotToken *ast.Node) *ast.Node {
var argumentExpression *ast.Expression
if p.token == ast.KindCloseBracketToken {
p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.An_element_access_expression_should_take_an_argument)
argumentExpression = p.createMissingIdentifier()
} else {
argument := p.parseExpressionAllowIn()
switch argument.Kind {
case ast.KindStringLiteral:
argument.AsStringLiteral().Text = p.internIdentifier(argument.AsStringLiteral().Text)
case ast.KindNoSubstitutionTemplateLiteral:
argument.AsNoSubstitutionTemplateLiteral().Text = p.internIdentifier(argument.AsNoSubstitutionTemplateLiteral().Text)
case ast.KindNumericLiteral:
argument.AsNumericLiteral().Text = p.internIdentifier(argument.AsNumericLiteral().Text)
}
argumentExpression = argument
}
p.parseExpected(ast.KindCloseBracketToken)
isOptionalChain := questionDotToken != nil || p.tryReparseOptionalChain(expression)
return p.finishNode(p.factory.NewElementAccessExpression(expression, questionDotToken, argumentExpression, core.IfElse(isOptionalChain, ast.NodeFlagsOptionalChain, ast.NodeFlagsNone)), pos)
}
func (p *Parser) parseCallExpressionRest(pos int, expression *ast.Expression) *ast.Expression {
for {
expression = p.parseMemberExpressionRest(pos, expression /*allowOptionalChain*/, true)
var typeArguments *ast.NodeList
questionDotToken := p.parseOptionalToken(ast.KindQuestionDotToken)
if questionDotToken != nil {
typeArguments = p.tryParseTypeArgumentsInExpression()
if p.isTemplateStartOfTaggedTemplate() {
expression = p.parseTaggedTemplateRest(pos, expression, questionDotToken, typeArguments)
continue
}
}
if typeArguments != nil || p.token == ast.KindOpenParenToken {
// Absorb type arguments into CallExpression when preceding expression is ExpressionWithTypeArguments
if questionDotToken == nil && expression.Kind == ast.KindExpressionWithTypeArguments {
typeArguments = expression.AsExpressionWithTypeArguments().TypeArguments
expression = expression.AsExpressionWithTypeArguments().Expression
}
inner := expression
argumentList := p.parseArgumentList()
isOptionalChain := questionDotToken != nil || p.tryReparseOptionalChain(expression)
expression = p.checkJSSyntax(p.finishNode(p.factory.NewCallExpression(expression, questionDotToken, typeArguments, argumentList, core.IfElse(isOptionalChain, ast.NodeFlagsOptionalChain, ast.NodeFlagsNone)), pos))
p.unparseExpressionWithTypeArguments(inner, typeArguments, expression)
continue
}
if questionDotToken != nil {
// We parsed `?.` but then failed to parse anything, so report a missing identifier here.
p.parseErrorAtCurrentToken(diagnostics.Identifier_expected)
name := p.createMissingIdentifier()
expression = p.finishNode(p.factory.NewPropertyAccessExpression(expression, questionDotToken, name, ast.NodeFlagsOptionalChain), pos)
}
break
}
return expression
}
func (p *Parser) parseArgumentList() *ast.NodeList {
p.parseExpected(ast.KindOpenParenToken)
result := p.parseDelimitedList(PCArgumentExpressions, (*Parser).parseArgumentExpression)
p.parseExpected(ast.KindCloseParenToken)
return result
}
func (p *Parser) parseArgumentExpression() *ast.Expression {
return doInContext(p, ast.NodeFlagsDisallowInContext|ast.NodeFlagsDecoratorContext, false, (*Parser).parseArgumentOrArrayLiteralElement)
}
func (p *Parser) parseArgumentOrArrayLiteralElement() *ast.Expression {
switch p.token {
case ast.KindDotDotDotToken:
return p.parseSpreadElement()
case ast.KindCommaToken:
return p.finishNode(p.factory.NewOmittedExpression(), p.nodePos())
}
return p.parseAssignmentExpressionOrHigher()
}
func (p *Parser) parseSpreadElement() *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindDotDotDotToken)
expression := p.parseAssignmentExpressionOrHigher()
return p.finishNode(p.factory.NewSpreadElement(expression), pos)
}
func (p *Parser) parseTaggedTemplateRest(pos int, tag *ast.Expression, questionDotToken *ast.Node, typeArguments *ast.NodeList) *ast.Node {
var template *ast.Expression
if p.token == ast.KindNoSubstitutionTemplateLiteral {
p.reScanTemplateToken(true /*isTaggedTemplate*/)
template = p.parseLiteralExpression(false /*intern*/)
} else {
template = p.parseTemplateExpression(true /*isTaggedTemplate*/)
}
isOptionalChain := questionDotToken != nil || tag.Flags&ast.NodeFlagsOptionalChain != 0
return p.checkJSSyntax(p.finishNode(p.factory.NewTaggedTemplateExpression(tag, questionDotToken, typeArguments, template, core.IfElse(isOptionalChain, ast.NodeFlagsOptionalChain, ast.NodeFlagsNone)), pos))
}
func (p *Parser) parseTemplateExpression(isTaggedTemplate bool) *ast.Expression {
pos := p.nodePos()
return p.finishNode(p.factory.NewTemplateExpression(p.parseTemplateHead(isTaggedTemplate), p.parseTemplateSpans(isTaggedTemplate)), pos)
}
func (p *Parser) parseTemplateSpans(isTaggedTemplate bool) *ast.NodeList {
pos := p.nodePos()
var list []*ast.Node
for {
span := p.parseTemplateSpan(isTaggedTemplate)
list = append(list, span)
if span.AsTemplateSpan().Literal.Kind != ast.KindTemplateMiddle {
break
}
}
return p.newNodeList(core.NewTextRange(pos, p.nodePos()), list)
}
func (p *Parser) parseTemplateSpan(isTaggedTemplate bool) *ast.Node {
pos := p.nodePos()
expression := p.parseExpressionAllowIn()
literal := p.parseLiteralOfTemplateSpan(isTaggedTemplate)
return p.finishNode(p.factory.NewTemplateSpan(expression, literal), pos)
}
func (p *Parser) parsePrimaryExpression() *ast.Expression {
switch p.token {
case ast.KindNoSubstitutionTemplateLiteral:
if p.scanner.TokenFlags()&ast.TokenFlagsIsInvalid != 0 {
p.reScanTemplateToken(false /*isTaggedTemplate*/)
}
fallthrough
case ast.KindNumericLiteral, ast.KindBigIntLiteral, ast.KindStringLiteral:
return p.parseLiteralExpression(false /*intern*/)
case ast.KindThisKeyword, ast.KindSuperKeyword, ast.KindNullKeyword, ast.KindTrueKeyword, ast.KindFalseKeyword:
return p.parseKeywordExpression()
case ast.KindOpenParenToken:
return p.parseParenthesizedExpression()
case ast.KindOpenBracketToken:
return p.parseArrayLiteralExpression()
case ast.KindOpenBraceToken:
return p.parseObjectLiteralExpression()
case ast.KindAsyncKeyword:
// Async arrow functions are parsed earlier in parseAssignmentExpressionOrHigher.
// If we encounter `async [no LineTerminator here] function` then this is an async
// function; otherwise, its an identifier.
if !p.lookAhead((*Parser).nextTokenIsFunctionKeywordOnSameLine) {
break
}
return p.parseFunctionExpression()
case ast.KindAtToken:
return p.parseDecoratedExpression()
case ast.KindClassKeyword:
return p.parseClassExpression()
case ast.KindFunctionKeyword:
return p.parseFunctionExpression()
case ast.KindNewKeyword:
return p.parseNewExpressionOrNewDotTarget()
case ast.KindSlashToken, ast.KindSlashEqualsToken:
if p.reScanSlashToken() == ast.KindRegularExpressionLiteral {
return p.parseLiteralExpression(false /*intern*/)
}
case ast.KindTemplateHead:
return p.parseTemplateExpression(false /*isTaggedTemplate*/)
case ast.KindPrivateIdentifier:
return p.parsePrivateIdentifier()
}
return p.parseIdentifierWithDiagnostic(diagnostics.Expression_expected, nil)
}
func (p *Parser) parseParenthesizedExpression() *ast.Expression {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
p.parseExpected(ast.KindOpenParenToken)
expression := p.parseExpressionAllowIn()
p.parseExpected(ast.KindCloseParenToken)
result := p.finishNode(p.factory.NewParenthesizedExpression(expression), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
func (p *Parser) parseArrayLiteralExpression() *ast.Expression {
pos := p.nodePos()
openBracketPosition := p.scanner.TokenStart()
openBracketParsed := p.parseExpected(ast.KindOpenBracketToken)
multiLine := p.hasPrecedingLineBreak()
elements := p.parseDelimitedList(PCArrayLiteralMembers, (*Parser).parseArgumentOrArrayLiteralElement)
p.parseExpectedMatchingBrackets(ast.KindOpenBracketToken, ast.KindCloseBracketToken, openBracketParsed, openBracketPosition)
return p.finishNode(p.factory.NewArrayLiteralExpression(elements, multiLine), pos)
}
func (p *Parser) parseObjectLiteralExpression() *ast.Expression {
pos := p.nodePos()
openBracePosition := p.scanner.TokenStart()
openBraceParsed := p.parseExpected(ast.KindOpenBraceToken)
multiLine := p.hasPrecedingLineBreak()
properties := p.parseDelimitedList(PCObjectLiteralMembers, (*Parser).parseObjectLiteralElement)
p.parseExpectedMatchingBrackets(ast.KindOpenBraceToken, ast.KindCloseBraceToken, openBraceParsed, openBracePosition)
return p.finishNode(p.factory.NewObjectLiteralExpression(properties, multiLine), pos)
}
func (p *Parser) parseObjectLiteralElement() *ast.Node {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
if p.parseOptional(ast.KindDotDotDotToken) {
expression := p.parseAssignmentExpressionOrHigher()
result := p.finishNode(p.factory.NewSpreadAssignment(expression), pos)
p.withJSDoc(result, hasJSDoc)
return result
}
modifiers := p.parseModifiersEx(true /*allowDecorators*/, false /*permitConstAsModifier*/, false /*stopOnStartOfClassStaticBlock*/)
if p.parseContextualModifier(ast.KindGetKeyword) {
return p.parseAccessorDeclaration(pos, hasJSDoc, modifiers, ast.KindGetAccessor, ParseFlagsNone)
}
if p.parseContextualModifier(ast.KindSetKeyword) {
return p.parseAccessorDeclaration(pos, hasJSDoc, modifiers, ast.KindSetAccessor, ParseFlagsNone)
}
asteriskToken := p.parseOptionalToken(ast.KindAsteriskToken)
tokenIsIdentifier := p.isIdentifier()
name := p.parsePropertyName()
// Disallowing of optional property assignments and definite assignment assertion happens in the grammar checker.
postfixToken := p.parseOptionalToken(ast.KindQuestionToken)
// Decorators, Modifiers, questionToken, and exclamationToken are not supported by property assignments and are reported in the grammar checker
if postfixToken == nil {
postfixToken = p.parseOptionalToken(ast.KindExclamationToken)
}
if asteriskToken != nil || p.token == ast.KindOpenParenToken || p.token == ast.KindLessThanToken {
return p.parseMethodDeclaration(pos, hasJSDoc, modifiers, asteriskToken, name, postfixToken, nil /*diagnosticMessage*/)
}
// check if it is short-hand property assignment or normal property assignment
// NOTE: if token is EqualsToken it is interpreted as CoverInitializedName production
// CoverInitializedName[Yield] :
// IdentifierReference[?Yield] Initializer[In, ?Yield]
// this is necessary because ObjectLiteral productions are also used to cover grammar for ObjectAssignmentPattern
var node *ast.Node
isShorthandPropertyAssignment := tokenIsIdentifier && p.token != ast.KindColonToken
if isShorthandPropertyAssignment {
equalsToken := p.parseOptionalToken(ast.KindEqualsToken)
var initializer *ast.Expression
if equalsToken != nil {
initializer = doInContext(p, ast.NodeFlagsDisallowInContext, false, (*Parser).parseAssignmentExpressionOrHigher)
}
node = p.factory.NewShorthandPropertyAssignment(modifiers, name, postfixToken, nil /*typeNode*/, equalsToken, initializer)
} else {
p.parseExpected(ast.KindColonToken)
initializer := doInContext(p, ast.NodeFlagsDisallowInContext, false, (*Parser).parseAssignmentExpressionOrHigher)
node = p.factory.NewPropertyAssignment(modifiers, name, postfixToken, nil /*typeNode*/, initializer)
}
p.finishNode(node, pos)
p.withJSDoc(node, hasJSDoc)
return node
}
func (p *Parser) parseFunctionExpression() *ast.Expression {
// GeneratorExpression:
// function* BindingIdentifier [Yield][opt](FormalParameters[Yield]){ GeneratorBody }
//
// FunctionExpression:
// function BindingIdentifier[opt](FormalParameters){ FunctionBody }
saveContexFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsDecoratorContext, false)
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
modifiers := p.parseModifiers()
p.parseExpected(ast.KindFunctionKeyword)
asteriskToken := p.parseOptionalToken(ast.KindAsteriskToken)
isGenerator := asteriskToken != nil
isAsync := modifierListHasAsync(modifiers)
signatureFlags := core.IfElse(isGenerator, ParseFlagsYield, ParseFlagsNone) | core.IfElse(isAsync, ParseFlagsAwait, ParseFlagsNone)
var name *ast.Node
switch {
case isGenerator && isAsync:
name = doInContext(p, ast.NodeFlagsYieldContext|ast.NodeFlagsAwaitContext, true, (*Parser).parseOptionalBindingIdentifier)
case isGenerator:
name = doInContext(p, ast.NodeFlagsYieldContext, true, (*Parser).parseOptionalBindingIdentifier)
case isAsync:
name = doInContext(p, ast.NodeFlagsAwaitContext, true, (*Parser).parseOptionalBindingIdentifier)
default:
name = p.parseOptionalBindingIdentifier()
}
typeParameters := p.parseTypeParameters()
parameters := p.parseParameters(signatureFlags)
returnType := p.parseReturnType(ast.KindColonToken, false /*isType*/)
body := p.parseFunctionBlock(signatureFlags, nil /*diagnosticMessage*/)
p.contextFlags = saveContexFlags
result := p.factory.NewFunctionExpression(modifiers, asteriskToken, name, typeParameters, parameters, returnType, nil /*fullSignature*/, body)
p.finishNode(result, pos)
p.withJSDoc(result, hasJSDoc)
p.checkJSSyntax(result)
return result
}
func (p *Parser) parseOptionalBindingIdentifier() *ast.Node {
if p.isBindingIdentifier() {
return p.parseBindingIdentifier()
}
return nil
}
func (p *Parser) parseDecoratedExpression() *ast.Expression {
pos := p.nodePos()
hasJSDoc := p.hasPrecedingJSDocComment()
modifiers := p.parseModifiersEx(true /*allowDecorators*/, false /*permitConstAsModifier*/, false /*stopOnStartOfClassStaticBlock*/)
if p.token == ast.KindClassKeyword {
return p.parseClassDeclarationOrExpression(pos, hasJSDoc, modifiers, ast.KindClassExpression)
}
p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.Expression_expected)
return p.finishNode(p.factory.NewMissingDeclaration(modifiers), pos)
}
func (p *Parser) unparseExpressionWithTypeArguments(expression *ast.Node, typeArguments *ast.NodeList, result *ast.Node) {
// force overwrite the `.Parent` of the expression and type arguments to erase the fact that they may have originally been parsed as an ExpressionWithTypeArguments and be parented to such
if expression != nil {
expression.Parent = result
}
if typeArguments != nil {
for _, a := range typeArguments.Nodes {
a.Parent = result
}
}
}
func (p *Parser) parseNewExpressionOrNewDotTarget() *ast.Node {
pos := p.nodePos()
p.parseExpected(ast.KindNewKeyword)
if p.parseOptional(ast.KindDotToken) {
name := p.parseIdentifierName()
return p.finishNode(p.factory.NewMetaProperty(ast.KindNewKeyword, name), pos)
}
expressionPos := p.nodePos()
expression := p.parseMemberExpressionRest(expressionPos, p.parsePrimaryExpression(), false /*allowOptionalChain*/)
var typeArguments *ast.NodeList
// Absorb type arguments into NewExpression when preceding expression is ExpressionWithTypeArguments
if expression.Kind == ast.KindExpressionWithTypeArguments {
typeArguments = expression.AsExpressionWithTypeArguments().TypeArguments
expression = expression.AsExpressionWithTypeArguments().Expression
}
if p.token == ast.KindQuestionDotToken {
p.parseErrorAtCurrentToken(diagnostics.Invalid_optional_chain_from_new_expression_Did_you_mean_to_call_0, scanner.GetTextOfNodeFromSourceText(p.sourceText, expression, false /*includeTrivia*/))
}
var argumentList *ast.NodeList
if p.token == ast.KindOpenParenToken {
argumentList = p.parseArgumentList()
}
result := p.checkJSSyntax(p.finishNode(p.factory.NewNewExpression(expression, typeArguments, argumentList), pos))
p.unparseExpressionWithTypeArguments(expression, typeArguments, result)
return result
}
func (p *Parser) parseKeywordExpression() *ast.Node {
pos := p.nodePos()
result := p.factory.NewKeywordExpression(p.token)
p.nextToken()
return p.finishNode(result, pos)
}
func (p *Parser) parseLiteralExpression(intern bool) *ast.Node {
pos := p.nodePos()
text := p.scanner.TokenValue()
if intern {
text = p.internIdentifier(text)
}
tokenFlags := p.scanner.TokenFlags()
var result *ast.Node
switch p.token {
case ast.KindStringLiteral:
result = p.factory.NewStringLiteral(text)
result.AsStringLiteral().TokenFlags |= tokenFlags & ast.TokenFlagsStringLiteralFlags
case ast.KindNumericLiteral:
result = p.factory.NewNumericLiteral(text)
result.AsNumericLiteral().TokenFlags |= tokenFlags & ast.TokenFlagsNumericLiteralFlags
case ast.KindBigIntLiteral:
result = p.factory.NewBigIntLiteral(text)
result.AsBigIntLiteral().TokenFlags |= tokenFlags & ast.TokenFlagsNumericLiteralFlags
case ast.KindRegularExpressionLiteral:
result = p.factory.NewRegularExpressionLiteral(text)
result.AsRegularExpressionLiteral().TokenFlags |= tokenFlags & ast.TokenFlagsRegularExpressionLiteralFlags
case ast.KindNoSubstitutionTemplateLiteral:
result = p.factory.NewNoSubstitutionTemplateLiteral(text)
result.AsNoSubstitutionTemplateLiteral().TokenFlags |= tokenFlags & ast.TokenFlagsTemplateLiteralLikeFlags
default:
panic("Unhandled case in parseLiteralExpression")
}
p.nextToken()
return p.finishNode(result, pos)
}
func (p *Parser) parseIdentifierNameErrorOnUnicodeEscapeSequence() *ast.Node {
if p.scanner.HasUnicodeEscape() || p.scanner.HasExtendedUnicodeEscape() {
p.parseErrorAtCurrentToken(diagnostics.Unicode_escape_sequence_cannot_appear_here)
}
return p.createIdentifier(tokenIsIdentifierOrKeyword(p.token))
}
func (p *Parser) parseBindingIdentifier() *ast.Node {
return p.parseBindingIdentifierWithDiagnostic(nil)
}
func (p *Parser) parseBindingIdentifierWithDiagnostic(privateIdentifierDiagnosticMessage *diagnostics.Message) *ast.Node {
saveHasAwaitIdentifier := p.statementHasAwaitIdentifier
id := p.createIdentifierWithDiagnostic(p.isBindingIdentifier(), nil /*diagnosticMessage*/, privateIdentifierDiagnosticMessage)
p.statementHasAwaitIdentifier = saveHasAwaitIdentifier
return id
}
func (p *Parser) parseIdentifierName() *ast.Node {
return p.parseIdentifierNameWithDiagnostic(nil)
}
func (p *Parser) parseIdentifierNameWithDiagnostic(diagnosticMessage *diagnostics.Message) *ast.Node {
return p.createIdentifierWithDiagnostic(tokenIsIdentifierOrKeyword(p.token), diagnosticMessage, nil)
}
func (p *Parser) parseIdentifier() *ast.Node {
return p.parseIdentifierWithDiagnostic(nil, nil)
}
func (p *Parser) parseIdentifierWithDiagnostic(diagnosticMessage *diagnostics.Message, privateIdentifierDiagnosticMessage *diagnostics.Message) *ast.Node {
return p.createIdentifierWithDiagnostic(p.isIdentifier(), diagnosticMessage, privateIdentifierDiagnosticMessage)
}
func (p *Parser) createIdentifier(isIdentifier bool) *ast.Node {
return p.createIdentifierWithDiagnostic(isIdentifier, nil, nil)
}
func (p *Parser) createIdentifierWithDiagnostic(isIdentifier bool, diagnosticMessage *diagnostics.Message, privateIdentifierDiagnosticMessage *diagnostics.Message) *ast.Node {
if isIdentifier {
var pos int
if p.scanner.HasPrecedingJSDocLeadingAsterisks() {
pos = p.scanner.TokenStart()
} else {
pos = p.nodePos()
}
text := p.scanner.TokenValue()
p.nextTokenWithoutCheck()
return p.finishNode(p.newIdentifier(p.internIdentifier(text)), pos)
}
if p.token == ast.KindPrivateIdentifier {
if privateIdentifierDiagnosticMessage != nil {
p.parseErrorAtCurrentToken(privateIdentifierDiagnosticMessage)
} else {
p.parseErrorAtCurrentToken(diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies)
}
return p.createIdentifier(true /*isIdentifier*/)
}
if diagnosticMessage != nil {
p.parseErrorAtCurrentToken(diagnosticMessage)
} else if isReservedWord(p.token) {
p.parseErrorAtCurrentToken(diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, p.scanner.TokenText())
} else {
p.parseErrorAtCurrentToken(diagnostics.Identifier_expected)
}
return p.createMissingIdentifier()
}
func (p *Parser) internIdentifier(text string) string {
if identifier, ok := p.identifiers[text]; ok {
return identifier
}
identifier := text
if p.identifiers == nil {
p.identifiers = make(map[string]string)
}
p.identifiers[identifier] = identifier
return identifier
}
func (p *Parser) newNodeList(loc core.TextRange, nodes []*ast.Node) *ast.NodeList {
list := p.factory.NewNodeList(nodes)
list.Loc = loc
return list
}
func (p *Parser) newModifierList(loc core.TextRange, nodes []*ast.Node) *ast.ModifierList {
list := p.factory.NewModifierList(nodes)
list.Loc = loc
return list
}
func (p *Parser) finishNode(node *ast.Node, pos int) *ast.Node {
return p.finishNodeWithEnd(node, pos, p.nodePos())
}
func (p *Parser) finishNodeWithEnd(node *ast.Node, pos int, end int) *ast.Node {
node.Loc = core.NewTextRange(pos, end)
node.Flags |= p.contextFlags
if p.hasParseError {
node.Flags |= ast.NodeFlagsThisNodeHasError
p.hasParseError = false
}
p.overrideParentInImmediateChildren(node)
return node
}
func (p *Parser) overrideParentInImmediateChildren(node *ast.Node) {
p.currentParent = node
node.ForEachChild(p.setParentFromContext)
p.currentParent = nil
}
func (p *Parser) nextTokenIsSlash() bool {
return p.nextToken() == ast.KindSlashToken
}
func (p *Parser) scanTypeMemberStart() bool {
// Return true if we have the start of a signature member
if p.token == ast.KindOpenParenToken || p.token == ast.KindLessThanToken || p.token == ast.KindGetKeyword || p.token == ast.KindSetKeyword {
return true
}
idToken := false
// Eat up all modifiers, but hold on to the last one in case it is actually an identifier
for ast.IsModifierKind(p.token) {
idToken = true
p.nextToken()
}
// Index signatures and computed property names are type members
if p.token == ast.KindOpenBracketToken {
return true
}
// Try to get the first property-like token following all modifiers
if p.isLiteralPropertyName() {
idToken = true
p.nextToken()
}
// If we were able to get any potential identifier, check that it is
// the start of a member declaration
if idToken {
return p.token == ast.KindOpenParenToken || p.token == ast.KindLessThanToken || p.token == ast.KindQuestionToken || p.token == ast.KindColonToken || p.token == ast.KindCommaToken || p.canParseSemicolon()
}
return false
}
func (p *Parser) scanClassMemberStart() bool {
idToken := ast.KindUnknown
if p.token == ast.KindAtToken {
return true
}
// Eat up all modifiers, but hold on to the last one in case it is actually an identifier.
for ast.IsModifierKind(p.token) {
idToken = p.token
// If the idToken is a class modifier (protected, private, public, and static), it is
// certain that we are starting to parse class member. This allows better error recovery
// Example:
// public foo() ... // true
// public @dec blah ... // true; we will then report an error later
// export public ... // true; we will then report an error later
if ast.IsClassMemberModifier(idToken) {
return true
}
p.nextToken()
}
if p.token == ast.KindAsteriskToken {
return true
}
// Try to get the first property-like token following all modifiers.
// This can either be an identifier or the 'get' or 'set' keywords.
if p.isLiteralPropertyName() {
idToken = p.token
p.nextToken()
}
// Index signatures and computed properties are class members; we can parse.
if p.token == ast.KindOpenBracketToken {
return true
}
// If we were able to get any potential identifier...
if idToken != ast.KindUnknown {
// If we have a non-keyword identifier, or if we have an accessor, then it's safe to parse.
if !ast.IsKeyword(idToken) || idToken == ast.KindSetKeyword || idToken == ast.KindGetKeyword {
return true
}
// If it *is* a keyword, but not an accessor, check a little farther along
// to see if it should actually be parsed as a class member.
switch p.token {
case ast.KindOpenParenToken, // Method declaration
ast.KindLessThanToken, // Generic Method declaration
ast.KindExclamationToken, // Non-null assertion on property name
ast.KindColonToken, // Type Annotation for declaration
ast.KindEqualsToken, // Initializer for declaration
ast.KindQuestionToken: // Not valid, but permitted so that it gets caught later on.
return true
}
// Covers
// - Semicolons (declaration termination)
// - Closing braces (end-of-class, must be declaration)
// - End-of-files (not valid, but permitted so that it gets caught later on)
// - Line-breaks (enabling *automatic semicolon insertion*)
return p.canParseSemicolon()
}
return false
}
func (p *Parser) canParseSemicolon() bool {
// If there's a real semicolon, then we can always parse it out.
// We can parse out an optional semicolon in ASI cases in the following cases.
return p.token == ast.KindSemicolonToken || p.token == ast.KindCloseBraceToken || p.token == ast.KindEndOfFile || p.hasPrecedingLineBreak()
}
func (p *Parser) tryParseSemicolon() bool {
if !p.canParseSemicolon() {
return false
}
if p.token == ast.KindSemicolonToken {
// consume the semicolon if it was explicitly provided.
p.nextToken()
}
return true
}
func (p *Parser) parseSemicolon() bool {
return p.tryParseSemicolon() || p.parseExpected(ast.KindSemicolonToken)
}
func (p *Parser) isLiteralPropertyName() bool {
return tokenIsIdentifierOrKeyword(p.token) || p.token == ast.KindStringLiteral || p.token == ast.KindNumericLiteral || p.token == ast.KindBigIntLiteral
}
func (p *Parser) isStartOfStatement() bool {
switch p.token {
// 'catch' and 'finally' do not actually indicate that the code is part of a statement,
// however, we say they are here so that we may gracefully parse them and error later.
case ast.KindAtToken, ast.KindSemicolonToken, ast.KindOpenBraceToken, ast.KindVarKeyword, ast.KindLetKeyword,
ast.KindUsingKeyword, ast.KindFunctionKeyword, ast.KindClassKeyword, ast.KindEnumKeyword, ast.KindIfKeyword,
ast.KindDoKeyword, ast.KindWhileKeyword, ast.KindForKeyword, ast.KindContinueKeyword, ast.KindBreakKeyword,
ast.KindReturnKeyword, ast.KindWithKeyword, ast.KindSwitchKeyword, ast.KindThrowKeyword, ast.KindTryKeyword,
ast.KindDebuggerKeyword, ast.KindCatchKeyword, ast.KindFinallyKeyword:
return true
case ast.KindImportKeyword:
return p.isStartOfDeclaration() || p.isNextTokenOpenParenOrLessThanOrDot()
case ast.KindConstKeyword, ast.KindExportKeyword:
return p.isStartOfDeclaration()
case ast.KindAsyncKeyword, ast.KindDeclareKeyword, ast.KindInterfaceKeyword, ast.KindModuleKeyword, ast.KindNamespaceKeyword,
ast.KindTypeKeyword, ast.KindGlobalKeyword, ast.KindDeferKeyword:
// When these don't start a declaration, they're an identifier in an expression statement
return true
case ast.KindAccessorKeyword, ast.KindPublicKeyword, ast.KindPrivateKeyword, ast.KindProtectedKeyword, ast.KindStaticKeyword,
ast.KindReadonlyKeyword:
// When these don't start a declaration, they may be the start of a class member if an identifier
// immediately follows. Otherwise they're an identifier in an expression statement.
return p.isStartOfDeclaration() || !p.lookAhead((*Parser).nextTokenIsIdentifierOrKeywordOnSameLine)
default:
return p.isStartOfExpression()
}
}
func (p *Parser) isStartOfDeclaration() bool {
return p.lookAhead((*Parser).scanStartOfDeclaration)
}
func (p *Parser) scanStartOfDeclaration() bool {
for {
switch p.token {
case ast.KindVarKeyword, ast.KindLetKeyword, ast.KindConstKeyword, ast.KindFunctionKeyword, ast.KindClassKeyword,
ast.KindEnumKeyword:
return true
case ast.KindUsingKeyword:
return p.isUsingDeclaration()
case ast.KindAwaitKeyword:
return p.isAwaitUsingDeclaration()
// 'declare', 'module', 'namespace', 'interface'* and 'type' are all legal JavaScript identifiers;
// however, an identifier cannot be followed by another identifier on the same line. This is what we
// count on to parse out the respective declarations. For instance, we exploit this to say that
//
// namespace n
//
// can be none other than the beginning of a namespace declaration, but need to respect that JavaScript sees
//
// namespace
// n
//
// as the identifier 'namespace' on one line followed by the identifier 'n' on another.
// We need to look one token ahead to see if it permissible to try parsing a declaration.
//
// *Note*: 'interface' is actually a strict mode reserved word. So while
//
// "use strict"
// interface
// I {}
//
// could be legal, it would add complexity for very little gain.
case ast.KindInterfaceKeyword, ast.KindTypeKeyword, ast.KindDeferKeyword:
return p.nextTokenIsIdentifierOnSameLine()
case ast.KindModuleKeyword, ast.KindNamespaceKeyword:
return p.nextTokenIsIdentifierOrStringLiteralOnSameLine()
case ast.KindAbstractKeyword, ast.KindAccessorKeyword, ast.KindAsyncKeyword, ast.KindDeclareKeyword, ast.KindPrivateKeyword,
ast.KindProtectedKeyword, ast.KindPublicKeyword, ast.KindReadonlyKeyword:
previousToken := p.token
p.nextToken()
// ASI takes effect for this modifier.
if p.hasPrecedingLineBreak() {
return false
}
if previousToken == ast.KindDeclareKeyword && p.token == ast.KindTypeKeyword {
// If we see 'declare type', then commit to parsing a type alias. parseTypeAliasDeclaration will
// report Line_break_not_permitted_here if needed.
return true
}
continue
case ast.KindGlobalKeyword:
p.nextToken()
return p.token == ast.KindOpenBraceToken || p.token == ast.KindIdentifier || p.token == ast.KindExportKeyword
case ast.KindImportKeyword:
p.nextToken()
return p.token == ast.KindDeferKeyword || p.token == ast.KindStringLiteral || p.token == ast.KindAsteriskToken || p.token == ast.KindOpenBraceToken || tokenIsIdentifierOrKeyword(p.token)
case ast.KindExportKeyword:
p.nextToken()
if p.token == ast.KindEqualsToken || p.token == ast.KindAsteriskToken || p.token == ast.KindOpenBraceToken ||
p.token == ast.KindDefaultKeyword || p.token == ast.KindAsKeyword || p.token == ast.KindAtToken {
return true
}
if p.token == ast.KindTypeKeyword {
p.nextToken()
return p.token == ast.KindAsteriskToken || p.token == ast.KindOpenBraceToken || p.isIdentifier() && !p.hasPrecedingLineBreak()
}
continue
case ast.KindStaticKeyword:
p.nextToken()
continue
}
return false
}
}
func (p *Parser) isStartOfExpression() bool {
if p.isStartOfLeftHandSideExpression() {
return true
}
switch p.token {
case ast.KindPlusToken, ast.KindMinusToken, ast.KindTildeToken, ast.KindExclamationToken, ast.KindDeleteKeyword,
ast.KindTypeOfKeyword, ast.KindVoidKeyword, ast.KindPlusPlusToken, ast.KindMinusMinusToken, ast.KindLessThanToken,
ast.KindAwaitKeyword, ast.KindYieldKeyword, ast.KindPrivateIdentifier, ast.KindAtToken:
// Yield/await always starts an expression. Either it is an identifier (in which case
// it is definitely an expression). Or it's a keyword (either because we're in
// a generator or async function, or in strict mode (or both)) and it started a yield or await expression.
return true
}
// Error tolerance. If we see the start of some binary operator, we consider
// that the start of an expression. That way we'll parse out a missing identifier,
// give a good message about an identifier being missing, and then consume the
// rest of the binary expression.
if p.isBinaryOperator() {
return true
}
return p.isIdentifier()
}
func (p *Parser) isStartOfLeftHandSideExpression() bool {
switch p.token {
case ast.KindThisKeyword, ast.KindSuperKeyword, ast.KindNullKeyword, ast.KindTrueKeyword, ast.KindFalseKeyword,
ast.KindNumericLiteral, ast.KindBigIntLiteral, ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral, ast.KindTemplateHead,
ast.KindOpenParenToken, ast.KindOpenBracketToken, ast.KindOpenBraceToken, ast.KindFunctionKeyword, ast.KindClassKeyword,
ast.KindNewKeyword, ast.KindSlashToken, ast.KindSlashEqualsToken, ast.KindIdentifier:
return true
case ast.KindImportKeyword:
return p.isNextTokenOpenParenOrLessThanOrDot()
}
return p.isIdentifier()
}
func (p *Parser) isStartOfType(inStartOfParameter bool) bool {
switch p.token {
case ast.KindAnyKeyword, ast.KindUnknownKeyword, ast.KindStringKeyword, ast.KindNumberKeyword, ast.KindBigIntKeyword,
ast.KindBooleanKeyword, ast.KindReadonlyKeyword, ast.KindSymbolKeyword, ast.KindUniqueKeyword, ast.KindVoidKeyword,
ast.KindUndefinedKeyword, ast.KindNullKeyword, ast.KindThisKeyword, ast.KindTypeOfKeyword, ast.KindNeverKeyword,
ast.KindOpenBraceToken, ast.KindOpenBracketToken, ast.KindLessThanToken, ast.KindBarToken, ast.KindAmpersandToken,
ast.KindNewKeyword, ast.KindStringLiteral, ast.KindNumericLiteral, ast.KindBigIntLiteral, ast.KindTrueKeyword,
ast.KindFalseKeyword, ast.KindObjectKeyword, ast.KindAsteriskToken, ast.KindQuestionToken, ast.KindExclamationToken,
ast.KindDotDotDotToken, ast.KindInferKeyword, ast.KindImportKeyword, ast.KindAssertsKeyword, ast.KindNoSubstitutionTemplateLiteral,
ast.KindTemplateHead:
return true
case ast.KindFunctionKeyword:
return !inStartOfParameter
case ast.KindMinusToken:
return !inStartOfParameter && p.lookAhead((*Parser).nextTokenIsNumericOrBigIntLiteral)
case ast.KindOpenParenToken:
// Only consider '(' the start of a type if followed by ')', '...', an identifier, a modifier,
// or something that starts a type. We don't want to consider things like '(1)' a type.
return !inStartOfParameter && p.lookAhead((*Parser).nextIsParenthesizedOrFunctionType)
}
return p.isIdentifier()
}
func (p *Parser) nextTokenIsNumericOrBigIntLiteral() bool {
p.nextToken()
return p.token == ast.KindNumericLiteral || p.token == ast.KindBigIntLiteral
}
func (p *Parser) nextIsParenthesizedOrFunctionType() bool {
p.nextToken()
return p.token == ast.KindCloseParenToken || p.isStartOfParameter(false /*isJSDocParameter*/) || p.isStartOfType(false /*inStartOfParameter*/)
}
func (p *Parser) isStartOfParameter(isJSDocParameter bool) bool {
return p.token == ast.KindDotDotDotToken ||
p.isBindingIdentifierOrPrivateIdentifierOrPattern() ||
ast.IsModifierKind(p.token) ||
p.token == ast.KindAtToken ||
p.isStartOfType(!isJSDocParameter /*inStartOfParameter*/)
}
func (p *Parser) isBindingIdentifierOrPrivateIdentifierOrPattern() bool {
return p.token == ast.KindOpenBraceToken || p.token == ast.KindOpenBracketToken || p.token == ast.KindPrivateIdentifier || p.isBindingIdentifier()
}
func (p *Parser) isNextTokenOpenParenOrLessThanOrDot() bool {
return p.lookAhead((*Parser).nextTokenIsOpenParenOrLessThanOrDot)
}
func (p *Parser) nextTokenIsOpenParenOrLessThanOrDot() bool {
switch p.nextToken() {
case ast.KindOpenParenToken, ast.KindLessThanToken, ast.KindDotToken:
return true
}
return false
}
func (p *Parser) nextTokenIsIdentifierOnSameLine() bool {
p.nextToken()
return p.isIdentifier() && !p.hasPrecedingLineBreak()
}
func (p *Parser) nextTokenIsIdentifierOrStringLiteralOnSameLine() bool {
p.nextToken()
return (p.isIdentifier() || p.token == ast.KindStringLiteral) && !p.hasPrecedingLineBreak()
}
// Ignore strict mode flag because we will report an error in type checker instead.
func (p *Parser) isIdentifier() bool {
if p.token == ast.KindIdentifier {
return true
}
// If we have a 'yield' keyword, and we're in the [yield] context, then 'yield' is
// considered a keyword and is not an identifier.
// If we have a 'await' keyword, and we're in the [Await] context, then 'await' is
// considered a keyword and is not an identifier.
if p.token == ast.KindYieldKeyword && p.inYieldContext() || p.token == ast.KindAwaitKeyword && p.inAwaitContext() {
return false
}
return p.token > ast.KindLastReservedWord
}
func (p *Parser) isBindingIdentifier() bool {
// `let await`/`let yield` in [Yield] or [Await] are allowed here and disallowed in the binder.
return p.token == ast.KindIdentifier || p.token > ast.KindLastReservedWord
}
func (p *Parser) isImportAttributeName() bool {
return tokenIsIdentifierOrKeyword(p.token) || p.token == ast.KindStringLiteral
}
func (p *Parser) isBinaryOperator() bool {
if p.inDisallowInContext() && p.token == ast.KindInKeyword {
return false
}
return ast.GetBinaryOperatorPrecedence(p.token) != ast.OperatorPrecedenceInvalid
}
func (p *Parser) isValidHeritageClauseObjectLiteral() bool {
return p.lookAhead((*Parser).nextIsValidHeritageClauseObjectLiteral)
}
func (p *Parser) nextIsValidHeritageClauseObjectLiteral() bool {
if p.nextToken() == ast.KindCloseBraceToken {
// if we see "extends {}" then only treat the {} as what we're extending (and not
// the class body) if we have:
//
// extends {} {
// extends {},
// extends {} extends
// extends {} implements
next := p.nextToken()
return next == ast.KindCommaToken || next == ast.KindOpenBraceToken || next == ast.KindExtendsKeyword || next == ast.KindImplementsKeyword
}
return true
}
func (p *Parser) isHeritageClause() bool {
return p.token == ast.KindExtendsKeyword || p.token == ast.KindImplementsKeyword
}
func (p *Parser) isHeritageClauseExtendsOrImplementsKeyword() bool {
return p.isHeritageClause() && p.lookAhead((*Parser).nextIsStartOfExpression)
}
func (p *Parser) nextIsStartOfExpression() bool {
p.nextToken()
return p.isStartOfExpression()
}
func (p *Parser) isUsingDeclaration() bool {
// 'using' always starts a lexical declaration if followed by an identifier. We also eagerly parse
// |ObjectBindingPattern| so that we can report a grammar error during check. We don't parse out
// |ArrayBindingPattern| since it potentially conflicts with element access (i.e., `using[x]`).
return p.lookAhead(func(p *Parser) bool {
return p.nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLine( /*disallowOf*/ false)
})
}
func (p *Parser) nextTokenIsEqualsOrSemicolonOrColonToken() bool {
p.nextToken()
return p.token == ast.KindEqualsToken || p.token == ast.KindSemicolonToken || p.token == ast.KindColonToken
}
func (p *Parser) nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLine(disallowOf bool) bool {
p.nextToken()
if disallowOf && p.token == ast.KindOfKeyword {
return p.lookAhead((*Parser).nextTokenIsEqualsOrSemicolonOrColonToken)
}
return p.isBindingIdentifier() || p.token == ast.KindOpenBraceToken && !p.hasPrecedingLineBreak()
}
func (p *Parser) nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLineDisallowOf() bool {
return p.nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLine( /*disallowOf*/ true)
}
func (p *Parser) isAwaitUsingDeclaration() bool {
return p.lookAhead((*Parser).nextIsUsingKeywordThenBindingIdentifierOrStartOfObjectDestructuringOnSameLine)
}
func (p *Parser) nextIsUsingKeywordThenBindingIdentifierOrStartOfObjectDestructuringOnSameLine() bool {
return p.nextToken() == ast.KindUsingKeyword && p.nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLine( /*disallowOf*/ false)
}
func (p *Parser) nextTokenIsTokenStringLiteral() bool {
return p.nextToken() == ast.KindStringLiteral
}
func (p *Parser) setContextFlags(flags ast.NodeFlags, value bool) {
if value {
p.contextFlags |= flags
} else {
p.contextFlags &= ^flags
}
}
func doInContext[T any](p *Parser, flags ast.NodeFlags, value bool, f func(p *Parser) T) T {
saveContextFlags := p.contextFlags
p.setContextFlags(flags, value)
result := f(p)
p.contextFlags = saveContextFlags
return result
}
func (p *Parser) inYieldContext() bool {
return p.contextFlags&ast.NodeFlagsYieldContext != 0
}
func (p *Parser) inDisallowInContext() bool {
return p.contextFlags&ast.NodeFlagsDisallowInContext != 0
}
func (p *Parser) inDisallowConditionalTypesContext() bool {
return p.contextFlags&ast.NodeFlagsDisallowConditionalTypesContext != 0
}
func (p *Parser) inDecoratorContext() bool {
return p.contextFlags&ast.NodeFlagsDecoratorContext != 0
}
func (p *Parser) inAwaitContext() bool {
return p.contextFlags&ast.NodeFlagsAwaitContext != 0
}
func (p *Parser) skipRangeTrivia(textRange core.TextRange) core.TextRange {
return core.NewTextRange(scanner.SkipTrivia(p.sourceText, textRange.Pos()), textRange.End())
}
func isReservedWord(token ast.Kind) bool {
return ast.KindFirstReservedWord <= token && token <= ast.KindLastReservedWord
}
func tagNamesAreEquivalent(lhs *ast.Expression, rhs *ast.Expression) bool {
if lhs.Kind != rhs.Kind {
return false
}
switch lhs.Kind {
case ast.KindIdentifier:
return lhs.AsIdentifier().Text == rhs.AsIdentifier().Text
case ast.KindThisKeyword:
return true
case ast.KindJsxNamespacedName:
return lhs.AsJsxNamespacedName().Namespace.AsIdentifier().Text == rhs.AsJsxNamespacedName().Namespace.AsIdentifier().Text &&
lhs.AsJsxNamespacedName().Name().AsIdentifier().Text == rhs.AsJsxNamespacedName().Name().AsIdentifier().Text
case ast.KindPropertyAccessExpression:
return lhs.AsPropertyAccessExpression().Name().Text() == rhs.AsPropertyAccessExpression().Name().Text() &&
tagNamesAreEquivalent(lhs.AsPropertyAccessExpression().Expression, rhs.AsPropertyAccessExpression().Expression)
}
panic("Unhandled case in tagNamesAreEquivalent")
}
func attachFileToDiagnostics(diagnostics []*ast.Diagnostic, file *ast.SourceFile) []*ast.Diagnostic {
for _, d := range diagnostics {
d.SetFile(file)
for _, r := range d.RelatedInformation() {
r.SetFile(file)
}
}
return diagnostics
}
func getCommentPragmas(f *ast.NodeFactory, sourceText string) (pragmas []ast.Pragma) {
for commentRange := range scanner.GetLeadingCommentRanges(f, sourceText, 0) {
comment := sourceText[commentRange.Pos():commentRange.End()]
pragmas = append(pragmas, extractPragmas(commentRange, comment)...)
}
return pragmas
}
func extractPragmas(commentRange ast.CommentRange, text string) []ast.Pragma {
if commentRange.Kind == ast.KindSingleLineCommentTrivia {
pos := 2
tripleSlash := match(text, pos, "/")
if tripleSlash {
pos++
}
pos = skipBlanks(text, pos)
if tripleSlash && match(text, pos, "<") {
tagName := extractName(text, pos+1)
if tagName != "reference" {
return nil
}
pos += 10
args := make(map[string]ast.PragmaArgument)
for {
pos = skipBlanks(text, pos)
if match(text, pos, "/>") {
break
}
argName := extractName(text, pos)
if argName == "" {
break
}
pos = skipBlanks(text, pos+len(argName))
if !match(text, pos, "=") {
break
}
pos = skipBlanks(text, pos+1)
value, ok := extractQuotedString(text, pos)
if !ok {
break
}
args[argName] = ast.PragmaArgument{
Name: argName,
Value: value,
TextRange: core.NewTextRange(commentRange.Pos()+pos+1, commentRange.Pos()+pos+1+len(value)),
}
pos += len(value) + 2
}
return []ast.Pragma{{
CommentRange: commentRange,
Name: "reference",
Args: args,
}}
}
if match(text, pos, "@") {
pos++
pragmaName := extractName(text, pos)
if !(pragmaName == "ts-check" || pragmaName == "ts-nocheck") {
return nil
}
return []ast.Pragma{{
CommentRange: commentRange,
Name: pragmaName,
}}
}
}
if commentRange.Kind == ast.KindMultiLineCommentTrivia {
text = strings.TrimSuffix(text, "*/")
pos := 2
var pragmas []ast.Pragma
for {
if pos = skipTo(text, pos, "@"); pos < 0 {
break
}
pragmaName := extractName(text, pos+1)
if !(pragmaName == "jsx" || pragmaName == "jsxfrag" || pragmaName == "jsximportsource" || pragmaName == "jsxruntime") {
break
}
start := skipBlanks(text, pos+len(pragmaName)+1)
pos = skipNonBlanks(text, start)
if pos == start {
break
}
args := make(map[string]ast.PragmaArgument, 1)
args["factory"] = ast.PragmaArgument{
Name: "factory",
Value: text[start:pos],
TextRange: core.NewTextRange(commentRange.Pos()+start, commentRange.Pos()+pos),
}
pragmas = append(pragmas, ast.Pragma{
CommentRange: commentRange,
Name: pragmaName,
Args: args,
})
}
return pragmas
}
return nil
}
func match(text string, pos int, s string) bool {
return strings.HasPrefix(text[pos:], s)
}
func skipBlanks(text string, pos int) int {
for pos < len(text) && (text[pos] == ' ' || text[pos] == '\t') {
pos++
}
return pos
}
func skipNonBlanks(text string, pos int) int {
for pos < len(text) && (text[pos] != ' ' && text[pos] != '\t' && text[pos] != '\r' && text[pos] != '\n') {
pos++
}
return pos
}
func skipTo(text string, pos int, s string) int {
i := strings.Index(text[pos:], s)
if i < 0 {
return -1
}
return pos + i
}
func extractName(text string, pos int) string {
start := pos
for pos < len(text) && (text[pos] >= 'A' && text[pos] <= 'Z' || text[pos] >= 'a' && text[pos] <= 'z' || text[pos] == '-') {
pos++
}
return strings.ToLower(text[start:pos])
}
func extractQuotedString(text string, pos int) (string, bool) {
if pos == len(text) {
return "", false
}
quote := text[pos]
if quote != '\'' && quote != '"' {
return "", false
}
pos++
start := pos
for pos < len(text) && text[pos] != quote {
pos++
}
if pos == len(text) {
return "", false
}
return text[start:pos], true
}
func (p *Parser) processPragmasIntoFields(context *ast.SourceFile) {
context.CheckJsDirective = nil
context.ReferencedFiles = nil
context.TypeReferenceDirectives = nil
context.LibReferenceDirectives = nil
// context.AmdDependencies = nil
for _, pragma := range context.Pragmas {
switch pragma.Name {
case "reference":
types, typesOk := pragma.Args["types"]
lib, libOk := pragma.Args["lib"]
path, pathOk := pragma.Args["path"]
resolutionMode, resolutionModeOk := pragma.Args["resolution-mode"]
preserve, preserveOk := pragma.Args["preserve"]
noDefaultLib, noDefaultLibOk := pragma.Args["no-default-lib"]
switch {
case noDefaultLibOk && noDefaultLib.Value == "true":
// Ignored.
case typesOk:
var parsed core.ResolutionMode
if resolutionModeOk {
parsed = parseResolutionMode(resolutionMode.Value, resolutionMode.Pos(), resolutionMode.End() /*, reportDiagnostic*/)
}
context.TypeReferenceDirectives = append(context.TypeReferenceDirectives, &ast.FileReference{
TextRange: types.TextRange,
FileName: types.Value,
ResolutionMode: parsed,
Preserve: preserveOk && preserve.Value == "true",
})
case libOk:
context.LibReferenceDirectives = append(context.LibReferenceDirectives, &ast.FileReference{
TextRange: lib.TextRange,
FileName: lib.Value,
Preserve: preserveOk && preserve.Value == "true",
})
case pathOk:
context.ReferencedFiles = append(context.ReferencedFiles, &ast.FileReference{
TextRange: path.TextRange,
FileName: path.Value,
Preserve: preserveOk && preserve.Value == "true",
})
default:
p.parseErrorAtRange(pragma.TextRange, diagnostics.Invalid_reference_directive_syntax)
}
case "ts-check", "ts-nocheck":
// _last_ of either nocheck or check in a file is the "winner"
for _, directive := range context.Pragmas {
if context.CheckJsDirective == nil || directive.TextRange.Pos() > context.CheckJsDirective.Range.Pos() {
context.CheckJsDirective = &ast.CheckJsDirective{
Enabled: directive.Name == "ts-check",
Range: directive.CommentRange,
}
}
}
case "jsx", "jsxfrag", "jsximportsource", "jsxruntime":
// Nothing to do here
default:
panic("Unhandled pragma kind: " + pragma.Name)
}
}
}
func parseResolutionMode(mode string, pos int, end int /*reportDiagnostic: PragmaDiagnosticReporter*/) (resolutionKind core.ResolutionMode) {
if mode == "import" {
resolutionKind = core.ModuleKindESNext
}
if mode == "require" {
resolutionKind = core.ModuleKindCommonJS
}
return resolutionKind
// reportDiagnostic(pos, end - pos, Diagnostics.resolution_mode_should_be_either_require_or_import);
// return undefined;
}
func (p *Parser) jsErrorAtRange(loc core.TextRange, message *diagnostics.Message, args ...any) {
p.jsDiagnostics = append(p.jsDiagnostics, ast.NewDiagnostic(nil, core.NewTextRange(scanner.SkipTrivia(p.sourceText, loc.Pos()), loc.End()), message, args...))
}
func (p *Parser) checkJSSyntax(node *ast.Node) *ast.Node {
if node.Flags&ast.NodeFlagsJavaScriptFile == 0 || node.Flags&(ast.NodeFlagsJSDoc|ast.NodeFlagsReparsed) != 0 {
return node
}
switch node.Kind {
case ast.KindParameter, ast.KindPropertyDeclaration, ast.KindMethodDeclaration:
if token := node.PostfixToken(); token != nil && token.Flags&ast.NodeFlagsReparsed == 0 && ast.IsQuestionToken(token) {
p.jsErrorAtRange(token.Loc, diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, "?")
}
fallthrough
case ast.KindMethodSignature, ast.KindConstructor, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindFunctionExpression,
ast.KindFunctionDeclaration, ast.KindArrowFunction, ast.KindVariableDeclaration, ast.KindIndexSignature:
if ast.IsFunctionLike(node) && node.Body() == nil {
p.jsErrorAtRange(node.Loc, diagnostics.Signature_declarations_can_only_be_used_in_TypeScript_files)
} else if t := node.Type(); t != nil && t.Flags&ast.NodeFlagsReparsed == 0 {
p.jsErrorAtRange(t.Loc, diagnostics.Type_annotations_can_only_be_used_in_TypeScript_files)
}
case ast.KindImportDeclaration:
if clause := node.AsImportDeclaration().ImportClause; clause != nil && clause.IsTypeOnly() {
p.jsErrorAtRange(node.Loc, diagnostics.X_0_declarations_can_only_be_used_in_TypeScript_files, "import type")
}
case ast.KindExportDeclaration:
if node.IsTypeOnly() {
p.jsErrorAtRange(node.Loc, diagnostics.X_0_declarations_can_only_be_used_in_TypeScript_files, "export type")
}
case ast.KindImportSpecifier:
if node.IsTypeOnly() {
p.jsErrorAtRange(node.Loc, diagnostics.X_0_declarations_can_only_be_used_in_TypeScript_files, "import...type")
}
case ast.KindExportSpecifier:
if node.IsTypeOnly() {
p.jsErrorAtRange(node.Loc, diagnostics.X_0_declarations_can_only_be_used_in_TypeScript_files, "export...type")
}
case ast.KindImportEqualsDeclaration:
p.jsErrorAtRange(node.Loc, diagnostics.X_import_can_only_be_used_in_TypeScript_files)
case ast.KindExportAssignment:
if node.AsExportAssignment().IsExportEquals {
p.jsErrorAtRange(node.Loc, diagnostics.X_export_can_only_be_used_in_TypeScript_files)
}
case ast.KindHeritageClause:
if node.AsHeritageClause().Token == ast.KindImplementsKeyword {
p.jsErrorAtRange(node.Loc, diagnostics.X_implements_clauses_can_only_be_used_in_TypeScript_files)
}
case ast.KindInterfaceDeclaration:
p.jsErrorAtRange(node.Name().Loc, diagnostics.X_0_declarations_can_only_be_used_in_TypeScript_files, "interface")
case ast.KindModuleDeclaration:
p.jsErrorAtRange(node.Name().Loc, diagnostics.X_0_declarations_can_only_be_used_in_TypeScript_files, scanner.TokenToString(node.AsModuleDeclaration().Keyword))
case ast.KindTypeAliasDeclaration:
p.jsErrorAtRange(node.Name().Loc, diagnostics.Type_aliases_can_only_be_used_in_TypeScript_files)
case ast.KindEnumDeclaration:
p.jsErrorAtRange(node.Name().Loc, diagnostics.X_0_declarations_can_only_be_used_in_TypeScript_files, "enum")
case ast.KindNonNullExpression:
p.jsErrorAtRange(node.Loc, diagnostics.Non_null_assertions_can_only_be_used_in_TypeScript_files)
case ast.KindAsExpression:
p.jsErrorAtRange(node.Type().Loc, diagnostics.Type_assertion_expressions_can_only_be_used_in_TypeScript_files)
case ast.KindSatisfiesExpression:
p.jsErrorAtRange(node.Type().Loc, diagnostics.Type_satisfaction_expressions_can_only_be_used_in_TypeScript_files)
}
// Check absence of type parameters, type arguments and non-JavaScript modifiers
switch node.Kind {
case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindMethodDeclaration, ast.KindConstructor, ast.KindGetAccessor,
ast.KindSetAccessor, ast.KindFunctionExpression, ast.KindFunctionDeclaration, ast.KindArrowFunction:
if list := node.TypeParameterList(); list != nil && core.Some(list.Nodes, func(n *ast.Node) bool { return n.Flags&ast.NodeFlagsReparsed == 0 }) {
p.jsErrorAtRange(list.Loc, diagnostics.Type_parameter_declarations_can_only_be_used_in_TypeScript_files)
}
fallthrough
case ast.KindVariableStatement, ast.KindPropertyDeclaration:
if modifiers := node.Modifiers(); modifiers != nil {
for _, modifier := range modifiers.Nodes {
if modifier.Flags&ast.NodeFlagsReparsed == 0 && modifier.Kind != ast.KindDecorator && ast.ModifierToFlag(modifier.Kind)&ast.ModifierFlagsJavaScript == 0 {
p.jsErrorAtRange(modifier.Loc, diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, scanner.TokenToString(modifier.Kind))
}
}
}
case ast.KindParameter:
if core.Some(node.ModifierNodes(), ast.IsModifier) {
p.jsErrorAtRange(node.Modifiers().Loc, diagnostics.Parameter_modifiers_can_only_be_used_in_TypeScript_files)
}
case ast.KindCallExpression, ast.KindNewExpression, ast.KindExpressionWithTypeArguments, ast.KindJsxSelfClosingElement,
ast.KindJsxOpeningElement, ast.KindTaggedTemplateExpression:
if list := node.TypeArgumentList(); list != nil && core.Some(list.Nodes, func(n *ast.Node) bool { return n.Flags&ast.NodeFlagsReparsed == 0 }) {
p.jsErrorAtRange(list.Loc, diagnostics.Type_arguments_can_only_be_used_in_TypeScript_files)
}
}
return node
}