aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/go/parser/parser.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/go/parser/parser.go')
-rw-r--r--libgo/go/go/parser/parser.go152
1 files changed, 124 insertions, 28 deletions
diff --git a/libgo/go/go/parser/parser.go b/libgo/go/go/parser/parser.go
index a122baf0879..e362e13a7b6 100644
--- a/libgo/go/go/parser/parser.go
+++ b/libgo/go/go/parser/parser.go
@@ -40,6 +40,13 @@ type parser struct {
tok token.Token // one token look-ahead
lit string // token literal
+ // Error recovery
+ // (used to limit the number of calls to syncXXX functions
+ // w/o making scanning progress - avoids potential endless
+ // loops across multiple parser functions during error recovery)
+ syncPos token.Pos // last synchronization position
+ syncCnt int // number of calls to syncXXX without progress
+
// Non-syntactic parser control
exprLev int // < 0: in control clause, >= 0: in expression
@@ -362,18 +369,36 @@ func (p *parser) expect(tok token.Token) token.Pos {
// expectClosing is like expect but provides a better error message
// for the common case of a missing comma before a newline.
//
-func (p *parser) expectClosing(tok token.Token, construct string) token.Pos {
+func (p *parser) expectClosing(tok token.Token, context string) token.Pos {
if p.tok != tok && p.tok == token.SEMICOLON && p.lit == "\n" {
- p.error(p.pos, "missing ',' before newline in "+construct)
+ p.error(p.pos, "missing ',' before newline in "+context)
p.next()
}
return p.expect(tok)
}
func (p *parser) expectSemi() {
+ // semicolon is optional before a closing ')' or '}'
if p.tok != token.RPAREN && p.tok != token.RBRACE {
- p.expect(token.SEMICOLON)
+ if p.tok == token.SEMICOLON {
+ p.next()
+ } else {
+ p.errorExpected(p.pos, "';'")
+ syncStmt(p)
+ }
+ }
+}
+
+func (p *parser) atComma(context string) bool {
+ if p.tok == token.COMMA {
+ return true
+ }
+ if p.tok == token.SEMICOLON && p.lit == "\n" {
+ p.error(p.pos, "missing ',' before newline in "+context)
+ return true // "insert" the comma and continue
+
}
+ return false
}
func assert(cond bool, msg string) {
@@ -382,6 +407,68 @@ func assert(cond bool, msg string) {
}
}
+// syncStmt advances to the next statement.
+// Used for synchronization after an error.
+//
+func syncStmt(p *parser) {
+ for {
+ switch p.tok {
+ case token.BREAK, token.CONST, token.CONTINUE, token.DEFER,
+ token.FALLTHROUGH, token.FOR, token.GO, token.GOTO,
+ token.IF, token.RETURN, token.SELECT, token.SWITCH,
+ token.TYPE, token.VAR:
+ // Return only if parser made some progress since last
+ // sync or if it has not reached 10 sync calls without
+ // progress. Otherwise consume at least one token to
+ // avoid an endless parser loop (it is possible that
+ // both parseOperand and parseStmt call syncStmt and
+ // correctly do not advance, thus the need for the
+ // invocation limit p.syncCnt).
+ if p.pos == p.syncPos && p.syncCnt < 10 {
+ p.syncCnt++
+ return
+ }
+ if p.pos > p.syncPos {
+ p.syncPos = p.pos
+ p.syncCnt = 0
+ return
+ }
+ // Reaching here indicates a parser bug, likely an
+ // incorrect token list in this function, but it only
+ // leads to skipping of possibly correct code if a
+ // previous error is present, and thus is preferred
+ // over a non-terminating parse.
+ case token.EOF:
+ return
+ }
+ p.next()
+ }
+}
+
+// syncDecl advances to the next declaration.
+// Used for synchronization after an error.
+//
+func syncDecl(p *parser) {
+ for {
+ switch p.tok {
+ case token.CONST, token.TYPE, token.VAR:
+ // see comments in syncStmt
+ if p.pos == p.syncPos && p.syncCnt < 10 {
+ p.syncCnt++
+ return
+ }
+ if p.pos > p.syncPos {
+ p.syncPos = p.pos
+ p.syncCnt = 0
+ return
+ }
+ case token.EOF:
+ return
+ }
+ p.next()
+ }
+}
+
// ----------------------------------------------------------------------------
// Identifiers
@@ -522,9 +609,11 @@ func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident {
for i, x := range list {
ident, isIdent := x.(*ast.Ident)
if !isIdent {
- pos := x.Pos()
- p.errorExpected(pos, "identifier")
- ident = &ast.Ident{NamePos: pos, Name: "_"}
+ if _, isBad := x.(*ast.BadExpr); !isBad {
+ // only report error if it's a new one
+ p.errorExpected(x.Pos(), "identifier")
+ }
+ ident = &ast.Ident{NamePos: x.Pos(), Name: "_"}
}
idents[i] = ident
}
@@ -688,7 +777,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
// Go spec: The scope of an identifier denoting a function
// parameter or result variable is the function body.
p.declare(field, nil, scope, ast.Var, idents...)
- if p.tok != token.COMMA {
+ if !p.atComma("parameter list") {
break
}
p.next()
@@ -991,19 +1080,19 @@ func (p *parser) parseOperand(lhs bool) ast.Expr {
case token.FUNC:
return p.parseFuncTypeOrLit()
+ }
- default:
- if typ := p.tryIdentOrType(true); typ != nil {
- // could be type for composite literal or conversion
- _, isIdent := typ.(*ast.Ident)
- assert(!isIdent, "type cannot be identifier")
- return typ
- }
+ if typ := p.tryIdentOrType(true); typ != nil {
+ // could be type for composite literal or conversion
+ _, isIdent := typ.(*ast.Ident)
+ assert(!isIdent, "type cannot be identifier")
+ return typ
}
+ // we have an error
pos := p.pos
p.errorExpected(pos, "operand")
- p.next() // make progress
+ syncStmt(p)
return &ast.BadExpr{From: pos, To: p.pos}
}
@@ -1078,7 +1167,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
ellipsis = p.pos
p.next()
}
- if p.tok != token.COMMA {
+ if !p.atComma("argument list") {
break
}
p.next()
@@ -1118,7 +1207,7 @@ func (p *parser) parseElementList() (list []ast.Expr) {
for p.tok != token.RBRACE && p.tok != token.EOF {
list = append(list, p.parseElement(true))
- if p.tok != token.COMMA {
+ if !p.atComma("composite literal") {
break
}
p.next()
@@ -1262,8 +1351,8 @@ L:
x = p.parseTypeAssertion(p.checkExpr(x))
default:
pos := p.pos
- p.next() // make progress
p.errorExpected(pos, "selector or type assertion")
+ p.next() // make progress
x = &ast.BadExpr{From: pos, To: p.pos}
}
case token.LBRACK:
@@ -1471,7 +1560,10 @@ func (p *parser) parseCallExpr() *ast.CallExpr {
if call, isCall := x.(*ast.CallExpr); isCall {
return call
}
- p.errorExpected(x.Pos(), "function/method call")
+ if _, isBad := x.(*ast.BadExpr); !isBad {
+ // only report error if it's a new one
+ p.errorExpected(x.Pos(), "function/method call")
+ }
return nil
}
@@ -1862,7 +1954,7 @@ func (p *parser) parseStmt() (s ast.Stmt) {
switch p.tok {
case token.CONST, token.TYPE, token.VAR:
- s = &ast.DeclStmt{Decl: p.parseDecl()}
+ s = &ast.DeclStmt{Decl: p.parseDecl(syncStmt)}
case
// tokens that may start an expression
token.IDENT, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operands
@@ -1904,7 +1996,7 @@ func (p *parser) parseStmt() (s ast.Stmt) {
// no statement found
pos := p.pos
p.errorExpected(pos, "statement")
- p.next() // make progress
+ syncStmt(p)
s = &ast.BadStmt{From: pos, To: p.pos}
}
@@ -2095,8 +2187,13 @@ func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
recv := par.List[0]
base := deref(recv.Type)
if _, isIdent := base.(*ast.Ident); !isIdent {
- p.errorExpected(base.Pos(), "(unqualified) identifier")
- par.List = []*ast.Field{{Type: &ast.BadExpr{From: recv.Pos(), To: recv.End()}}}
+ if _, isBad := base.(*ast.BadExpr); !isBad {
+ // only report error if it's a new one
+ p.errorExpected(base.Pos(), "(unqualified) identifier")
+ }
+ par.List = []*ast.Field{
+ {Type: &ast.BadExpr{From: recv.Pos(), To: recv.End()}},
+ }
}
return par
@@ -2152,7 +2249,7 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
return decl
}
-func (p *parser) parseDecl() ast.Decl {
+func (p *parser) parseDecl(sync func(*parser)) ast.Decl {
if p.trace {
defer un(trace(p, "Declaration"))
}
@@ -2174,9 +2271,8 @@ func (p *parser) parseDecl() ast.Decl {
default:
pos := p.pos
p.errorExpected(pos, "declaration")
- p.next() // make progress
- decl := &ast.BadDecl{From: pos, To: p.pos}
- return decl
+ sync(p)
+ return &ast.BadDecl{From: pos, To: p.pos}
}
return p.parseGenDecl(p.tok, f)
@@ -2215,7 +2311,7 @@ func (p *parser) parseFile() *ast.File {
if p.mode&ImportsOnly == 0 {
// rest of package body
for p.tok != token.EOF {
- decls = append(decls, p.parseDecl())
+ decls = append(decls, p.parseDecl(syncDecl))
}
}
}