mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: detect and diagnose invalid //go: directive placement
Thie CL changes cmd/compile/internal/syntax to give the gc half of
the compiler more control over pragma handling, so that it can prepare
better errors, diagnose misuse, and so on. Before, the API between
the two was hard-coded as a uint16. Now it is an interface{}.
This should set us up better for future directives.
In addition to the split, this CL emits a "misplaced compiler directive"
error for any directive that is in a place where it has no effect.
I've certainly been confused in the past by adding comments
that were doing nothing and not realizing it. This should help
avoid that kind of confusion.
The rule, now applied consistently, is that a //go: directive
must appear on a line by itself immediately before the declaration
specifier it means to apply to. See cmd/compile/doc.go for
precise text and test/directive.go for examples.
This may cause some code to stop compiling, but that code
was broken. For example, this code formerly applied the
//go:noinline to f (not c) but now will fail to compile:
//go:noinline
const c = 1
func f() {}
Change-Id: Ieba9b8d90a27cfab25de79d2790a895cefe5296f
Reviewed-on: https://go-review.googlesource.com/c/go/+/228578
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
4f27e1d7aa
commit
768201729d
10 changed files with 345 additions and 88 deletions
|
|
@ -241,6 +241,10 @@ func (p *noder) node() {
|
|||
p.setlineno(p.file.PkgName)
|
||||
mkpackage(p.file.PkgName.Value)
|
||||
|
||||
if pragma, ok := p.file.Pragma.(*Pragma); ok {
|
||||
p.checkUnused(pragma)
|
||||
}
|
||||
|
||||
xtop = append(xtop, p.decls(p.file.DeclList)...)
|
||||
|
||||
for _, n := range p.linknames {
|
||||
|
|
@ -313,6 +317,10 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
|
|||
return // avoid follow-on errors if there was a syntax error
|
||||
}
|
||||
|
||||
if pragma, ok := imp.Pragma.(*Pragma); ok {
|
||||
p.checkUnused(pragma)
|
||||
}
|
||||
|
||||
val := p.basicLit(imp.Path)
|
||||
ipkg := importfile(&val)
|
||||
|
||||
|
|
@ -363,6 +371,10 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
|
|||
exprs = p.exprList(decl.Values)
|
||||
}
|
||||
|
||||
if pragma, ok := decl.Pragma.(*Pragma); ok {
|
||||
p.checkUnused(pragma)
|
||||
}
|
||||
|
||||
p.setlineno(decl)
|
||||
return variter(names, typ, exprs)
|
||||
}
|
||||
|
|
@ -384,6 +396,10 @@ func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []*Node {
|
|||
}
|
||||
}
|
||||
|
||||
if pragma, ok := decl.Pragma.(*Pragma); ok {
|
||||
p.checkUnused(pragma)
|
||||
}
|
||||
|
||||
names := p.declNames(decl.NameList)
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
|
|
@ -438,11 +454,13 @@ func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
|
|||
|
||||
param := n.Name.Param
|
||||
param.Ntype = typ
|
||||
param.Pragma = decl.Pragma
|
||||
param.Alias = decl.Alias
|
||||
if param.Alias && param.Pragma != 0 {
|
||||
yyerror("cannot specify directive with type alias")
|
||||
param.Pragma = 0
|
||||
if pragma, ok := decl.Pragma.(*Pragma); ok {
|
||||
if !decl.Alias {
|
||||
param.Pragma = pragma.Flag & TypePragmas
|
||||
pragma.Flag &^= TypePragmas
|
||||
}
|
||||
p.checkUnused(pragma)
|
||||
}
|
||||
|
||||
nod := p.nod(decl, ODCLTYPE, n, nil)
|
||||
|
|
@ -493,10 +511,13 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
|
|||
f.Func.Nname.Name.Defn = f
|
||||
f.Func.Nname.Name.Param.Ntype = t
|
||||
|
||||
pragma := fun.Pragma
|
||||
f.Func.Pragma = fun.Pragma
|
||||
if pragma&Systemstack != 0 && pragma&Nosplit != 0 {
|
||||
yyerrorl(f.Pos, "go:nosplit and go:systemstack cannot be combined")
|
||||
if pragma, ok := fun.Pragma.(*Pragma); ok {
|
||||
f.Func.Pragma = pragma.Flag & FuncPragmas
|
||||
if pragma.Flag&Systemstack != 0 && pragma.Flag&Nosplit != 0 {
|
||||
yyerrorl(f.Pos, "go:nosplit and go:systemstack cannot be combined")
|
||||
}
|
||||
pragma.Flag &^= FuncPragmas
|
||||
p.checkUnused(pragma)
|
||||
}
|
||||
|
||||
if fun.Recv == nil {
|
||||
|
|
@ -1479,13 +1500,58 @@ var allowedStdPragmas = map[string]bool{
|
|||
"go:generate": true,
|
||||
}
|
||||
|
||||
// *Pragma is the value stored in a syntax.Pragma during parsing.
|
||||
type Pragma struct {
|
||||
Flag PragmaFlag // collected bits
|
||||
Pos []PragmaPos // position of each individual flag
|
||||
}
|
||||
|
||||
type PragmaPos struct {
|
||||
Flag PragmaFlag
|
||||
Pos syntax.Pos
|
||||
}
|
||||
|
||||
func (p *noder) checkUnused(pragma *Pragma) {
|
||||
for _, pos := range pragma.Pos {
|
||||
if pos.Flag&pragma.Flag != 0 {
|
||||
p.yyerrorpos(pos.Pos, "misplaced compiler directive")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *noder) checkUnusedDuringParse(pragma *Pragma) {
|
||||
for _, pos := range pragma.Pos {
|
||||
if pos.Flag&pragma.Flag != 0 {
|
||||
p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pragma is called concurrently if files are parsed concurrently.
|
||||
func (p *noder) pragma(pos syntax.Pos, text string) syntax.Pragma {
|
||||
switch {
|
||||
case strings.HasPrefix(text, "line "):
|
||||
func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.Pragma) syntax.Pragma {
|
||||
pragma, _ := old.(*Pragma)
|
||||
if pragma == nil {
|
||||
pragma = new(Pragma)
|
||||
}
|
||||
|
||||
if text == "" {
|
||||
// unused pragma; only called with old != nil.
|
||||
p.checkUnusedDuringParse(pragma)
|
||||
return nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(text, "line ") {
|
||||
// line directives are handled by syntax package
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
if !blankLine {
|
||||
// directive must be on line by itself
|
||||
p.error(syntax.Error{Pos: pos, Msg: "misplaced compiler directive"})
|
||||
return pragma
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(text, "go:linkname "):
|
||||
f := strings.Fields(text)
|
||||
if !(2 <= len(f) && len(f) <= 3) {
|
||||
|
|
@ -1513,7 +1579,8 @@ func (p *noder) pragma(pos syntax.Pos, text string) syntax.Pragma {
|
|||
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("invalid library name %q in cgo_import_dynamic directive", lib)})
|
||||
}
|
||||
p.pragcgo(pos, text)
|
||||
return pragmaValue("go:cgo_import_dynamic")
|
||||
pragma.Flag |= pragmaFlag("go:cgo_import_dynamic")
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case strings.HasPrefix(text, "go:cgo_"):
|
||||
|
|
@ -1530,18 +1597,19 @@ func (p *noder) pragma(pos syntax.Pos, text string) syntax.Pragma {
|
|||
if i := strings.Index(text, " "); i >= 0 {
|
||||
verb = verb[:i]
|
||||
}
|
||||
prag := pragmaValue(verb)
|
||||
flag := pragmaFlag(verb)
|
||||
const runtimePragmas = Systemstack | Nowritebarrier | Nowritebarrierrec | Yeswritebarrierrec
|
||||
if !compiling_runtime && prag&runtimePragmas != 0 {
|
||||
if !compiling_runtime && flag&runtimePragmas != 0 {
|
||||
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)})
|
||||
}
|
||||
if prag == 0 && !allowedStdPragmas[verb] && compiling_std {
|
||||
if flag == 0 && !allowedStdPragmas[verb] && compiling_std {
|
||||
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)})
|
||||
}
|
||||
return prag
|
||||
pragma.Flag |= flag
|
||||
pragma.Pos = append(pragma.Pos, PragmaPos{flag, pos})
|
||||
}
|
||||
|
||||
return 0
|
||||
return pragma
|
||||
}
|
||||
|
||||
// isCgoGeneratedFile reports whether pos is in a file
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue