6588 lines
259 KiB
Go
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
|
|
}
|