From 2c6df2e35dd95621d025306a49b863efd77ebf3a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 22 May 2020 15:22:52 -0400 Subject: cmd/compile: reject misplaced go:build comments We are converting from using error-prone ad-hoc syntax // +build lines to less error-prone, standard boolean syntax //go:build lines. The timeline is: Go 1.16: prepare for transition - Builds still use // +build for file selection. - Source files may not contain //go:build without // +build. - Builds fail when a source file contains //go:build lines without // +build lines. <<< Go 1.17: start transition - Builds prefer //go:build for file selection, falling back to // +build for files containing only // +build. - Source files may contain //go:build without // +build (but they won't build with Go 1.16). - Gofmt moves //go:build and // +build lines to proper file locations. - Gofmt introduces //go:build lines into files with only // +build lines. - Go vet rejects files with mismatched //go:build and // +build lines. Go 1.18: complete transition - Go fix removes // +build lines, leaving behind equivalent // +build lines. This CL provides part of the <<< marked line above in the Go 1.16 step: rejecting files containing //go:build but not // +build. The standard go command checks only consider the top of the file. This compiler check, along with a separate go vet check for ignored files, handles the remainder of the file. For #41184. Change-Id: I014006eebfc84ab5943de18bc90449e534f150a2 Reviewed-on: https://go-review.googlesource.com/c/go/+/240601 Trust: Russ Cox Run-TryBot: Russ Cox TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/cmd/compile/internal/gc/noder.go | 1 + 1 file changed, 1 insertion(+) (limited to 'src/cmd/compile/internal/gc/noder.go') diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 5dce533e4b..c63c80dd36 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -242,6 +242,7 @@ func (p *noder) node() { mkpackage(p.file.PkgName.Value) if pragma, ok := p.file.Pragma.(*Pragma); ok { + pragma.Flag &^= GoBuildPragma p.checkUnused(pragma) } -- cgit v1.2.1 From e2931612b04e2ea6be337872c6f4a31c7d7dec54 Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Mon, 12 Oct 2020 15:02:59 +0200 Subject: cmd/compile: rename strlit, Bool, and Int64 *Node accessors The Node type has shortcuts to access bool and int Values: func (n *Node) Int64() int64 for n.Val().U.(*Mpint).Int64() func (n *Node) Bool() bool for n.Val().U.(bool) I was convinced we didn't have one for string literal nodes, until I noticed that we do, it's just called strlit, it's not a method, and it's later in the file: func strlit(n *Node) string This change, for consistency: - Renames strlit to StringVal and makes it a *Node method - Renames Bool and Int64 to BoolVal and Int64Val - Moves StringVal near the other two Change-Id: I18e635384c35eb3a238fd52b1ccd322b1a74d733 Reviewed-on: https://go-review.googlesource.com/c/go/+/261361 Trust: Alberto Donizetti Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/noder.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/cmd/compile/internal/gc/noder.go') diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index c63c80dd36..68d0327cdb 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -774,7 +774,7 @@ func (p *noder) sum(x syntax.Expr) *Node { n := p.expr(x) if Isconst(n, CTSTR) && n.Sym == nil { nstr = n - chunks = append(chunks, strlit(nstr)) + chunks = append(chunks, nstr.StringVal()) } for i := len(adds) - 1; i >= 0; i-- { @@ -784,12 +784,12 @@ func (p *noder) sum(x syntax.Expr) *Node { if Isconst(r, CTSTR) && r.Sym == nil { if nstr != nil { // Collapse r into nstr instead of adding to n. - chunks = append(chunks, strlit(r)) + chunks = append(chunks, r.StringVal()) continue } nstr = r - chunks = append(chunks, strlit(nstr)) + chunks = append(chunks, nstr.StringVal()) } else { if len(chunks) > 1 { nstr.SetVal(Val{U: strings.Join(chunks, "")}) -- cgit v1.2.1 From 178b0f59ff0d8be6d41d0f4099d16bfd3bc21ea7 Mon Sep 17 00:00:00 2001 From: hk Date: Mon, 19 Oct 2020 12:13:25 +0000 Subject: cmd/compile/internal/gc: remove an unused method Change-Id: Ib496d1e9a47d2cf69509ffd0009038b7d34a0149 GitHub-Last-Rev: 5b5589dd03e1d781e4cedc643d5e8a7f8bda2139 GitHub-Pull-Request: golang/go#42060 Reviewed-on: https://go-review.googlesource.com/c/go/+/263618 Run-TryBot: Robert Griesemer Reviewed-by: Matthew Dempsky Trust: Matthew Dempsky Trust: Robert Griesemer TryBot-Result: Go Bot --- src/cmd/compile/internal/gc/noder.go | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/cmd/compile/internal/gc/noder.go') diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 68d0327cdb..85e710086a 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -1438,11 +1438,6 @@ func (p *noder) mkname(name *syntax.Name) *Node { return mkname(p.name(name)) } -func (p *noder) newname(name *syntax.Name) *Node { - // TODO(mdempsky): Set line number? - return newname(p.name(name)) -} - func (p *noder) wrapname(n syntax.Node, x *Node) *Node { // These nodes do not carry line numbers. // Introduce a wrapper node to give them the correct line. -- cgit v1.2.1 From 8bde9b320e25b2d6edf96fa5e694046fea0c04c8 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sun, 19 Jul 2020 00:32:02 -0400 Subject: cmd/compile: add //go:embed support This commit contains the compiler support for //go:embed lines. The go command passes to the compiler an "embed config" that maps literal patterns like *.txt to the set of files to embed. The compiler then lays out the content of those files as static data in the form of an embed.Files or string or []byte in the final object file. The test for this code is the end-to-end test hooking up the embed, cmd/compile, and cmd/go changes, in the next CL. For #41191. Change-Id: I916e57f8cc65871dc0044c13d3f90c252a3fe1bf Reviewed-on: https://go-review.googlesource.com/c/go/+/243944 Trust: Russ Cox Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/gc/noder.go | 145 +++++++++++++++++++++++++++++++---- 1 file changed, 131 insertions(+), 14 deletions(-) (limited to 'src/cmd/compile/internal/gc/noder.go') diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 85e710086a..67d24ef0bc 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -11,6 +11,7 @@ import ( "runtime" "strconv" "strings" + "unicode" "unicode/utf8" "cmd/compile/internal/syntax" @@ -90,7 +91,11 @@ func (p *noder) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase { } else { // line directive base p0 := b0.Pos() - p1 := src.MakePos(p.makeSrcPosBase(p0.Base()), p0.Line(), p0.Col()) + p0b := p0.Base() + if p0b == b0 { + panic("infinite recursion in makeSrcPosBase") + } + p1 := src.MakePos(p.makeSrcPosBase(p0b), p0.Line(), p0.Col()) b1 = src.NewLinePragmaBase(p1, fn, fileh(fn), b0.Line(), b0.Col()) } p.basemap[b0] = b1 @@ -130,11 +135,13 @@ type noder struct { base *src.PosBase } - file *syntax.File - linknames []linkname - pragcgobuf [][]string - err chan syntax.Error - scope ScopeID + file *syntax.File + linknames []linkname + pragcgobuf [][]string + err chan syntax.Error + scope ScopeID + importedUnsafe bool + importedEmbed bool // scopeVars is a stack tracking the number of variables declared in the // current function at the moment each open scope was opened. @@ -236,7 +243,8 @@ type linkname struct { func (p *noder) node() { types.Block = 1 - imported_unsafe = false + p.importedUnsafe = false + p.importedEmbed = false p.setlineno(p.file.PkgName) mkpackage(p.file.PkgName.Value) @@ -249,7 +257,7 @@ func (p *noder) node() { xtop = append(xtop, p.decls(p.file.DeclList)...) for _, n := range p.linknames { - if !imported_unsafe { + if !p.importedUnsafe { p.yyerrorpos(n.pos, "//go:linkname only allowed in Go files that import \"unsafe\"") continue } @@ -324,7 +332,6 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) { val := p.basicLit(imp.Path) ipkg := importfile(&val) - if ipkg == nil { if nerrors == 0 { Fatalf("phase error in import") @@ -332,6 +339,13 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) { return } + if ipkg == unsafepkg { + p.importedUnsafe = true + } + if ipkg.Path == "embed" { + p.importedEmbed = true + } + ipkg.Direct = true var my *types.Sym @@ -373,6 +387,20 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node { } if pragma, ok := decl.Pragma.(*Pragma); ok { + if len(pragma.Embeds) > 0 { + if !p.importedEmbed { + // This check can't be done when building the list pragma.Embeds + // because that list is created before the noder starts walking over the file, + // so at that point it hasn't seen the imports. + // We're left to check now, just before applying the //go:embed lines. + for _, e := range pragma.Embeds { + p.yyerrorpos(e.Pos, "//go:embed only allowed in Go files that import \"embed\"") + } + } else { + exprs = varEmbed(p, names, typ, exprs, pragma.Embeds) + } + pragma.Embeds = nil + } p.checkUnused(pragma) } @@ -455,17 +483,17 @@ func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node { param := n.Name.Param param.Ntype = typ - param.Alias = decl.Alias + param.SetAlias(decl.Alias) if pragma, ok := decl.Pragma.(*Pragma); ok { if !decl.Alias { - param.Pragma = pragma.Flag & TypePragmas + param.SetPragma(pragma.Flag & TypePragmas) pragma.Flag &^= TypePragmas } p.checkUnused(pragma) } nod := p.nod(decl, ODCLTYPE, n, nil) - if param.Alias && !langSupported(1, 9, localpkg) { + if param.Alias() && !langSupported(1, 9, localpkg) { yyerrorl(nod.Pos, "type aliases only supported as of -lang=go1.9") } return nod @@ -1493,13 +1521,15 @@ var allowedStdPragmas = map[string]bool{ "go:cgo_import_dynamic": true, "go:cgo_ldflag": true, "go:cgo_dynamic_linker": true, + "go:embed": true, "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 + Flag PragmaFlag // collected bits + Pos []PragmaPos // position of each individual flag + Embeds []PragmaEmbed } type PragmaPos struct { @@ -1507,12 +1537,22 @@ type PragmaPos struct { Pos syntax.Pos } +type PragmaEmbed struct { + Pos syntax.Pos + Patterns []string +} + func (p *noder) checkUnused(pragma *Pragma) { for _, pos := range pragma.Pos { if pos.Flag&pragma.Flag != 0 { p.yyerrorpos(pos.Pos, "misplaced compiler directive") } } + if len(pragma.Embeds) > 0 { + for _, e := range pragma.Embeds { + p.yyerrorpos(e.Pos, "misplaced go:embed directive") + } + } } func (p *noder) checkUnusedDuringParse(pragma *Pragma) { @@ -1521,6 +1561,11 @@ func (p *noder) checkUnusedDuringParse(pragma *Pragma) { p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"}) } } + if len(pragma.Embeds) > 0 { + for _, e := range pragma.Embeds { + p.error(syntax.Error{Pos: e.Pos, Msg: "misplaced go:embed directive"}) + } + } } // pragma is called concurrently if files are parsed concurrently. @@ -1565,6 +1610,17 @@ func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.P } p.linknames = append(p.linknames, linkname{pos, f[1], target}) + case text == "go:embed", strings.HasPrefix(text, "go:embed "): + args, err := parseGoEmbed(text[len("go:embed"):]) + if err != nil { + p.error(syntax.Error{Pos: pos, Msg: err.Error()}) + } + if len(args) == 0 { + p.error(syntax.Error{Pos: pos, Msg: "usage: //go:embed pattern..."}) + break + } + pragma.Embeds = append(pragma.Embeds, PragmaEmbed{pos, args}) + case strings.HasPrefix(text, "go:cgo_import_dynamic "): // This is permitted for general use because Solaris // code relies on it in golang.org/x/sys/unix and others. @@ -1637,3 +1693,64 @@ func mkname(sym *types.Sym) *Node { } return n } + +// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns. +// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings. +// go/build/read.go also processes these strings and contains similar logic. +func parseGoEmbed(args string) ([]string, error) { + var list []string + for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) { + var path string + Switch: + switch args[0] { + default: + i := len(args) + for j, c := range args { + if unicode.IsSpace(c) { + i = j + break + } + } + path = args[:i] + args = args[i:] + + case '`': + i := strings.Index(args[1:], "`") + if i < 0 { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + path = args[1 : 1+i] + args = args[1+i+1:] + + case '"': + i := 1 + for ; i < len(args); i++ { + if args[i] == '\\' { + i++ + continue + } + if args[i] == '"' { + q, err := strconv.Unquote(args[:i+1]) + if err != nil { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1]) + } + path = q + args = args[i+1:] + break Switch + } + } + if i >= len(args) { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + } + + if args != "" { + r, _ := utf8.DecodeRuneInString(args) + if !unicode.IsSpace(r) { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + } + list = append(list, path) + } + return list, nil +} -- cgit v1.2.1