mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Instead of a separate check control flow pass (checkcfg.go) operating on nodes, perform this check at parse time on the new syntax tree. Permits this check to be done concurrently, and doesn't depend on the specifics of the symbol's dclstack implementation anymore. The remaining dclstack uses will be removed in a follow-up change. - added CheckBranches Mode flag (so we can turn off the check if we only care about syntactic correctness, e.g. for tests) - adjusted test/goto.go error messages: the new branches checker only reports if a goto jumps into a block, but not which block (we may want to improve this again, eventually) - also, the new branches checker reports one variable that is being jumped over by a goto, but it may not be the first one declared (this is fine either way) - the new branches checker reports additional errors for fixedbugs/issue14006.go (not crucial to avoid those errors) - the new branches checker now correctly reports only variable declarations being jumped over, rather than all declarations (issue 8042). Added respective tests. Fixes #8042. Change-Id: I53b6e1bda189748e1e1fb5b765a8a64337c27d40 Reviewed-on: https://go-review.googlesource.com/39998 Reviewed-by: Matthew Dempsky <mdempsky@google.com>
105 lines
2.9 KiB
Go
105 lines
2.9 KiB
Go
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package syntax
|
|
|
|
import (
|
|
"cmd/internal/src"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
// Mode describes the parser mode.
|
|
type Mode uint
|
|
|
|
// Modes supported by the parser.
|
|
const (
|
|
CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements
|
|
)
|
|
|
|
// Error describes a syntax error. Error implements the error interface.
|
|
type Error struct {
|
|
Pos src.Pos
|
|
Msg string
|
|
}
|
|
|
|
func (err Error) Error() string {
|
|
return fmt.Sprintf("%s: %s", err.Pos, err.Msg)
|
|
}
|
|
|
|
var _ error = Error{} // verify that Error implements error
|
|
|
|
// An ErrorHandler is called for each error encountered reading a .go file.
|
|
type ErrorHandler func(err error)
|
|
|
|
// A Pragma value is a set of flags that augment a function or
|
|
// type declaration. Callers may assign meaning to the flags as
|
|
// appropriate.
|
|
type Pragma uint16
|
|
|
|
// A PragmaHandler is used to process //line and //go: directives as
|
|
// they're scanned. The returned Pragma value will be unioned into the
|
|
// next FuncDecl node.
|
|
type PragmaHandler func(pos src.Pos, text string) Pragma
|
|
|
|
// Parse parses a single Go source file from src and returns the corresponding
|
|
// syntax tree. If there are errors, Parse will return the first error found,
|
|
// and a possibly partially constructed syntax tree, or nil if no correct package
|
|
// clause was found. The base argument is only used for position information.
|
|
//
|
|
// If errh != nil, it is called with each error encountered, and Parse will
|
|
// process as much source as possible. If errh is nil, Parse will terminate
|
|
// immediately upon encountering an error.
|
|
//
|
|
// If a PragmaHandler is provided, it is called with each pragma encountered.
|
|
//
|
|
// The Mode argument is currently ignored.
|
|
func Parse(base *src.PosBase, src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (_ *File, first error) {
|
|
defer func() {
|
|
if p := recover(); p != nil {
|
|
if err, ok := p.(Error); ok {
|
|
first = err
|
|
return
|
|
}
|
|
panic(p)
|
|
}
|
|
}()
|
|
|
|
var p parser
|
|
p.init(base, src, errh, pragh, mode)
|
|
p.next()
|
|
return p.fileOrNil(), p.first
|
|
}
|
|
|
|
// ParseBytes behaves like Parse but it reads the source from the []byte slice provided.
|
|
func ParseBytes(base *src.PosBase, src []byte, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
|
|
return Parse(base, &bytesReader{src}, errh, pragh, mode)
|
|
}
|
|
|
|
type bytesReader struct {
|
|
data []byte
|
|
}
|
|
|
|
func (r *bytesReader) Read(p []byte) (int, error) {
|
|
if len(r.data) > 0 {
|
|
n := copy(p, r.data)
|
|
r.data = r.data[n:]
|
|
return n, nil
|
|
}
|
|
return 0, io.EOF
|
|
}
|
|
|
|
// ParseFile behaves like Parse but it reads the source from the named file.
|
|
func ParseFile(filename string, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
|
|
f, err := os.Open(filename)
|
|
if err != nil {
|
|
if errh != nil {
|
|
errh(err)
|
|
}
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
return Parse(src.NewFileBase(filename, filename), f, errh, pragh, mode)
|
|
}
|