Bumping k8s dependencies to 1.13

This commit is contained in:
Cheng Xing
2018-11-16 14:08:25 -08:00
parent 305407125c
commit b4c0b68ec7
8002 changed files with 884099 additions and 276228 deletions

View File

@@ -105,7 +105,7 @@ var (
outputFile = flag.String("o", "", "write output to `file` (default standard output)")
dstPath = flag.String("dst", "", "set destination import `path` (default taken from current directory)")
pkgName = flag.String("pkg", "", "set destination package `name` (default taken from current directory)")
prefix = flag.String("prefix", "", "set bundled identifier prefix to `p` (default source package name + \"_\")")
prefix = flag.String("prefix", "&_", "set bundled identifier prefix to `p` (default is \"&_\", where & stands for the original name)")
underscore = flag.Bool("underscore", false, "rewrite golang.org to golang_org in imports; temporary workaround for golang.org/issue/16333")
importMap = map[string]string{}
@@ -203,9 +203,8 @@ func bundle(src, dst, dstpkg, prefix string) ([]byte, error) {
// Because there was a single Import call and Load succeeded,
// InitialPackages is guaranteed to hold the sole requested package.
info := lprog.InitialPackages()[0]
if prefix == "" {
pkgName := info.Files[0].Name.Name
prefix = pkgName + "_"
if strings.Contains(prefix, "&") {
prefix = strings.Replace(prefix, "&", info.Files[0].Name.Name, -1)
}
objsToUpdate := make(map[types.Object]bool)

View File

@@ -37,7 +37,7 @@ import (
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/callgraph/rta"
"golang.org/x/tools/go/callgraph/static"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
@@ -67,7 +67,7 @@ const Usage = `callgraph: display the the call graph of a Go program.
Usage:
callgraph [-algo=static|cha|rta|pta] [-test] [-format=...] <args>...
callgraph [-algo=static|cha|rta|pta] [-test] [-format=...] package...
Flags:
@@ -118,8 +118,6 @@ Flags:
import path of the enclosing package. Consult the go/ssa
API documentation for details.
` + loader.FromArgsUsage + `
Examples:
Show the call graph of the trivial web server application:
@@ -158,7 +156,7 @@ func init() {
func main() {
flag.Parse()
if err := doCallgraph(&build.Default, *algoFlag, *formatFlag, *testFlag, flag.Args()); err != nil {
if err := doCallgraph("", "", *algoFlag, *formatFlag, *testFlag, flag.Args()); err != nil {
fmt.Fprintf(os.Stderr, "callgraph: %s\n", err)
os.Exit(1)
}
@@ -166,28 +164,30 @@ func main() {
var stdout io.Writer = os.Stdout
func doCallgraph(ctxt *build.Context, algo, format string, tests bool, args []string) error {
conf := loader.Config{Build: ctxt}
func doCallgraph(dir, gopath, algo, format string, tests bool, args []string) error {
if len(args) == 0 {
fmt.Fprintln(os.Stderr, Usage)
return nil
}
// Use the initial packages from the command line.
_, err := conf.FromArgs(args, tests)
cfg := &packages.Config{
Mode: packages.LoadAllSyntax,
Tests: tests,
Dir: dir,
}
if gopath != "" {
cfg.Env = append(os.Environ(), "GOPATH="+gopath) // to enable testing
}
initial, err := packages.Load(cfg, args...)
if err != nil {
return err
}
// Load, parse and type-check the whole program.
iprog, err := conf.Load()
if err != nil {
return err
if packages.PrintErrors(initial) > 0 {
return fmt.Errorf("packages contain errors")
}
// Create and build SSA-form program representation.
prog := ssautil.CreateProgram(iprog, 0)
prog, pkgs := ssautil.AllPackages(initial, 0)
prog.Build()
// -- call graph construction ------------------------------------------
@@ -221,7 +221,7 @@ func doCallgraph(ctxt *build.Context, algo, format string, tests bool, args []st
}
}
mains, err := mainPackages(prog, tests)
mains, err := mainPackages(pkgs)
if err != nil {
return err
}
@@ -237,7 +237,7 @@ func doCallgraph(ctxt *build.Context, algo, format string, tests bool, args []st
cg = ptares.CallGraph
case "rta":
mains, err := mainPackages(prog, tests)
mains, err := mainPackages(pkgs)
if err != nil {
return err
}
@@ -305,25 +305,13 @@ func doCallgraph(ctxt *build.Context, algo, format string, tests bool, args []st
// mainPackages returns the main packages to analyze.
// Each resulting package is named "main" and has a main function.
func mainPackages(prog *ssa.Program, tests bool) ([]*ssa.Package, error) {
pkgs := prog.AllPackages() // TODO(adonovan): use only initial packages
// If tests, create a "testmain" package for each test.
func mainPackages(pkgs []*ssa.Package) ([]*ssa.Package, error) {
var mains []*ssa.Package
if tests {
for _, pkg := range pkgs {
if main := prog.CreateTestMainPackage(pkg); main != nil {
mains = append(mains, main)
}
for _, p := range pkgs {
if p != nil && p.Pkg.Name() == "main" && p.Func("main") != nil {
mains = append(mains, p)
}
if mains == nil {
return nil, fmt.Errorf("no tests")
}
return mains, nil
}
// Otherwise, use the main packages.
mains = append(mains, ssautil.MainPackages(pkgs)...)
if len(mains) == 0 {
return nil, fmt.Errorf("no main packages")
}

View File

@@ -5,31 +5,44 @@
// No testdata on Android.
// +build !android
// +build go1.11
package main
import (
"bytes"
"fmt"
"go/build"
"reflect"
"sort"
"log"
"os"
"path/filepath"
"strings"
"testing"
)
func TestCallgraph(t *testing.T) {
ctxt := build.Default // copy
ctxt.GOPATH = "testdata"
func init() {
// This test currently requires GOPATH mode.
// Explicitly disabling module mode should suffix, but
// we'll also turn off GOPROXY just for good measure.
if err := os.Setenv("GO111MODULE", "off"); err != nil {
log.Fatal(err)
}
if err := os.Setenv("GOPROXY", "off"); err != nil {
log.Fatal(err)
}
}
const format = "{{.Caller}} --> {{.Callee}}"
func TestCallgraph(t *testing.T) {
gopath, err := filepath.Abs("testdata")
if err != nil {
t.Fatal(err)
}
for _, test := range []struct {
algo, format string
tests bool
want []string
algo string
tests bool
want []string
}{
{"rta", format, false, []string{
{"rta", false, []string{
// rta imprecisely shows cross product of {main,main2} x {C,D}
`pkg.main --> (pkg.C).f`,
`pkg.main --> (pkg.D).f`,
@@ -37,7 +50,7 @@ func TestCallgraph(t *testing.T) {
`pkg.main2 --> (pkg.C).f`,
`pkg.main2 --> (pkg.D).f`,
}},
{"pta", format, false, []string{
{"pta", false, []string{
// pta distinguishes main->C, main2->D. Also has a root node.
`<root> --> pkg.init`,
`<root> --> pkg.main`,
@@ -45,37 +58,42 @@ func TestCallgraph(t *testing.T) {
`pkg.main --> pkg.main2`,
`pkg.main2 --> (pkg.D).f`,
}},
// tests: main is not called.
{"rta", format, true, []string{
`pkg$testmain.init --> pkg.init`,
// tests: both the package's main and the test's main are called.
// The callgraph includes all the guts of the "testing" package.
{"rta", true, []string{
`pkg.test.main --> testing.MainStart`,
`testing.runExample --> pkg.Example`,
`pkg.Example --> (pkg.C).f`,
`pkg.main --> (pkg.C).f`,
}},
{"pta", format, true, []string{
`<root> --> pkg$testmain.init`,
`<root> --> pkg.Example`,
`pkg$testmain.init --> pkg.init`,
{"pta", true, []string{
`<root> --> pkg.test.main`,
`<root> --> pkg.main`,
`pkg.test.main --> testing.MainStart`,
`testing.runExample --> pkg.Example`,
`pkg.Example --> (pkg.C).f`,
`pkg.main --> (pkg.C).f`,
}},
} {
const format = "{{.Caller}} --> {{.Callee}}"
stdout = new(bytes.Buffer)
if err := doCallgraph(&ctxt, test.algo, test.format, test.tests, []string{"pkg"}); err != nil {
if err := doCallgraph("testdata/src", gopath, test.algo, format, test.tests, []string{"pkg"}); err != nil {
t.Error(err)
continue
}
got := sortedLines(fmt.Sprint(stdout))
if !reflect.DeepEqual(got, test.want) {
t.Errorf("callgraph(%q, %q, %t):\ngot:\n%s\nwant:\n%s",
test.algo, test.format, test.tests,
strings.Join(got, "\n"),
strings.Join(test.want, "\n"))
edges := make(map[string]bool)
for _, line := range strings.Split(fmt.Sprint(stdout), "\n") {
edges[line] = true
}
for _, edge := range test.want {
if !edges[edge] {
t.Errorf("callgraph(%q, %t): missing edge: %s",
test.algo, test.tests, edge)
}
}
if t.Failed() {
t.Log("got:\n", stdout)
}
}
}
func sortedLines(s string) []string {
s = strings.TrimSpace(s)
lines := strings.Split(s, "\n")
sort.Strings(lines)
return lines
}

View File

@@ -1,7 +1,10 @@
package main
// Don't import "testing", it adds a lot of callgraph edges.
// An Example function must have an "Output:" comment for the go build
// system to generate a call to it from the test main package.
func Example() {
C(0).f()
// Output:
}

View File

@@ -67,9 +67,9 @@ package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"go/build"
"io/ioutil"
"log"
"os"
@@ -144,6 +144,7 @@ func main() {
log.Fatalf("%s env GOROOT: %v", *flagGoCmd, err)
}
goroot = strings.TrimSpace(string(s))
os.Setenv("GOROOT", goroot) // for any subcommands
compiler = *flagCompiler
if compiler == "" {
@@ -250,11 +251,28 @@ func runBuild(name, dir string, count int) {
return
}
pkg, err := build.Import(dir, ".", 0)
// Make sure dependencies needed by go tool compile are installed to GOROOT/pkg.
out, err := exec.Command(*flagGoCmd, "build", "-i", dir).CombinedOutput()
if err != nil {
log.Print(err)
log.Printf("go build -i %s: %v\n%s", dir, err, out)
return
}
// Find dir and source file list.
var pkg struct {
Dir string
GoFiles []string
}
out, err = exec.Command(*flagGoCmd, "list", "-json", dir).Output()
if err != nil {
log.Printf("go list -json %s: %v\n", dir, err)
return
}
if err := json.Unmarshal(out, &pkg); err != nil {
log.Printf("go list -json %s: unmarshal: %v", dir, err)
return
}
args := []string{"-o", "_compilebench_.o"}
if is6g {
*flagMemprofilerate = -1

View File

@@ -8,7 +8,7 @@
//
// TODO(adonovan):
// - support input files other than stdin
// - suport alternative formats (AT&T GraphViz, CSV, etc),
// - support alternative formats (AT&T GraphViz, CSV, etc),
// a comment syntax, etc.
// - allow queries to nest, like Blaze query language.
//
@@ -89,11 +89,12 @@ Example usage:
`
func main() {
flag.Usage = func() { fmt.Fprintln(os.Stderr, Usage) }
flag.Parse()
args := flag.Args()
if len(args) == 0 {
fmt.Println(Usage)
fmt.Fprintln(os.Stderr, Usage)
return
}

View File

@@ -491,15 +491,18 @@ func list(args ...string) ([]*listPackage, error) {
return pkgs, nil
}
var cwd string
func init() {
var err error
cwd, err = os.Getwd()
// cwd contains the current working directory of the tool.
//
// It is initialized directly so that its value will be set for any other
// package variables or init functions that depend on it, such as the gopath
// variable in main_test.go.
var cwd string = func() string {
cwd, err := os.Getwd()
if err != nil {
log.Fatalf("os.Getwd: %v", err)
}
}
return cwd
}()
// shortPath returns an absolute or relative name for path, whatever is shorter.
// Plundered from $GOROOT/src/cmd/go/build.go.

View File

@@ -10,6 +10,7 @@ package main
import (
"bytes"
"log"
"os"
"path/filepath"
"runtime"
@@ -32,11 +33,25 @@ import (
// titanic.biz/bar -- domain is sinking; package has jumped ship to new.com/bar
// titanic.biz/foo -- domain is sinking but package has no import comment yet
func TestFixImports(t *testing.T) {
gopath := filepath.Join(cwd, "testdata")
var gopath = filepath.Join(cwd, "testdata")
func init() {
if err := os.Setenv("GOPATH", gopath); err != nil {
t.Fatalf("os.Setenv: %v", err)
log.Fatal(err)
}
// This test currently requires GOPATH mode.
// Explicitly disabling module mode should suffix, but
// we'll also turn off GOPROXY just for good measure.
if err := os.Setenv("GO111MODULE", "off"); err != nil {
log.Fatal(err)
}
if err := os.Setenv("GOPROXY", "off"); err != nil {
log.Fatal(err)
}
}
func TestFixImports(t *testing.T) {
defer func() {
stderr = os.Stderr
*badDomains = "code.google.com"
@@ -224,11 +239,6 @@ import (
// TestDryRun tests that the -n flag suppresses calls to writeFile.
func TestDryRun(t *testing.T) {
gopath := filepath.Join(cwd, "testdata")
if err := os.Setenv("GOPATH", gopath); err != nil {
t.Fatalf("os.Setenv: %v", err)
}
*dryrun = true
defer func() { *dryrun = false }() // restore
stderr = new(bytes.Buffer)

View File

@@ -7,7 +7,7 @@ package main
import (
"bytes"
"fmt"
exact "go/constant"
"go/constant"
"go/token"
"go/types"
"io"
@@ -213,9 +213,9 @@ func (p *printer) printDecl(keyword string, n int, printGroup func()) {
// absInt returns the absolute value of v as a *big.Int.
// v must be a numeric value.
func absInt(v exact.Value) *big.Int {
func absInt(v constant.Value) *big.Int {
// compute big-endian representation of v
b := exact.Bytes(v) // little-endian
b := constant.Bytes(v) // little-endian
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
@@ -229,14 +229,14 @@ var (
// floatString returns the string representation for a
// numeric value v in normalized floating-point format.
func floatString(v exact.Value) string {
if exact.Sign(v) == 0 {
func floatString(v constant.Value) string {
if constant.Sign(v) == 0 {
return "0.0"
}
// x != 0
// convert |v| into a big.Rat x
x := new(big.Rat).SetFrac(absInt(exact.Num(v)), absInt(exact.Denom(v)))
x := new(big.Rat).SetFrac(absInt(constant.Num(v)), absInt(constant.Denom(v)))
// normalize x and determine exponent e
// (This is not very efficient, but also not speed-critical.)
@@ -272,7 +272,7 @@ func floatString(v exact.Value) string {
if e != 0 {
s += fmt.Sprintf("e%+d", e)
}
if exact.Sign(v) < 0 {
if constant.Sign(v) < 0 {
s = "-" + s
}
@@ -286,29 +286,29 @@ func floatString(v exact.Value) string {
// valString returns the string representation for the value v.
// Setting floatFmt forces an integer value to be formatted in
// normalized floating-point format.
// TODO(gri) Move this code into package exact.
func valString(v exact.Value, floatFmt bool) string {
// TODO(gri) Move this code into package constant.
func valString(v constant.Value, floatFmt bool) string {
switch v.Kind() {
case exact.Int:
case constant.Int:
if floatFmt {
return floatString(v)
}
case exact.Float:
case constant.Float:
return floatString(v)
case exact.Complex:
re := exact.Real(v)
im := exact.Imag(v)
case constant.Complex:
re := constant.Real(v)
im := constant.Imag(v)
var s string
if exact.Sign(re) != 0 {
if constant.Sign(re) != 0 {
s = floatString(re)
if exact.Sign(im) >= 0 {
if constant.Sign(im) >= 0 {
s += " + "
} else {
s += " - "
im = exact.UnaryOp(token.SUB, im, 0) // negate im
im = constant.UnaryOp(token.SUB, im, 0) // negate im
}
}
// im != 0, otherwise v would be exact.Int or exact.Float
// im != 0, otherwise v would be constant.Int or constant.Float
return s + floatString(im) + "i"
}
return v.String()

3
vendor/golang.org/x/tools/cmd/godoc/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
index.split.*
godoc.index
godoc.zip

67
vendor/golang.org/x/tools/cmd/godoc/Dockerfile.prod generated vendored Normal file
View File

@@ -0,0 +1,67 @@
# Builder
#########
FROM golang:1.11 AS build
RUN apt-get update && apt-get install -y \
zip # required for generate-index.bash
# Check out the desired version of Go, both to build the godoc binary and serve
# as the goroot for content serving.
ARG GO_REF
RUN test -n "$GO_REF" # GO_REF is required.
RUN git clone --single-branch --depth=1 -b $GO_REF https://go.googlesource.com/go /goroot
RUN cd /goroot/src && ./make.bash
ENV GOROOT /goroot
ENV PATH=/goroot/bin:$PATH
RUN go version
RUN go get -v -d \
golang.org/x/net/context \
google.golang.org/appengine \
cloud.google.com/go/datastore \
golang.org/x/build \
github.com/gomodule/redigo/redis
COPY . /go/src/golang.org/x/tools
WORKDIR /go/src/golang.org/x/tools/cmd/godoc
RUN GODOC_DOCSET=/goroot ./generate-index.bash
RUN go build -o /godoc -tags=golangorg golang.org/x/tools/cmd/godoc
# Clean up goroot for the final image.
RUN cd /goroot && git clean -xdf
# Add build metadata.
RUN cd /goroot && echo "go repo HEAD: $(git rev-parse HEAD)" >> /goroot/buildinfo
RUN echo "requested go ref: ${GO_REF}" >> /goroot/buildinfo
ARG TOOLS_HEAD
RUN echo "x/tools HEAD: ${TOOLS_HEAD}" >> /goroot/buildinfo
ARG TOOLS_CLEAN
RUN echo "x/tools clean: ${TOOLS_CLEAN}" >> /goroot/buildinfo
ARG DOCKER_TAG
RUN echo "image: ${DOCKER_TAG}" >> /goroot/buildinfo
ARG BUILD_ENV
RUN echo "build env: ${BUILD_ENV}" >> /goroot/buildinfo
RUN rm -rf /goroot/.git
# Final image
#############
FROM gcr.io/distroless/base
WORKDIR /app
COPY --from=build /godoc /app/
COPY --from=build /go/src/golang.org/x/tools/cmd/godoc/hg-git-mapping.bin /app/
COPY --from=build /goroot /goroot
ENV GOROOT /goroot
COPY --from=build /go/src/golang.org/x/tools/cmd/godoc/index.split.* /app/
ENV GODOC_INDEX_GLOB index.split.*
CMD ["/app/godoc"]

80
vendor/golang.org/x/tools/cmd/godoc/Makefile generated vendored Normal file
View File

@@ -0,0 +1,80 @@
# Copyright 2018 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.
.PHONY: usage
GO_REF ?= release-branch.go1.11
TOOLS_HEAD := $(shell git rev-parse HEAD)
TOOLS_CLEAN := $(shell (git status --porcelain | grep -q .) && echo dirty || echo clean)
ifeq ($(TOOLS_CLEAN),clean)
DOCKER_VERSION ?= $(TOOLS_HEAD)
else
DOCKER_VERSION ?= $(TOOLS_HEAD)-dirty
endif
GCP_PROJECT := golang-org
DOCKER_TAG := gcr.io/$(GCP_PROJECT)/godoc:$(DOCKER_VERSION)
usage:
@echo "See Makefile and README.godoc-app"
@exit 1
cloud-build: Dockerfile.prod
gcloud builds submit \
--project=$(GCP_PROJECT) \
--config=cloudbuild.yaml \
--substitutions=_GO_REF=$(GO_REF),_TOOLS_HEAD=$(TOOLS_HEAD),_TOOLS_CLEAN=$(TOOLS_CLEAN),_DOCKER_TAG=$(DOCKER_TAG) \
../.. # source code
docker-build: Dockerfile.prod
# NOTE(cbro): move up in directory to include entire tools repo.
# NOTE(cbro): any changes made to this command must also be made in cloudbuild.yaml.
cd ../..; docker build \
-f=cmd/godoc/Dockerfile.prod \
--build-arg=GO_REF=$(GO_REF) \
--build-arg=TOOLS_HEAD=$(TOOLS_HEAD) \
--build-arg=TOOLS_CLEAN=$(TOOLS_CLEAN) \
--build-arg=DOCKER_TAG=$(DOCKER_TAG) \
--build-arg=BUILD_ENV=local \
--tag=$(DOCKER_TAG) \
.
docker-push: docker-build
docker push $(DOCKER_TAG)
deploy:
gcloud -q app deploy app.prod.yaml \
--project=$(GCP_PROJECT) \
--no-promote \
--image-url=$(DOCKER_TAG)
get-latest-url:
@gcloud app versions list \
--service=default \
--project=$(GCP_PROJECT) \
--sort-by=~version.createTime \
--format='value(version.versionUrl)' \
--limit 1 | cut -f1 # NOTE(cbro): gcloud prints out createTime as the second field.
get-latest-id:
@gcloud app versions list \
--service=default \
--project=$(GCP_PROJECT) \
--sort-by=~version.createTime \
--format='value(version.id)' \
--limit 1 | cut -f1 # NOTE(cbro): gcloud prints out createTime as the second field.
regtest:
go test -v \
-regtest.host=$(shell make get-latest-url) \
-run=Live
publish: regtest
gcloud -q app services set-traffic default \
--splits=$(shell make get-latest-id)=1 \
--project=$(GCP_PROJECT)
@echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@echo Stop and/or delete old versions:
@echo "https://console.cloud.google.com/appengine/versions?project=$(GCP_PROJECT)&serviceId=default&versionssize=50"
@echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

View File

@@ -1,56 +1,94 @@
godoc on appengine
------------------
godoc on Google App Engine
==========================
Prerequisites
-------------
* Go appengine SDK
https://developers.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go
* Google Cloud SDK
https://cloud.google.com/sdk/
* Go sources at tip under $GOROOT
* Redis
* Godoc sources at tip inside $GOPATH
* Go sources under $GOROOT
* Godoc sources inside $GOPATH
(go get -d golang.org/x/tools/cmd/godoc)
Directory structure
-------------------
Running locally, in production mode
-----------------------------------
* Let $APPDIR be the directory containing the app engine files.
(e.g., $APPDIR=$HOME/godoc-app)
Build the app:
* $APPDIR contains the following entries (this may change depending on
app-engine release and version of godoc):
go build -tags golangorg
app.yaml
golang.org/x/tools/cmd/godoc
godoc.zip
index.split.*
Run the app:
* The app.yaml file is set up per app engine documentation.
For instance:
./godoc
application: godoc-app
version: 1
runtime: go
api_version: go1
godoc should come up at http://localhost:8080
handlers:
- url: /.*
script: _go_app
Use the PORT environment variable to change the port:
PORT=8081 ./godoc
Running locally, in production mode, using Docker
-------------------------------------------------
Build the app's Docker container:
make docker-build
Make sure redis is running on port 6379:
$ echo PING | nc localhost 6379
+PONG
^C
Run the datastore emulator:
gcloud beta emulators datastore start --project golang-org
In another terminal window, run the container:
$(gcloud beta emulators datastore env-init)
docker run --rm \
--net host \
--env GODOC_REDIS_ADDR=localhost:6379 \
--env DATASTORE_EMULATOR_HOST=$DATASTORE_EMULATOR_HOST \
--env DATASTORE_PROJECT_ID=$DATASTORE_PROJECT_ID \
gcr.io/golang-org/godoc
godoc should come up at http://localhost:8080
Configuring and running godoc
-----------------------------
Deploying to golang.org
-----------------------
To configure godoc, run
Make sure you're signed in to gcloud:
bash setup-godoc-app.bash
gcloud auth login
to prepare an $APPDIR as described above. See the script for details on usage.
Build the image, push it to gcr.io, and deploy to Flex:
To run godoc locally, using the App Engine development server, run
make cloud-build deploy
<path to go_appengine>/dev_appserver.py $APPDIR
Point the load balancer to the newly deployed version:
(This also runs regression tests)
godoc should come up at http://localhost:8080 .
make publish
Stop and/or delete down any very old versions. (Stopped versions can be re-started.)
Keep at least one older verson to roll back to, just in case.
You can also migrate traffic to the new version via this UI.
https://console.cloud.google.com/appengine/versions?project=golang-org&serviceId=default&versionssize=50
Troubleshooting
---------------
Ensure the Cloud SDK is on your PATH and you have the app-engine-go component
installed (gcloud components install app-engine-go) and your components are
up-to-date (gcloud components update)

13
vendor/golang.org/x/tools/cmd/godoc/app.dev.yaml generated vendored Normal file
View File

@@ -0,0 +1,13 @@
runtime: go
api_version: go1
instance_class: F4_1G
handlers:
- url: /s
script: _go_app
login: admin
- url: /dl/init
script: _go_app
login: admin
- url: /.*
script: _go_app

16
vendor/golang.org/x/tools/cmd/godoc/app.prod.yaml generated vendored Normal file
View File

@@ -0,0 +1,16 @@
runtime: custom
env: flex
env_variables:
GODOC_PROD: true
GODOC_ENFORCE_HOSTS: true
GODOC_REDIS_ADDR: 10.0.0.4:6379 # instance "gophercache"
GODOC_ANALYTICS: UA-11222381-2
DATASTORE_PROJECT_ID: golang-org
network:
name: golang
resources:
cpu: 4
memory_gb: 7.50

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build appengine
// +build golangorg
package main
@@ -11,25 +11,46 @@ package main
import (
"archive/zip"
"context"
"io"
"log"
"net/http"
"os"
"path"
"regexp"
"runtime"
"strings"
"golang.org/x/tools/godoc"
"golang.org/x/tools/godoc/dl"
"golang.org/x/tools/godoc/proxy"
"golang.org/x/tools/godoc/redirect"
"golang.org/x/tools/godoc/short"
"golang.org/x/tools/godoc/static"
"golang.org/x/tools/godoc/vfs"
"golang.org/x/tools/godoc/vfs/gatefs"
"golang.org/x/tools/godoc/vfs/mapfs"
"golang.org/x/tools/godoc/vfs/zipfs"
"google.golang.org/appengine"
"cloud.google.com/go/datastore"
"golang.org/x/tools/internal/memcache"
)
func init() {
enforceHosts = !appengine.IsDevAppServer()
func main() {
log.SetFlags(log.Lshortfile | log.LstdFlags)
var (
// .zip filename
zipFilename = os.Getenv("GODOC_ZIP")
// goroot directory in .zip file
zipGoroot = os.Getenv("GODOC_ZIP_PREFIX")
// glob pattern describing search index files
// (if empty, the index is built at run-time)
indexFilenames = os.Getenv("GODOC_INDEX_GLOB")
)
playEnabled = true
log.Println("initializing godoc ...")
@@ -37,16 +58,20 @@ func init() {
log.Printf(".zip GOROOT = %s", zipGoroot)
log.Printf("index files = %s", indexFilenames)
goroot := path.Join("/", zipGoroot) // fsHttp paths are relative to '/'
// read .zip file and set up file systems
const zipfile = zipFilename
rc, err := zip.OpenReader(zipfile)
if err != nil {
log.Fatalf("%s: %s\n", zipfile, err)
if zipFilename != "" {
goroot := path.Join("/", zipGoroot) // fsHttp paths are relative to '/'
// read .zip file and set up file systems
rc, err := zip.OpenReader(zipFilename)
if err != nil {
log.Fatalf("%s: %s\n", zipFilename, err)
}
// rc is never closed (app running forever)
fs.Bind("/", zipfs.New(rc, zipFilename), goroot, vfs.BindReplace)
} else {
rootfs := gatefs.New(vfs.OS(runtime.GOROOT()), make(chan bool, 20))
fs.Bind("/", rootfs, "/", vfs.BindReplace)
}
// rc is never closed (app running forever)
fs.Bind("/", zipfs.New(rc, zipFilename), goroot, vfs.BindReplace)
fs.Bind("/lib/godoc", mapfs.New(static.Files), "/", vfs.BindReplace)
corpus := godoc.NewCorpus(fs)
@@ -58,25 +83,73 @@ func init() {
log.Fatal(err)
}
corpus.IndexDirectory = indexDirectoryDefault
go corpus.RunIndexer()
corpus.InitVersionInfo()
if indexFilenames != "" {
corpus.RunIndexer()
} else {
go corpus.RunIndexer()
}
pres = godoc.NewPresentation(corpus)
pres.TabWidth = 8
pres.ShowPlayground = true
pres.ShowExamples = true
pres.DeclLinks = true
pres.NotesRx = regexp.MustCompile("BUG")
pres.GoogleAnalytics = os.Getenv("GODOC_ANALYTICS")
readTemplates(pres, true)
readTemplates(pres)
datastoreClient, memcacheClient := getClients()
// NOTE(cbro): registerHandlers registers itself against DefaultServeMux.
// The mux returned has host enforcement, so it's important to register
// against this mux and not DefaultServeMux.
mux := registerHandlers(pres)
dl.RegisterHandlers(mux)
short.RegisterHandlers(mux)
dl.RegisterHandlers(mux, datastoreClient, memcacheClient)
short.RegisterHandlers(mux, datastoreClient, memcacheClient)
// Register /compile and /share handlers against the default serve mux
// so that other app modules can make plain HTTP requests to those
// hosts. (For reasons, HTTPS communication between modules is broken.)
proxy.RegisterHandlers(http.DefaultServeMux)
http.HandleFunc("/_ah/health", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "ok")
})
http.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "User-agent: *\nDisallow: /search\n")
})
if err := redirect.LoadChangeMap("hg-git-mapping.bin"); err != nil {
log.Fatalf("LoadChangeMap: %v", err)
}
log.Println("godoc initialization complete")
// TODO(cbro): add instrumentation via opencensus.
port := "8080"
if p := os.Getenv("PORT"); p != "" { // PORT is set by GAE flex.
port = p
}
log.Fatal(http.ListenAndServe(":"+port, nil))
}
func getClients() (*datastore.Client, *memcache.Client) {
ctx := context.Background()
datastoreClient, err := datastore.NewClient(ctx, "")
if err != nil {
if strings.Contains(err.Error(), "missing project") {
log.Fatalf("Missing datastore project. Set the DATASTORE_PROJECT_ID env variable. Use `gcloud beta emulators datastore` to start a local datastore.")
}
log.Fatalf("datastore.NewClient: %v.", err)
}
redisAddr := os.Getenv("GODOC_REDIS_ADDR")
if redisAddr == "" {
log.Fatalf("Missing redis server for godoc in production mode. set GODOC_REDIS_ADDR environment variable.")
}
memcacheClient := memcache.New(redisAddr)
return datastoreClient, memcacheClient
}

View File

@@ -21,7 +21,7 @@ import (
const (
blogRepo = "golang.org/x/blog"
blogURL = "http://blog.golang.org/"
blogURL = "https://blog.golang.org/"
blogPath = "/blog/"
)
@@ -42,10 +42,11 @@ func init() {
}
func blogInit(host string) {
// Binary distributions will include the blog content in "/blog".
// Binary distributions included the blog content in "/blog".
// We stopped including this in Go 1.11.
root := filepath.Join(runtime.GOROOT(), "blog")
// Prefer content from go.blog repository if present.
// Prefer content from the golang.org/x/blog repository if present.
if pkg, err := build.Import(blogRepo, "", build.FindOnly); err == nil {
root = pkg.Dir
}

25
vendor/golang.org/x/tools/cmd/godoc/cloudbuild.yaml generated vendored Normal file
View File

@@ -0,0 +1,25 @@
# Copyright 2018 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.
# NOTE(cbro): any changes to the docker command must also be
# made in docker-build in the Makefile.
#
# Variable substitutions must have a preceding underscore. See:
# https://cloud.google.com/cloud-build/docs/configuring-builds/substitute-variable-values#using_user-defined_substitutions
steps:
- name: 'gcr.io/cloud-builders/docker'
args: [
'build',
'-f=cmd/godoc/Dockerfile.prod',
'--build-arg=GO_REF=${_GO_REF}',
'--build-arg=TOOLS_HEAD=${_TOOLS_HEAD}',
'--build-arg=TOOLS_CLEAN=${_TOOLS_CLEAN}',
'--build-arg=DOCKER_TAG=${_DOCKER_TAG}',
'--build-arg=BUILD_ENV=cloudbuild',
'--tag=${_DOCKER_TAG}',
'.',
]
images: ['${_DOCKER_TAG}']
options:
machineType: 'N1_HIGHCPU_8' # building the godoc index takes a lot of memory.

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !appengine
// +build !golangorg
package main

View File

@@ -6,50 +6,19 @@
Godoc extracts and generates documentation for Go programs.
It has two modes.
Without the -http flag, it runs in command-line mode and prints plain text
documentation to standard output and exits. If both a library package and
a command with the same name exists, using the prefix cmd/ will force
documentation on the command rather than the library package. If the -src
flag is specified, godoc prints the exported interface of a package in Go
source form, or the implementation of a specific exported language entity:
godoc fmt # documentation for package fmt
godoc fmt Printf # documentation for fmt.Printf
godoc cmd/go # force documentation for the go command
godoc -src fmt # fmt package interface in Go source form
godoc -src fmt Printf # implementation of fmt.Printf
In command-line mode, the -q flag enables search queries against a godoc running
as a webserver. If no explicit server address is specified with the -server flag,
godoc first tries localhost:6060 and then http://golang.org.
godoc -q Reader
godoc -q math.Sin
godoc -server=:6060 -q sin
With the -http flag, it runs as a web server and presents the documentation as a
It runs as a web server and presents the documentation as a
web page.
godoc -http=:6060
Usage:
godoc [flag] package [name ...]
godoc [flag]
The flags are:
-v
verbose mode
-q
arguments are considered search queries: a legal query is a
single identifier (such as ToLower) or a qualified identifier
(such as math.Sin)
-src
print (exported) source in command-line mode
-tabwidth=4
width of tabs in units of spaces
-timestamps=true
show timestamps with directory listings
-index
@@ -63,7 +32,12 @@ The flags are:
to the indexer (the indexer will never finish), a value of 1.0
means that index creation is running at full throttle (other
goroutines may get no time while the index is built)
-links=true:
-index_interval=0
interval of indexing; a value of 0 sets it to 5 minutes, a
negative value indexes only once at startup
-play=false
enable playground
-links=true
link identifiers to their declarations
-write_index=false
write index to a file; the file name must be specified with
@@ -74,21 +48,17 @@ The flags are:
-notes="BUG"
regular expression matching note markers to show
(e.g., "BUG|TODO", ".*")
-html
print HTML in command-line mode
-goroot=$GOROOT
Go root directory
-http=addr
HTTP service address (e.g., '127.0.0.1:6060' or just ':6060')
-server=addr
webserver address for command line searches
-analysis=type,pointer
comma-separated list of analyses to perform
"type": display identifier resolution, type info, method sets,
'implements', and static callees
"pointer": display channel peers, callers and dynamic callees
(significantly slower)
See http://golang.org/lib/godoc/analysis/help.html for details.
See https://golang.org/lib/godoc/analysis/help.html for details.
-templates=""
directory containing alternate template files; if set,
the directory may provide alternative template files
@@ -103,7 +73,7 @@ By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set
This behavior can be altered by providing an alternative $GOROOT with the -goroot
flag.
When godoc runs as a web server and -index is set, a search index is maintained.
When the -index flag is set, a search index is maintained.
The index is created at startup.
The index contains both identifier and full text search information (searchable
@@ -111,10 +81,8 @@ via regular expressions). The maximum number of full text search results shown
can be set with the -maxresults flag; if set to 0, no full text results are
shown, and only an identifier index but no full text search index is created.
By default, godoc uses the system's GOOS/GOARCH; in command-line mode you can
set the GOOS/GOARCH environment variables to get output for the system specified.
If -http was specified you can provide the URL parameters "GOOS" and "GOARCH"
to set the output on the web page.
By default, godoc uses the system's GOOS/GOARCH. You can provide the URL parameters
"GOOS" and "GOARCH" to set the output on the web page for the target system.
The presentation mode of web pages served by godoc can be controlled with the
"m" URL parameter; it accepts a comma-separated list of flag names as value:
@@ -122,12 +90,9 @@ The presentation mode of web pages served by godoc can be controlled with the
all show documentation for all declarations, not just the exported ones
methods show all embedded methods, not just those of unexported anonymous fields
src show the original source code rather then the extracted documentation
text present the page in textual (command-line) form rather than HTML
flat present flat (not indented) directory listings using full paths
For instance, http://golang.org/pkg/math/big/?m=all,text shows the documentation
for all (not just the exported) declarations of package big, in textual form (as
it would appear when using godoc from the command line: "godoc -src math/big .*").
For instance, https://golang.org/pkg/math/big/?m=all shows the documentation
for all (not just the exported) declarations of package big.
By default, godoc serves files from the file system of the underlying OS.
Instead, a .zip file may be provided via the -zip flag, which contains
@@ -143,11 +108,11 @@ one may run godoc as follows:
godoc -http=:6060 -zip=go.zip -goroot=$HOME/go
Godoc documentation is converted to HTML or to text using the go/doc package;
see http://golang.org/pkg/go/doc/#ToHTML for the exact rules.
see https://golang.org/pkg/go/doc/#ToHTML for the exact rules.
Godoc also shows example code that is runnable by the testing package;
see http://golang.org/pkg/testing/#hdr-Examples for the conventions.
see https://golang.org/pkg/testing/#hdr-Examples for the conventions.
See "Godoc: documenting Go code" for how to write good comments for godoc:
http://golang.org/doc/articles/godoc_documenting_go_code.html
https://golang.org/doc/articles/godoc_documenting_go_code.html
*/
package main // import "golang.org/x/tools/cmd/godoc"

72
vendor/golang.org/x/tools/cmd/godoc/generate-index.bash generated vendored Executable file
View File

@@ -0,0 +1,72 @@
#!/usr/bin/env bash
# Copyright 2011 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.
# This script creates a .zip file representing the $GOROOT file system
# and computes the corresponding search index files.
#
# These are used in production (see app.prod.yaml)
set -e -u -x
ZIPFILE=godoc.zip
INDEXFILE=godoc.index
SPLITFILES=index.split.
error() {
echo "error: $1"
exit 2
}
install() {
go install
}
getArgs() {
if [ ! -v GODOC_DOCSET ]; then
GODOC_DOCSET="$(go env GOROOT)"
echo "GODOC_DOCSET not set explicitly, using GOROOT instead"
fi
# safety checks
if [ ! -d "$GODOC_DOCSET" ]; then
error "$GODOC_DOCSET is not a directory"
fi
# reporting
echo "GODOC_DOCSET = $GODOC_DOCSET"
}
makeZipfile() {
echo "*** make $ZIPFILE"
rm -f $ZIPFILE goroot
ln -s "$GODOC_DOCSET" goroot
zip -q -r $ZIPFILE goroot/* # glob to ignore dotfiles (like .git)
rm goroot
}
makeIndexfile() {
echo "*** make $INDEXFILE"
godoc=$(go env GOPATH)/bin/godoc
# NOTE: run godoc without GOPATH set. Otherwise third-party packages will end up in the index.
GOPATH= $godoc -write_index -goroot goroot -index_files=$INDEXFILE -zip=$ZIPFILE
}
splitIndexfile() {
echo "*** split $INDEXFILE"
rm -f $SPLITFILES*
split -b8m $INDEXFILE $SPLITFILES
}
cd $(dirname $0)
install
getArgs "$@"
makeZipfile
makeIndexfile
splitIndexfile
rm $INDEXFILE
echo "*** setup complete"

View File

@@ -1,9 +0,0 @@
// Copyright 2017 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.
// +build go1.9
package main_test
func init() { isGo19 = true }

View File

@@ -8,6 +8,7 @@ import (
"bufio"
"bytes"
"fmt"
"go/build"
"io"
"io/ioutil"
"net"
@@ -53,91 +54,6 @@ func buildGodoc(t *testing.T) (bin string, cleanup func()) {
return bin, func() { os.RemoveAll(tmp) }
}
var isGo19 bool // godoc19_test.go sets it to true.
// Basic regression test for godoc command-line tool.
func TestCLI(t *testing.T) {
bin, cleanup := buildGodoc(t)
defer cleanup()
// condStr returns s if cond is true, otherwise empty string.
condStr := func(cond bool, s string) string {
if !cond {
return ""
}
return s
}
tests := []struct {
args []string
matches []string // regular expressions
dontmatch []string // regular expressions
}{
{
args: []string{"fmt"},
matches: []string{
`import "fmt"`,
`Package fmt implements formatted I/O`,
},
},
{
args: []string{"io", "WriteString"},
matches: []string{
`func WriteString\(`,
`WriteString writes the contents of the string s to w`,
},
},
{
args: []string{"nonexistingpkg"},
matches: []string{
`cannot find package` +
// TODO: Remove this when support for Go 1.8 is dropped.
condStr(!isGo19,
// For Go 1.8 and older, because it doesn't have CL 33158 change applied to go/build.
// The last pattern (does not e) is for plan9:
// http://build.golang.org/log/2d8e5e14ed365bfa434b37ec0338cd9e6f8dd9bf
`|no such file or directory|does not exist|cannot find the file|(?:' does not e)`),
},
},
{
args: []string{"fmt", "NonexistentSymbol"},
matches: []string{
`No match found\.`,
},
},
{
args: []string{"-src", "syscall", "Open"},
matches: []string{
`func Open\(`,
},
dontmatch: []string{
`No match found\.`,
},
},
}
for _, test := range tests {
cmd := exec.Command(bin, test.args...)
cmd.Args[0] = "godoc"
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("Running with args %#v: %v", test.args, err)
continue
}
for _, pat := range test.matches {
re := regexp.MustCompile(pat)
if !re.Match(out) {
t.Errorf("godoc %v =\n%s\nwanted /%v/", strings.Join(test.args, " "), out, pat)
}
}
for _, pat := range test.dontmatch {
re := regexp.MustCompile(pat)
if re.Match(out) {
t.Errorf("godoc %v =\n%s\ndid not want /%v/", strings.Join(test.args, " "), out, pat)
}
}
}
}
func serverAddress(t *testing.T) string {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
@@ -202,6 +118,17 @@ func waitForServer(t *testing.T, url, match string, timeout time.Duration, rever
t.Fatalf("Server failed to respond in %v", timeout)
}
// hasTag checks whether a given release tag is contained in the current version
// of the go binary.
func hasTag(t string) bool {
for _, v := range build.Default.ReleaseTags {
if t == v {
return true
}
}
return false
}
func killAndWait(cmd *exec.Cmd) {
cmd.Process.Kill()
cmd.Wait()
@@ -237,10 +164,14 @@ func testWeb(t *testing.T, withIndex bool) {
cmd.Stderr = os.Stderr
cmd.Args[0] = "godoc"
// Set GOPATH variable to non-existing path.
// Set GOPATH variable to non-existing path
// and GOPROXY=off to disable module fetches.
// We cannot just unset GOPATH variable because godoc would default it to ~/go.
// (We don't want the indexer looking at the local workspace during tests.)
cmd.Env = append(os.Environ(), "GOPATH=does_not_exist")
cmd.Env = append(os.Environ(),
"GOPATH=does_not_exist",
"GOPROXY=off",
"GO111MODULE=off")
if err := cmd.Start(); err != nil {
t.Fatalf("failed to start godoc: %s", err)
@@ -255,71 +186,109 @@ func testWeb(t *testing.T, withIndex bool) {
}
tests := []struct {
path string
match []string
dontmatch []string
needIndex bool
path string
contains []string // substring
match []string // regexp
notContains []string
needIndex bool
releaseTag string // optional release tag that must be in go/build.ReleaseTags
}{
{
path: "/",
match: []string{"Go is an open source programming language"},
path: "/",
contains: []string{"Go is an open source programming language"},
},
{
path: "/pkg/fmt/",
match: []string{"Package fmt implements formatted I/O"},
path: "/pkg/fmt/",
contains: []string{"Package fmt implements formatted I/O"},
},
{
path: "/src/fmt/",
match: []string{"scan_test.go"},
path: "/src/fmt/",
contains: []string{"scan_test.go"},
},
{
path: "/src/fmt/print.go",
match: []string{"// Println formats using"},
path: "/src/fmt/print.go",
contains: []string{"// Println formats using"},
},
{
path: "/pkg",
match: []string{
contains: []string{
"Standard library",
"Package fmt implements formatted I/O",
},
dontmatch: []string{
notContains: []string{
"internal/syscall",
"cmd/gc",
},
},
{
path: "/pkg/?m=all",
match: []string{
contains: []string{
"Standard library",
"Package fmt implements formatted I/O",
"internal/syscall/?m=all",
},
dontmatch: []string{
notContains: []string{
"cmd/gc",
},
},
{
path: "/search?q=ListenAndServe",
match: []string{
contains: []string{
"/src",
},
dontmatch: []string{
notContains: []string{
"/pkg/bootstrap",
},
needIndex: true,
},
{
path: "/pkg/strings/",
match: []string{
contains: []string{
`href="/src/strings/strings.go"`,
},
},
{
path: "/cmd/compile/internal/amd64/",
match: []string{
contains: []string{
`href="/src/cmd/compile/internal/amd64/ssa.go"`,
},
},
{
path: "/pkg/math/bits/",
contains: []string{
`Added in Go 1.9`,
},
},
{
path: "/pkg/net/",
contains: []string{
`// IPv6 scoped addressing zone; added in Go 1.1`,
},
},
{
path: "/pkg/net/http/httptrace/",
match: []string{
`Got1xxResponse.*// Go 1\.11`,
},
releaseTag: "go1.11",
},
// Verify we don't add version info to a struct field added the same time
// as the struct itself:
{
path: "/pkg/net/http/httptrace/",
match: []string{
`(?m)GotFirstResponseByte func\(\)\s*$`,
},
},
// Remove trailing periods before adding semicolons:
{
path: "/pkg/database/sql/",
contains: []string{
"The number of connections currently in use; added in Go 1.11",
"The number of idle connections; added in Go 1.11",
},
releaseTag: "go1.11",
},
}
for _, test := range tests {
if test.needIndex && !withIndex {
@@ -332,18 +301,34 @@ func testWeb(t *testing.T, withIndex bool) {
continue
}
body, err := ioutil.ReadAll(resp.Body)
strBody := string(body)
resp.Body.Close()
if err != nil {
t.Errorf("GET %s: failed to read body: %s (response: %v)", url, err, resp)
}
isErr := false
for _, substr := range test.match {
for _, substr := range test.contains {
if test.releaseTag != "" && !hasTag(test.releaseTag) {
continue
}
if !bytes.Contains(body, []byte(substr)) {
t.Errorf("GET %s: wanted substring %q in body", url, substr)
isErr = true
}
}
for _, substr := range test.dontmatch {
for _, re := range test.match {
if test.releaseTag != "" && !hasTag(test.releaseTag) {
continue
}
if ok, err := regexp.MatchString(re, strBody); !ok || err != nil {
if err != nil {
t.Fatalf("Bad regexp %q: %v", re, err)
}
t.Errorf("GET %s: wanted to match %s in body", url, re)
isErr = true
}
}
for _, substr := range test.notContains {
if bytes.Contains(body, []byte(substr)) {
t.Errorf("GET %s: didn't want substring %q in body", url, substr)
isErr = true
@@ -398,6 +383,8 @@ func main() { print(lib.V) }
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, fmt.Sprintf("GOROOT=%s", filepath.Join(tmpdir, "goroot")))
cmd.Env = append(cmd.Env, fmt.Sprintf("GOPATH=%s", filepath.Join(tmpdir, "gopath")))
cmd.Env = append(cmd.Env, "GO111MODULE=off")
cmd.Env = append(cmd.Env, "GOPROXY=off")
cmd.Stdout = os.Stderr
stderr, err := cmd.StderrPipe()
if err != nil {

View File

@@ -21,6 +21,7 @@ import (
"text/template"
"golang.org/x/tools/godoc"
"golang.org/x/tools/godoc/env"
"golang.org/x/tools/godoc/redirect"
"golang.org/x/tools/godoc/vfs"
)
@@ -30,8 +31,6 @@ var (
fs = vfs.NameSpace{}
)
var enforceHosts = false // set true in production on app engine
// hostEnforcerHandler redirects requests to "http://foo.golang.org/bar"
// to "https://golang.org/bar".
// It permits requests to the host "godoc-test.golang.org" for testing and
@@ -41,11 +40,11 @@ type hostEnforcerHandler struct {
}
func (h hostEnforcerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !enforceHosts {
if !env.EnforceHosts() {
h.h.ServeHTTP(w, r)
return
}
if r.TLS == nil || !h.validHost(r.Host) {
if !h.isHTTPS(r) || !h.validHost(r.Host) {
r.URL.Scheme = "https"
if h.validHost(r.Host) {
r.URL.Host = r.Host
@@ -59,9 +58,17 @@ func (h hostEnforcerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.h.ServeHTTP(w, r)
}
func (h hostEnforcerHandler) isHTTPS(r *http.Request) bool {
return r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https"
}
func (h hostEnforcerHandler) validHost(host string) bool {
switch strings.ToLower(host) {
case "golang.org", "godoc-test.golang.org", "golang.google.cn":
case "golang.org", "golang.google.cn":
return true
}
if strings.HasSuffix(host, "-dot-golang-org.appspot.com") {
// staging/test
return true
}
return false
@@ -105,28 +112,23 @@ func readTemplate(name string) *template.Template {
return t
}
func readTemplates(p *godoc.Presentation, html bool) {
p.PackageText = readTemplate("package.txt")
p.SearchText = readTemplate("search.txt")
if html || p.HTMLMode {
codewalkHTML = readTemplate("codewalk.html")
codewalkdirHTML = readTemplate("codewalkdir.html")
p.CallGraphHTML = readTemplate("callgraph.html")
p.DirlistHTML = readTemplate("dirlist.html")
p.ErrorHTML = readTemplate("error.html")
p.ExampleHTML = readTemplate("example.html")
p.GodocHTML = readTemplate("godoc.html")
p.ImplementsHTML = readTemplate("implements.html")
p.MethodSetHTML = readTemplate("methodset.html")
p.PackageHTML = readTemplate("package.html")
p.PackageRootHTML = readTemplate("packageroot.html")
p.SearchHTML = readTemplate("search.html")
p.SearchDocHTML = readTemplate("searchdoc.html")
p.SearchCodeHTML = readTemplate("searchcode.html")
p.SearchTxtHTML = readTemplate("searchtxt.html")
p.SearchDescXML = readTemplate("opensearch.xml")
}
func readTemplates(p *godoc.Presentation) {
codewalkHTML = readTemplate("codewalk.html")
codewalkdirHTML = readTemplate("codewalkdir.html")
p.CallGraphHTML = readTemplate("callgraph.html")
p.DirlistHTML = readTemplate("dirlist.html")
p.ErrorHTML = readTemplate("error.html")
p.ExampleHTML = readTemplate("example.html")
p.GodocHTML = readTemplate("godoc.html")
p.ImplementsHTML = readTemplate("implements.html")
p.MethodSetHTML = readTemplate("methodset.html")
p.PackageHTML = readTemplate("package.html")
p.PackageRootHTML = readTemplate("packageroot.html")
p.SearchHTML = readTemplate("search.html")
p.SearchDocHTML = readTemplate("searchdoc.html")
p.SearchCodeHTML = readTemplate("searchcode.html")
p.SearchTxtHTML = readTemplate("searchtxt.html")
p.SearchDescXML = readTemplate("opensearch.xml")
}
type fmtResponse struct {

BIN
vendor/golang.org/x/tools/cmd/godoc/hg-git-mapping.bin generated vendored Normal file

Binary file not shown.

View File

@@ -14,28 +14,20 @@
// (idea is if you say import "compress/zlib", you go to
// http://godoc/pkg/compress/zlib)
//
// Command-line interface:
//
// godoc packagepath [name ...]
//
// godoc compress/zlib
// - prints doc for package compress/zlib
// godoc crypto/block Cipher NewCMAC
// - prints doc for Cipher and NewCMAC in package crypto/block
// +build !appengine
// +build !golangorg
package main
import (
"archive/zip"
"bytes"
_ "expvar" // to serve /debug/vars
"flag"
"fmt"
"go/build"
"log"
"net/http"
"net/http/httptest"
_ "net/http/pprof" // to serve /debug/pprof/*
"net/url"
"os"
@@ -53,7 +45,7 @@ import (
"golang.org/x/tools/godoc/vfs/zipfs"
)
const defaultAddr = ":6060" // default webserver address
const defaultAddr = "localhost:6060" // default webserver address
var (
// file system to serve
@@ -66,18 +58,11 @@ var (
analysisFlag = flag.String("analysis", "", `comma-separated list of analyses to perform (supported: type, pointer). See http://golang.org/lib/godoc/analysis/help.html`)
// network
httpAddr = flag.String("http", "", "HTTP service address (e.g., '"+defaultAddr+"')")
serverAddr = flag.String("server", "", "webserver address for command line searches")
httpAddr = flag.String("http", defaultAddr, "HTTP service address")
// layout control
html = flag.Bool("html", false, "print HTML in command-line mode")
srcMode = flag.Bool("src", false, "print (exported) source in command-line mode")
allMode = flag.Bool("all", false, "include unexported identifiers in command-line mode")
urlFlag = flag.String("url", "", "print HTML for named URL")
// command-line searches
query = flag.Bool("q", false, "arguments are considered search queries")
verbose = flag.Bool("v", false, "verbose mode")
// file system roots
@@ -85,11 +70,9 @@ var (
goroot = flag.String("goroot", findGOROOT(), "Go root directory")
// layout control
tabWidth = flag.Int("tabwidth", 4, "tab width")
showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings")
templateDir = flag.String("templates", "", "load templates/JS/CSS from disk in this directory")
showPlayground = flag.Bool("play", false, "enable playground in web interface")
showExamples = flag.Bool("ex", false, "show examples in command line mode")
showPlayground = flag.Bool("play", false, "enable playground")
declLinks = flag.Bool("links", true, "link identifiers to their declarations")
// search index
@@ -103,10 +86,19 @@ var (
notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show")
)
// An httpResponseRecorder is an http.ResponseWriter
type httpResponseRecorder struct {
body *bytes.Buffer
header http.Header
code int
}
func (w *httpResponseRecorder) Header() http.Header { return w.header }
func (w *httpResponseRecorder) Write(b []byte) (int, error) { return len(b), nil }
func (w *httpResponseRecorder) WriteHeader(code int) { w.code = code }
func usage() {
fmt.Fprintf(os.Stderr,
"usage: godoc package [name ...]\n"+
" godoc -http="+defaultAddr+"\n")
fmt.Fprintf(os.Stderr, "usage: godoc -http="+defaultAddr+"\n")
flag.PrintDefaults()
os.Exit(2)
}
@@ -133,22 +125,22 @@ func handleURLFlag() {
// Invoke default HTTP handler to serve request
// to our buffering httpWriter.
w := httptest.NewRecorder()
w := &httpResponseRecorder{code: 200, header: make(http.Header), body: new(bytes.Buffer)}
http.DefaultServeMux.ServeHTTP(w, req)
// Return data, error, or follow redirect.
switch w.Code {
switch w.code {
case 200: // ok
os.Stdout.Write(w.Body.Bytes())
os.Stdout.Write(w.body.Bytes())
return
case 301, 302, 303, 307: // redirect
redirect := w.HeaderMap.Get("Location")
redirect := w.header.Get("Location")
if redirect == "" {
log.Fatalf("HTTP %d without Location header", w.Code)
log.Fatalf("HTTP %d without Location header", w.code)
}
urlstr = redirect
default:
log.Fatalf("HTTP error %d", w.Code)
log.Fatalf("HTTP error %d", w.code)
}
}
log.Fatalf("too many redirects")
@@ -173,7 +165,7 @@ func main() {
// Check usage: server and no args.
if (*httpAddr != "" || *urlFlag != "") && (flag.NArg() > 0) {
fmt.Fprintln(os.Stderr, "can't use -http with args.")
fmt.Fprintln(os.Stderr, "Unexpected arguments.")
usage()
}
@@ -183,8 +175,10 @@ func main() {
usage()
}
var fsGate chan bool
fsGate = make(chan bool, 20)
// Set the resolved goroot.
vfs.GOROOT = *goroot
fsGate := make(chan bool, 20)
// Determine file system to use.
if *zipfile == "" {
@@ -211,8 +205,6 @@ func main() {
fs.Bind("/src", gatefs.New(vfs.OS(p), fsGate), "/src", vfs.BindAfter)
}
httpMode := *httpAddr != ""
var typeAnalysis, pointerAnalysis bool
if *analysisFlag != "" {
for _, a := range strings.Split(*analysisFlag, ",") {
@@ -230,7 +222,7 @@ func main() {
corpus := godoc.NewCorpus(fs)
corpus.Verbose = *verbose
corpus.MaxResults = *maxResults
corpus.IndexEnabled = *indexEnabled && httpMode
corpus.IndexEnabled = *indexEnabled
if *maxResults == 0 {
corpus.IndexFullText = false
}
@@ -241,29 +233,24 @@ func main() {
if *writeIndex {
corpus.IndexThrottle = 1.0
corpus.IndexEnabled = true
}
if *writeIndex || httpMode || *urlFlag != "" {
if httpMode {
go initCorpus(corpus)
} else {
initCorpus(corpus)
}
initCorpus(corpus)
} else {
go initCorpus(corpus)
}
// Initialize the version info before readTemplates, which saves
// the map value in a method value.
corpus.InitVersionInfo()
pres = godoc.NewPresentation(corpus)
pres.TabWidth = *tabWidth
pres.ShowTimestamps = *showTimestamps
pres.ShowPlayground = *showPlayground
pres.ShowExamples = *showExamples
pres.DeclLinks = *declLinks
pres.SrcMode = *srcMode
pres.HTMLMode = *html
pres.AllMode = *allMode
if *notesRx != "" {
pres.NotesRx = regexp.MustCompile(*notesRx)
}
readTemplates(pres, httpMode || *urlFlag != "")
readTemplates(pres)
registerHandlers(pres)
if *writeIndex {
@@ -298,67 +285,51 @@ func main() {
return
}
if httpMode {
// HTTP server mode.
var handler http.Handler = http.DefaultServeMux
if *verbose {
log.Printf("Go Documentation Server")
log.Printf("version = %s", runtime.Version())
log.Printf("address = %s", *httpAddr)
log.Printf("goroot = %s", *goroot)
log.Printf("tabwidth = %d", *tabWidth)
switch {
case !*indexEnabled:
log.Print("search index disabled")
case *maxResults > 0:
log.Printf("full text index enabled (maxresults = %d)", *maxResults)
default:
log.Print("identifier search index enabled")
var handler http.Handler = http.DefaultServeMux
if *verbose {
log.Printf("Go Documentation Server")
log.Printf("version = %s", runtime.Version())
log.Printf("address = %s", *httpAddr)
log.Printf("goroot = %s", *goroot)
switch {
case !*indexEnabled:
log.Print("search index disabled")
case *maxResults > 0:
log.Printf("full text index enabled (maxresults = %d)", *maxResults)
default:
log.Print("identifier search index enabled")
}
fs.Fprint(os.Stderr)
handler = loggingHandler(handler)
}
// Initialize search index.
if *indexEnabled {
go corpus.RunIndexer()
}
// Start type/pointer analysis.
if typeAnalysis || pointerAnalysis {
go analysis.Run(pointerAnalysis, &corpus.Analysis)
}
if runHTTPS != nil {
go func() {
if err := runHTTPS(handler); err != nil {
log.Fatalf("ListenAndServe TLS: %v", err)
}
fs.Fprint(os.Stderr)
handler = loggingHandler(handler)
}
// Initialize search index.
if *indexEnabled {
go corpus.RunIndexer()
}
// Start type/pointer analysis.
if typeAnalysis || pointerAnalysis {
go analysis.Run(pointerAnalysis, &corpus.Analysis)
}
if runHTTPS != nil {
go func() {
if err := runHTTPS(handler); err != nil {
log.Fatalf("ListenAndServe TLS: %v", err)
}
}()
}
// Start http server.
if *verbose {
log.Println("starting HTTP server")
}
if wrapHTTPMux != nil {
handler = wrapHTTPMux(handler)
}
if err := http.ListenAndServe(*httpAddr, handler); err != nil {
log.Fatalf("ListenAndServe %s: %v", *httpAddr, err)
}
return
}()
}
if *query {
handleRemoteSearch()
return
// Start http server.
if *verbose {
log.Println("starting HTTP server")
}
build.Default.GOROOT = *goroot
if err := godoc.CommandLine(os.Stdout, fs, pres, flag.Args()); err != nil {
log.Print(err)
if wrapHTTPMux != nil {
handler = wrapHTTPMux(handler)
}
if err := http.ListenAndServe(*httpAddr, handler); err != nil {
log.Fatalf("ListenAndServe %s: %v", *httpAddr, err)
}
}

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !appengine
// +build !golangorg
package main

155
vendor/golang.org/x/tools/cmd/godoc/regtest_test.go generated vendored Normal file
View File

@@ -0,0 +1,155 @@
// Copyright 2018 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.
// Regression tests to run against a production instance of godoc.
package main_test
import (
"bytes"
"flag"
"io"
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strings"
"testing"
)
var host = flag.String("regtest.host", "", "host to run regression test against")
func init() {
flag.Parse()
*host = strings.TrimSuffix(*host, "/")
}
func TestLiveServer(t *testing.T) {
if *host == "" {
t.Skip("regtest.host flag missing.")
}
substringTests := []struct {
Message string
Path string
Substring string
Regexp string
NoAnalytics bool // expect the response to not contain GA.
PostBody string
}{
{
Path: "/doc/faq",
Substring: "What is the purpose of the project",
},
{
Path: "/pkg/",
Substring: "Package tar",
},
{
Path: "/pkg/os/",
Substring: "func Open",
},
{
Path: "/pkg/net/http/",
Substring: `title="Added in Go 1.11"`,
Message: "version information not present - failed InitVersionInfo?",
},
{
Path: "/robots.txt",
Substring: "Disallow: /search",
Message: "robots not present - not deployed from Dockerfile?",
NoAnalytics: true,
},
{
Path: "/change/75944e2e3a63",
Substring: "bdb10cf",
Message: "no change redirect - hg to git mapping not registered?",
NoAnalytics: true,
},
{
Path: "/dl/",
Substring: "go1.11.windows-amd64.msi",
Message: "missing data on dl page - misconfiguration of datastore?",
},
{
Path: "/dl/?mode=json",
Substring: ".windows-amd64.msi",
NoAnalytics: true,
},
{
Message: "broken shortlinks - misconfiguration of datastore or memcache?",
Path: "/s/go2design",
Regexp: "proposal.*Found",
NoAnalytics: true,
},
{
Message: "incorrect search result - broken index?",
Path: "/search?q=IsDir",
Substring: "src/os/types.go",
},
{
Path: "/compile",
PostBody: "body=" + url.QueryEscape("package main; func main() { print(6*7); }"),
Regexp: `^{"compile_errors":"","output":"42"}$`,
NoAnalytics: true,
},
{
Path: "/compile",
PostBody: "body=" + url.QueryEscape("//empty"),
Substring: "expected 'package', found 'EOF'",
NoAnalytics: true,
},
{
Path: "/compile",
PostBody: "version=2&body=package+main%3Bimport+(%22fmt%22%3B%22time%22)%3Bfunc+main()%7Bfmt.Print(%22A%22)%3Btime.Sleep(time.Second)%3Bfmt.Print(%22B%22)%7D",
Regexp: `^{"Errors":"","Events":\[{"Message":"A","Kind":"stdout","Delay":0},{"Message":"B","Kind":"stdout","Delay":1000000000}\]}$`,
NoAnalytics: true,
},
}
for _, tc := range substringTests {
t.Run(tc.Path, func(t *testing.T) {
method := "GET"
var reqBody io.Reader
if tc.PostBody != "" {
method = "POST"
reqBody = strings.NewReader(tc.PostBody)
}
req, err := http.NewRequest(method, *host+tc.Path, reqBody)
if err != nil {
t.Fatalf("NewRequest: %v", err)
}
if reqBody != nil {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
resp, err := http.DefaultTransport.RoundTrip(req)
if err != nil {
t.Fatalf("RoundTrip: %v", err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("ReadAll: %v", err)
}
const googleAnalyticsID = "UA-11222381-2" // golang.org analytics ID
if !tc.NoAnalytics && !bytes.Contains(body, []byte(googleAnalyticsID)) {
t.Errorf("want response to contain analytics tracking ID")
}
if tc.Substring != "" {
tc.Regexp = regexp.QuoteMeta(tc.Substring)
}
re := regexp.MustCompile(tc.Regexp)
if !re.Match(body) {
t.Log("------ actual output -------")
t.Log(string(body))
t.Log("----------------------------")
if tc.Message != "" {
t.Log(tc.Message)
}
t.Fatalf("wanted response to match %s", tc.Regexp)
}
})
}
}

View File

@@ -1,72 +0,0 @@
// Copyright 2009 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.
// +build !appengine
package main
import (
"errors"
"flag"
"io"
"log"
"net/http"
"net/url"
"os"
)
func handleRemoteSearch() {
// Command-line queries.
for i := 0; i < flag.NArg(); i++ {
res, err := remoteSearch(flag.Arg(i))
if err != nil {
log.Fatalf("remoteSearch: %s", err)
}
io.Copy(os.Stdout, res.Body)
}
return
}
// remoteSearchURL returns the search URL for a given query as needed by
// remoteSearch. If html is set, an html result is requested; otherwise
// the result is in textual form.
// Adjust this function as necessary if modeNames or FormValue parameters
// change.
func remoteSearchURL(query string, html bool) string {
s := "/search?m=text&q="
if html {
s = "/search?q="
}
return s + url.QueryEscape(query)
}
func remoteSearch(query string) (res *http.Response, err error) {
// list of addresses to try
var addrs []string
if *serverAddr != "" {
// explicit server address - only try this one
addrs = []string{*serverAddr}
} else {
addrs = []string{
defaultAddr,
"golang.org",
}
}
// remote search
search := remoteSearchURL(query, *html)
for _, addr := range addrs {
url := "http://" + addr + search
res, err = http.Get(url)
if err == nil && res.StatusCode == http.StatusOK {
break
}
}
if err == nil && res.StatusCode != http.StatusOK {
err = errors.New(res.Status)
}
return
}

View File

@@ -1,134 +0,0 @@
#!/usr/bin/env bash
# Copyright 2011 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.
# This script creates a complete godoc app in $APPDIR.
# It copies the cmd/godoc and src/go/... sources from GOROOT,
# synthesizes an app.yaml file, and creates the .zip, index, and
# configuration files.
#
# If an argument is provided it is assumed to be the app-engine godoc directory.
# Without an argument, $APPDIR is used instead. If GOROOT is not set, "go env"
# is consulted to find the $GOROOT.
#
# The script creates a .zip file representing the $GOROOT file system
# and computes the correspondig search index files. These files are then
# copied to $APPDIR. A corresponding godoc configuration file is created
# in $APPDIR/appconfig.go.
ZIPFILE=godoc.zip
INDEXFILE=godoc.index
SPLITFILES=index.split.
GODOC=golang.org/x/tools/cmd/godoc
CONFIGFILE=$GODOC/appconfig.go
error() {
echo "error: $1"
exit 2
}
getArgs() {
if [ -z $APPENGINE_SDK ]; then
error "APPENGINE_SDK environment variable not set"
fi
if [ ! -x $APPENGINE_SDK/goapp ]; then
error "couldn't find goapp command in $APPENGINE_SDK"
fi
if [ -z $GOROOT ]; then
GOROOT=$(go env GOROOT)
echo "GOROOT not set explicitly, using go env value instead"
fi
if [ -z $APPDIR ]; then
if [ $# == 0 ]; then
error "APPDIR not set, and no argument provided"
fi
APPDIR=$1
echo "APPDIR not set, using argument instead"
fi
# safety checks
if [ ! -d $GOROOT ]; then
error "$GOROOT is not a directory"
fi
if [ -e $APPDIR ]; then
error "$APPDIR exists; check and remove it before trying again"
fi
# reporting
echo "GOROOT = $GOROOT"
echo "APPDIR = $APPDIR"
}
fetchGodoc() {
echo "*** Fetching godoc (if not already in GOPATH)"
unset GOBIN
go=$APPENGINE_SDK/goapp
$go get -d -tags appengine $GODOC
mkdir -p $APPDIR/$GODOC
cp $(find $($go list -f '{{.Dir}}' $GODOC) -mindepth 1 -maxdepth 1 -type f) $APPDIR/$GODOC/
}
makeAppYaml() {
echo "*** make $APPDIR/app.yaml"
cat > $APPDIR/app.yaml <<EOF
application: godoc
version: 1
runtime: go
api_version: go1
handlers:
- url: /.*
script: _go_app
EOF
}
makeZipfile() {
echo "*** make $APPDIR/$ZIPFILE"
zip -q -r $APPDIR/$ZIPFILE $GOROOT/*
}
makeIndexfile() {
echo "*** make $APPDIR/$INDEXFILE"
GOPATH= godoc -write_index -index_files=$APPDIR/$INDEXFILE -zip=$APPDIR/$ZIPFILE
}
splitIndexfile() {
echo "*** split $APPDIR/$INDEXFILE"
split -b8m $APPDIR/$INDEXFILE $APPDIR/$SPLITFILES
}
makeConfigfile() {
echo "*** make $APPDIR/$CONFIGFILE"
cat > $APPDIR/$CONFIGFILE <<EOF
package main
// GENERATED FILE - DO NOT MODIFY BY HAND.
// (generated by golang.org/x/tools/cmd/godoc/setup-godoc-app.bash)
const (
// .zip filename
zipFilename = "$ZIPFILE"
// goroot directory in .zip file
zipGoroot = "$GOROOT"
// glob pattern describing search index files
// (if empty, the index is built at run-time)
indexFilenames = "$SPLITFILES*"
)
EOF
}
getArgs "$@"
set -e
mkdir $APPDIR
fetchGodoc
makeAppYaml
makeZipfile
makeIndexfile
splitIndexfile
makeConfigfile
echo "*** setup complete"

147
vendor/golang.org/x/tools/cmd/golsp/main.go generated vendored Normal file
View File

@@ -0,0 +1,147 @@
// Copyright 2018 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.
// The golsp command is an LSP server for Go.
// The Language Server Protocol allows any text editor
// to be extended with IDE-like features;
// see https://langserver.org/ for details.
package main // import "golang.org/x/tools/cmd/golsp"
import (
"context"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"runtime"
"runtime/pprof"
"runtime/trace"
"time"
"golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp"
)
var (
cpuprofile = flag.String("cpuprofile", "", "write CPU profile to this file")
memprofile = flag.String("memprofile", "", "write memory profile to this file")
traceFlag = flag.String("trace", "", "write trace log to this file")
// Flags for compatitibility with VSCode.
logfile = flag.String("logfile", "", "filename to log to")
mode = flag.String("mode", "", "no effect")
)
func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: golsp [flags]\n")
flag.PrintDefaults()
}
flag.Parse()
if flag.NArg() > 0 {
flag.Usage()
os.Exit(2)
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal(err)
}
// NB: profile won't be written in case of error.
defer pprof.StopCPUProfile()
}
if *traceFlag != "" {
f, err := os.Create(*traceFlag)
if err != nil {
log.Fatal(err)
}
if err := trace.Start(f); err != nil {
log.Fatal(err)
}
// NB: trace log won't be written in case of error.
defer func() {
trace.Stop()
log.Printf("To view the trace, run:\n$ go tool trace view %s", *traceFlag)
}()
}
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal(err)
}
// NB: memprofile won't be written in case of error.
defer func() {
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatalf("Writing memory profile: %v", err)
}
f.Close()
}()
}
out := os.Stderr
if *logfile != "" {
f, err := os.Create(*logfile)
if err != nil {
log.Fatalf("Unable to create log file: %v", err)
}
defer f.Close()
log.SetOutput(io.MultiWriter(os.Stderr, f))
out = f
}
if err := lsp.RunServer(
context.Background(),
jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout),
func(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err *jsonrpc2.Error) {
if err != nil {
fmt.Fprintf(out, "[Error - %v] %s %s%s %v", time.Now().Format("3:04:05 PM"), direction, method, id, err)
return
}
fmt.Fprintf(out, "[Trace - %v] ", time.Now().Format("3:04:05 PM"))
switch direction {
case jsonrpc2.Send:
fmt.Fprint(out, "Received ")
case jsonrpc2.Receive:
fmt.Fprint(out, "Sending ")
}
switch {
case id == nil:
fmt.Fprint(out, "notification ")
case elapsed >= 0:
fmt.Fprint(out, "response ")
default:
fmt.Fprint(out, "request ")
}
fmt.Fprintf(out, "'%s", method)
switch {
case id == nil:
// do nothing
case id.Name != "":
fmt.Fprintf(out, " - (%s)", id.Name)
default:
fmt.Fprintf(out, " - (%d)", id.Number)
}
fmt.Fprint(out, "'")
if elapsed >= 0 {
fmt.Fprintf(out, " in %vms", elapsed.Nanoseconds()/1000)
}
params := string(*payload)
if params == "null" {
params = "{}"
}
fmt.Fprintf(out, ".\r\nParams: %s\r\n\r\n\r\n", params)
},
); err != nil {
log.Fatal(err)
}
}

View File

@@ -48,6 +48,8 @@ func TestGeneratedFiles(t *testing.T) {
env = append(env, envVar)
}
}
// gorename currently requires GOPATH mode.
env = append(env, "GO111MODULE=off")
// Testing renaming in packages that include cgo files:
for iter, renameTest := range []test{

View File

@@ -387,7 +387,7 @@ func setup() {
yaccpar = strings.Replace(yaccpartext, "$$", prefix, -1)
openup()
fmt.Fprintf(ftable, "// Code generated by goyacc %s. DO NOT EDIT.", strings.Join(os.Args[1:], " "))
fmt.Fprintf(ftable, "// Code generated by goyacc %s. DO NOT EDIT.\n", strings.Join(os.Args[1:], " "))
defin(0, "$end")
extval = PRIVATE // tokens start in unicode 'private use'
@@ -1272,7 +1272,7 @@ l1:
func cpyact(curprod []int, max int) {
if !lflag {
fmt.Fprintf(fcode, "\n\t\t//line %v:%v", infile, lineno)
fmt.Fprintf(fcode, "\n//line %v:%v", infile, lineno)
}
fmt.Fprint(fcode, "\n\t\t")

View File

@@ -18,7 +18,7 @@ import (
"golang.org/x/tools/go/ssa/ssautil"
)
// Callees reports the possible callees of the function call site
// The callees function reports the possible callees of the function call site
// identified by the specified source location.
func callees(q *Query) error {
lconf := loader.Config{Build: q.Build}

View File

@@ -16,7 +16,7 @@ import (
"golang.org/x/tools/go/ssa/ssautil"
)
// Callers reports the possible callers of the function
// The callers function reports the possible callers of the function
// immediately enclosing the specified source location.
//
func callers(q *Query) error {

View File

@@ -16,7 +16,7 @@ import (
"golang.org/x/tools/go/ssa/ssautil"
)
// Callstack displays an arbitrary path from a root of the callgraph
// The callstack function displays an arbitrary path from a root of the callgraph
// to the function at the current position.
//
// The information may be misleading in a context-insensitive

View File

@@ -8,7 +8,7 @@ import (
"bytes"
"fmt"
"go/ast"
exact "go/constant"
"go/constant"
"go/token"
"go/types"
"os"
@@ -349,10 +349,10 @@ func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error
type describeValueResult struct {
qpos *queryPos
expr ast.Expr // query node
typ types.Type // type of expression
constVal exact.Value // value of expression, if constant
obj types.Object // var/func/const object, if expr was Ident
expr ast.Expr // query node
typ types.Type // type of expression
constVal constant.Value // value of expression, if constant
obj types.Object // var/func/const object, if expr was Ident
methods []*types.Selection
fields []describeField
}

View File

@@ -35,6 +35,7 @@ import (
"go/token"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
@@ -49,6 +50,18 @@ import (
guru "golang.org/x/tools/cmd/guru"
)
func init() {
// This test currently requires GOPATH mode.
// Explicitly disabling module mode should suffix, but
// we'll also turn off GOPROXY just for good measure.
if err := os.Setenv("GO111MODULE", "off"); err != nil {
log.Fatal(err)
}
if err := os.Setenv("GOPROXY", "off"); err != nil {
log.Fatal(err)
}
}
var updateFlag = flag.Bool("update", false, "Update the golden files.")
type query struct {
@@ -268,11 +281,6 @@ func TestGuru(t *testing.T) {
// tests once we drop support for go1.8.
t.Skip()
}
if filename == "testdata/src/referrers/main.go" && !contains(build.Default.ReleaseTags, "go1.11") {
// Disabling broken test on Go 1.9 and Go 1.10. https://golang.org/issue/24421
// TODO(gri,adonovan): fix this test.
t.Skip()
}
json := strings.Contains(filename, "-json/")
queries := parseQueries(t, filename)

View File

@@ -19,7 +19,7 @@ import (
"golang.org/x/tools/refactor/importgraph"
)
// Implements displays the "implements" relation as it pertains to the
// The implements function displays the "implements" relation as it pertains to the
// selected type.
// If the selection is a method, 'implements' displays
// the corresponding methods of the types that would have been reported

View File

@@ -27,7 +27,7 @@ import (
"golang.org/x/tools/refactor/importgraph"
)
// Referrers reports all identifiers that resolve to the same object
// The referrers function reports all identifiers that resolve to the same object
// as the queried identifier, within any package in the workspace.
func referrers(q *Query) error {
fset := token.NewFileSet()

View File

@@ -60,5 +60,5 @@ references to var notexported int
-------- @referrers ref-type-U --------
references to type U int
open nosuchfile.y: no such file or directory (+ 1 more refs in this file)
open testdata/src/referrers/nosuchfile.y: no such file or directory (+ 1 more refs in this file)

View File

@@ -1,22 +0,0 @@
// Copyright 2012 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.
// +build appengine
package main
import (
"mime"
"golang.org/x/tools/present"
)
func init() {
initTemplates("./present/")
present.PlayEnabled = true
initPlayground("./present/", nil)
// App Engine has no /etc/mime.types
mime.AddExtensionType(".svg", "image/svg+xml")
}

View File

@@ -13,6 +13,7 @@ import (
"os"
"path/filepath"
"sort"
"strings"
"golang.org/x/tools/present"
)
@@ -21,19 +22,18 @@ func init() {
http.HandleFunc("/", dirHandler)
}
// dirHandler serves a directory listing for the requested path, rooted at basePath.
// dirHandler serves a directory listing for the requested path, rooted at *contentPath.
func dirHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/favicon.ico" {
http.Error(w, "not found", 404)
http.NotFound(w, r)
return
}
const base = "."
name := filepath.Join(base, r.URL.Path)
name := filepath.Join(*contentPath, r.URL.Path)
if isDoc(name) {
err := renderDoc(w, name)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
@@ -43,12 +43,12 @@ func dirHandler(w http.ResponseWriter, r *http.Request) {
addr = r.RemoteAddr
}
log.Printf("request from %s: %s", addr, err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} else if isDir {
return
}
http.FileServer(http.Dir(base)).ServeHTTP(w, r)
http.FileServer(http.Dir(*contentPath)).ServeHTTP(w, r)
}
func isDoc(path string) bool {
@@ -138,7 +138,9 @@ func dirList(w io.Writer, name string) (isDir bool, err error) {
if err != nil {
return false, err
}
d := &dirListData{Path: name}
strippedPath := strings.TrimPrefix(name, filepath.Clean(*contentPath))
strippedPath = strings.TrimPrefix(strippedPath, "/")
d := &dirListData{Path: strippedPath}
for _, fi := range fis {
// skip the golang.org directory
if name == "." && fi.Name() == "golang.org" {
@@ -146,15 +148,16 @@ func dirList(w io.Writer, name string) (isDir bool, err error) {
}
e := dirEntry{
Name: fi.Name(),
Path: filepath.ToSlash(filepath.Join(name, fi.Name())),
Path: filepath.ToSlash(filepath.Join(strippedPath, fi.Name())),
}
if fi.IsDir() && showDir(e.Name) {
d.Dirs = append(d.Dirs, e)
continue
}
if isDoc(e.Name) {
if p, err := parse(e.Path, present.TitlesOnly); err != nil {
log.Println(err)
fn := filepath.ToSlash(filepath.Join(name, fi.Name()))
if p, err := parse(fn, present.TitlesOnly); err != nil {
log.Printf("parse(%q, present.TitlesOnly): %v", fn, err)
} else {
e.Title = p.Title
}

View File

@@ -8,43 +8,38 @@ presents slide and article files from the current directory.
It may be run as a stand-alone command or an App Engine app.
Usage of present:
-base="": base path for slide template and static resources
-http="127.0.0.1:3999": HTTP service address (e.g., '127.0.0.1:3999')
-nacl=false: use Native Client environment playground (prevents non-Go code execution)
-notes=false: enable presenter notes (press 'N' from the browser to display them)
-orighost="": host component of web origin URL (e.g., 'localhost')
-play=true: enable playground (permit execution of arbitrary user code)
The setup of the Go version of NaCl is documented at:
https://golang.org/wiki/NativeClient
To use with App Engine, copy the tools/cmd/present directory to the root of
your application and create an app.yaml file similar to this:
To use with App Engine, copy the files in the tools/cmd/present directory to the
root of your application and create an app.yaml file similar to this:
application: [application]
version: [version]
runtime: go
api_version: go1
runtime: go111
handlers:
- url: /favicon.ico
static_files: present/static/favicon.ico
upload: present/static/favicon.ico
static_files: static/favicon.ico
upload: static/favicon.ico
- url: /static
static_dir: present/static
application_readable: true
static_dir: static
- url: /.*
script: _go_app
script: auto
# nobuild_files is a regexp that identifies which files to not build. It
# is useful for embedding static assets like code snippets and preventing
# them from producing build errors for your project.
nobuild_files: [path regexp for talk materials]
When running on App Engine, content will be served from the ./content/
subdirectory.
Present then can be tested in a local App Engine environment with
goapp serve
GAE_ENV=standard go run .
And deployed using
gcloud app deploy
Input files are named foo.extension, where "extension" defines the format of
the generated output. The supported formats are:
@@ -54,4 +49,4 @@ the generated output. The supported formats are:
The present file format is documented by the present package:
http://godoc.org/golang.org/x/tools/present
*/
package main // import "golang.org/x/tools/cmd/present"
package main

View File

@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !appengine
package main
import (
@@ -23,10 +21,12 @@ import (
const basePkg = "golang.org/x/tools/cmd/present"
var (
httpAddr = flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')")
originHost = flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')")
basePath = flag.String("base", "", "base path for slide template and static resources")
nativeClient = flag.Bool("nacl", false, "use Native Client environment playground (prevents non-Go code execution)")
httpAddr = flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')")
originHost = flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')")
basePath = flag.String("base", "", "base path for slide template and static resources")
contentPath = flag.String("content", ".", "base path for presentation content")
usePlayground = flag.Bool("use_playground", false, "run code snippets using play.golang.org; if false, run them locally and deliver results by WebSocket transport")
nativeClient = flag.Bool("nacl", false, "use Native Client environment playground (prevents non-Go code execution) when using local WebSocket transport")
)
func main() {
@@ -34,6 +34,22 @@ func main() {
flag.BoolVar(&present.NotesEnabled, "notes", false, "enable presenter notes (press 'N' from the browser to display them)")
flag.Parse()
if os.Getenv("GAE_ENV") == "standard" {
log.Print("Configuring for App Engine Standard")
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
*httpAddr = fmt.Sprintf("0.0.0.0:%s", port)
pwd, err := os.Getwd()
if err != nil {
log.Fatalf("Couldn't get pwd: %v\n", err)
}
*basePath = pwd
*usePlayground = true
*contentPath = "./content/"
}
if *basePath == "" {
p, err := build.Default.Import(basePkg, "", build.FindOnly)
if err != nil {
@@ -81,7 +97,7 @@ func main() {
http.Handle("/static/", http.FileServer(http.Dir(*basePath)))
if !ln.Addr().(*net.TCPAddr).IP.IsLoopback() &&
present.PlayEnabled && !*nativeClient {
present.PlayEnabled && !*nativeClient && !*usePlayground {
log.Print(localhostWarning)
}
@@ -121,11 +137,12 @@ You may use the -base flag to specify an alternate location.
const localhostWarning = `
WARNING! WARNING! WARNING!
The present server appears to be listening on an address that is not localhost.
Anyone with access to this address and port will have access to this machine as
the user running present.
The present server appears to be listening on an address that is not localhost
and is configured to run code snippets locally. Anyone with access to this address
and port will have access to this machine as the user running present.
To avoid this message, listen on localhost or run with -play=false.
To avoid this message, listen on localhost, run with -play=false, or run with
-play_socket=false.
If you don't understand this message, hit Control-C to terminate this process.

View File

@@ -9,10 +9,21 @@ import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path/filepath"
"runtime"
"time"
"golang.org/x/tools/godoc/static"
"golang.org/x/tools/playground/socket"
"golang.org/x/tools/present"
// This will register handlers at /compile and /share that will proxy to the
// respective endpoints at play.golang.org. This allows the frontend to call
// these endpoints without needing cross-origin request sharing (CORS).
// Note that this is imported regardless of whether the endpoints are used or
// not (in the case of a local socket connection, they are not called).
_ "golang.org/x/tools/playground"
)
var scripts = []string{"jquery.js", "jquery-ui.js", "playground.js", "play.js"}
@@ -41,3 +52,38 @@ func playScript(root, transport string) {
http.ServeContent(w, r, "", modTime, bytes.NewReader(b))
})
}
func initPlayground(basepath string, origin *url.URL) {
if !present.PlayEnabled {
return
}
if *usePlayground {
playScript(basepath, "HTTPTransport")
return
}
if *nativeClient {
// When specifying nativeClient, non-Go code cannot be executed
// because the NaCl setup doesn't support doing so.
socket.RunScripts = false
socket.Environ = func() []string {
if runtime.GOARCH == "amd64" {
return environ("GOOS=nacl", "GOARCH=amd64p32")
}
return environ("GOOS=nacl")
}
}
playScript(basepath, "SocketTransport")
http.Handle("/socket", socket.NewHandler(origin))
}
func playable(c present.Code) bool {
play := present.PlayEnabled && c.Play
// Restrict playable files to only Go source files when using play.golang.org,
// since there is no method to execute shell scripts there.
if *usePlayground {
return play && c.Ext == ".go"
}
return play
}

View File

@@ -1,23 +0,0 @@
// Copyright 2015 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.
// +build appengine appenginevm
package main
import (
"net/url"
"golang.org/x/tools/present"
_ "golang.org/x/tools/playground"
)
func initPlayground(basepath string, origin *url.URL) {
playScript(basepath, "HTTPTransport")
}
func playable(c present.Code) bool {
return present.PlayEnabled && c.Play && c.Ext == ".go"
}

View File

@@ -1,36 +0,0 @@
// Copyright 2015 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.
// +build !appengine,!appenginevm
package main
import (
"net/http"
"net/url"
"runtime"
"golang.org/x/tools/playground/socket"
"golang.org/x/tools/present"
)
func initPlayground(basepath string, origin *url.URL) {
if present.PlayEnabled {
if *nativeClient {
socket.RunScripts = false
socket.Environ = func() []string {
if runtime.GOARCH == "amd64" {
return environ("GOOS=nacl", "GOARCH=amd64p32")
}
return environ("GOOS=nacl")
}
}
playScript(basepath, "SocketTransport")
http.Handle("/socket", socket.NewHandler(origin))
}
}
func playable(c present.Code) bool {
return present.PlayEnabled && c.Play
}

View File

@@ -15,7 +15,7 @@ import (
"runtime/pprof"
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/interp"
"golang.org/x/tools/go/ssa/ssautil"
@@ -25,9 +25,9 @@ import (
var (
mode = ssa.BuilderMode(0)
testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.")
testFlag = flag.Bool("test", false, "include implicit test packages and executables")
runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.")
runFlag = flag.Bool("run", false, "interpret the SSA program")
interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter.
The value is a sequence of zero or more more of these letters:
@@ -36,23 +36,25 @@ T [T]race execution of the program. Best for single-threaded programs!
`)
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
args stringListValue
)
func init() {
flag.Var(&mode, "build", ssa.BuilderModeDoc)
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
flag.Var(&args, "arg", "add argument to interpreted program")
}
const usage = `SSA builder and interpreter.
Usage: ssadump [<flag> ...] <args> ...
Usage: ssadump [-build=[DBCSNFL]] [-test] [-run] [-interp=[TR]] [-arg=...] package...
Use -help flag to display options.
Examples:
% ssadump -build=F hello.go # dump SSA form of a single package
% ssadump -build=F -test fmt # dump SSA form of a package and its tests
% ssadump -run -interp=T hello.go # interpret a program, with tracing
` + loader.FromArgsUsage +
`
The -run flag causes ssadump to run the first package named main.
Interpretation of the standard "testing" package is no longer supported.
@@ -67,17 +69,24 @@ func main() {
func doMain() error {
flag.Parse()
args := flag.Args()
if len(flag.Args()) == 0 {
fmt.Fprint(os.Stderr, usage)
os.Exit(1)
}
conf := loader.Config{Build: &build.Default}
cfg := &packages.Config{
Mode: packages.LoadSyntax,
Tests: *testFlag,
}
// Choose types.Sizes from conf.Build.
// TODO(adonovan): remove this when go/packages provides a better way.
var wordSize int64 = 8
switch conf.Build.GOARCH {
switch build.Default.GOARCH {
case "386", "arm":
wordSize = 4
}
conf.TypeChecker.Sizes = &types.StdSizes{
sizes := &types.StdSizes{
MaxAlign: 8,
WordSize: wordSize,
}
@@ -94,11 +103,6 @@ func doMain() error {
}
}
if len(args) == 0 {
fmt.Fprint(os.Stderr, usage)
os.Exit(1)
}
// Profiling support.
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
@@ -110,58 +114,49 @@ func doMain() error {
defer pprof.StopCPUProfile()
}
// Use the initial packages from the command line.
args, err := conf.FromArgs(args, *testFlag)
if err != nil {
return err
}
// The interpreter needs the runtime package.
// Load, parse and type-check the initial packages,
// and, if -run, their dependencies.
if *runFlag {
conf.Import("runtime")
cfg.Mode = packages.LoadAllSyntax
}
// Load, parse and type-check the whole program.
lprog, err := conf.Load()
initial, err := packages.Load(cfg, flag.Args()...)
if err != nil {
return err
}
if len(initial) == 0 {
return fmt.Errorf("no packages")
}
if packages.PrintErrors(initial) > 0 {
return fmt.Errorf("packages contain errors")
}
// Create and build SSA-form program representation.
prog := ssautil.CreateProgram(lprog, mode)
// Create SSA-form program representation.
prog, pkgs := ssautil.AllPackages(initial, mode)
// Build and display only the initial packages
// (and synthetic wrappers), unless -run is specified.
var initpkgs []*ssa.Package
for _, info := range lprog.InitialPackages() {
ssapkg := prog.Package(info.Pkg)
ssapkg.Build()
if info.Pkg.Path() != "runtime" {
initpkgs = append(initpkgs, ssapkg)
for i, p := range pkgs {
if p == nil {
return fmt.Errorf("cannot build SSA for package %s", initial[i])
}
}
// Run the interpreter.
if *runFlag {
if !*runFlag {
// Build and display only the initial packages
// (and synthetic wrappers).
for _, p := range pkgs {
p.Build()
}
} else {
// Run the interpreter.
// Build SSA for all packages.
prog.Build()
var mains []*ssa.Package
if *testFlag {
// If -test, run the tests.
for _, pkg := range initpkgs {
if main := prog.CreateTestMainPackage(pkg); main != nil {
mains = append(mains, main)
}
}
if mains == nil {
return fmt.Errorf("no tests")
}
} else {
// Otherwise, run the main packages.
mains = ssautil.MainPackages(initpkgs)
if len(mains) == 0 {
return fmt.Errorf("no main package")
}
// The interpreter needs the runtime package.
// It is a limitation of go/packages that
// we cannot add "runtime" to its initial set,
// we can only check that it is present.
if prog.ImportedPackage("runtime") == nil {
return fmt.Errorf("-run: program does not depend on runtime")
}
if runtime.GOARCH != build.Default.GOARCH {
@@ -169,12 +164,27 @@ func doMain() error {
build.Default.GOARCH, runtime.GOARCH)
}
for _, main := range mains {
if len(mains) > 1 {
fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path())
}
interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Pkg.Path(), args)
// Run first main package.
for _, main := range ssautil.MainPackages(pkgs) {
fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path())
os.Exit(interp.Interpret(main, interpMode, sizes, main.Pkg.Path(), args))
}
return fmt.Errorf("no main package")
}
return nil
}
// stringListValue is a flag.Value that accumulates strings.
// e.g. --flag=one --flag=two would produce []string{"one", "two"}.
type stringListValue []string
func newStringListValue(val []string, p *[]string) *stringListValue {
*p = val
return (*stringListValue)(p)
}
func (ss *stringListValue) Get() interface{} { return []string(*ss) }
func (ss *stringListValue) String() string { return fmt.Sprintf("%q", *ss) }
func (ss *stringListValue) Set(s string) error { *ss = append(*ss, s); return nil }

View File

@@ -64,7 +64,7 @@ import (
"fmt"
"go/ast"
"go/build"
exact "go/constant"
"go/constant"
"go/format"
"go/parser"
"go/token"
@@ -87,7 +87,7 @@ var (
// Usage is a replacement usage function for the flags package.
func Usage() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, "Usage of stringer:\n")
fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n")
fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T files... # Must be a single package\n")
fmt.Fprintf(os.Stderr, "For more information, see:\n")
@@ -390,7 +390,7 @@ type Value struct {
// by Value.String.
value uint64 // Will be converted to int64 when needed.
signed bool // Whether the constant is a signed type.
str string // The string representation given by the "go/exact" package.
str string // The string representation given by the "go/constant" package.
}
func (v *Value) String() string {
@@ -428,10 +428,24 @@ func (f *File) genDecl(node ast.Node) bool {
for _, spec := range decl.Specs {
vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST.
if vspec.Type == nil && len(vspec.Values) > 0 {
// "X = 1". With no type but a value, the constant is untyped.
// Skip this vspec and reset the remembered type.
// "X = 1". With no type but a value. If the constant is untyped,
// skip this vspec and reset the remembered type.
typ = ""
continue
// If this is a simple type conversion, remember the type.
// We don't mind if this is actually a call; a qualified call won't
// be matched (that will be SelectorExpr, not Ident), and only unusual
// situations will result in a function call that appears to be
// a type conversion.
ce, ok := vspec.Values[0].(*ast.CallExpr)
if !ok {
continue
}
id, ok := ce.Fun.(*ast.Ident)
if !ok {
continue
}
typ = id.Name
}
if vspec.Type != nil {
// "X T". We have a type. Remember it.
@@ -464,11 +478,11 @@ func (f *File) genDecl(node ast.Node) bool {
log.Fatalf("can't handle non-integer constant type %s", typ)
}
value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST.
if value.Kind() != exact.Int {
if value.Kind() != constant.Int {
log.Fatalf("can't happen: constant is not an integer %s", name)
}
i64, isInt := exact.Int64Val(value)
u64, isUint := exact.Uint64Val(value)
i64, isInt := constant.Int64Val(value)
u64, isUint := constant.Uint64Val(value)
if !isInt && !isUint {
log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String())
}

View File

@@ -0,0 +1,41 @@
// Copyright 2018 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.
// Check that constants defined as a conversion are accepted.
package main
import "fmt"
type Other int // Imagine this is in another package.
const (
alpha Other = iota
beta
gamma
delta
)
type Conv int
const (
Alpha = Conv(alpha)
Beta = Conv(beta)
Gamma = Conv(gamma)
Delta = Conv(delta)
)
func main() {
ck(Alpha, "Alpha")
ck(Beta, "Beta")
ck(Gamma, "Gamma")
ck(Delta, "Delta")
ck(42, "Conv(42)")
}
func ck(c Conv, str string) {
if fmt.Sprint(c) != str {
panic("conv.go: " + str)
}
}

View File

@@ -135,7 +135,8 @@ RUN go install cloud.google.com/go/compute/metadata \
# golang sets GOPATH=/go
ADD . /go/src/tip
RUN go install --tags=autocert tip
WORKDIR /go/src/tip
RUN go install --tags=autocert
ENTRYPOINT ["/go/bin/tip"]
# We listen on 8080 (for historical reasons). The service.yaml maps public port 80 to 8080.

View File

@@ -2,7 +2,11 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
VERSION=v2
VERSION ?= $(shell git rev-parse --short HEAD)
MUTABLE_VERSION ?= latest
IMAGE_STAGING := gcr.io/go-dashboard-dev/tip
IMAGE_PROD := gcr.io/symbolic-datum-552/tip
.PHONY: usage
@@ -14,12 +18,23 @@ update-deps:
go install golang.org/x/build/cmd/gitlock
gitlock --update=Dockerfile --ignore=NONE --tags=autocert golang.org/x/tools/cmd/tip
docker-prod: Dockerfile
docker build -f Dockerfile --tag=gcr.io/symbolic-datum-552/tip:$(VERSION) .
docker-dev: Dockerfile
docker build -f Dockerfile --tag=gcr.io/go-dashboard-dev/tip:$(VERSION) .
docker-image: Dockerfile *.go
docker build --force-rm -f Dockerfile --tag=$(IMAGE_PROD):$(VERSION) .
docker tag $(IMAGE_PROD):$(VERSION) $(IMAGE_PROD):$(MUTABLE_VERSION)
docker tag $(IMAGE_PROD):$(VERSION) $(IMAGE_STAGING):$(VERSION)
docker tag $(IMAGE_PROD):$(VERSION) $(IMAGE_STAGING):$(MUTABLE_VERSION)
push-prod: docker-image
docker push $(IMAGE_PROD):$(MUTABLE_VERSION)
docker push $(IMAGE_PROD):$(VERSION)
push-staging: docker-image
docker push $(IMAGE_STAGING):$(MUTABLE_VERSION)
docker push $(IMAGE_STAGING):$(VERSION)
deploy-prod: push-prod
go install golang.org/x/build/cmd/xb
xb --prod kubectl set image deployment/tip-deployment tip=$(IMAGE_PROD):$(VERSION)
deploy-staging: push-staging
go install golang.org/x/build/cmd/xb
xb --staging kubectl set image deployment/tip-deployment tip=$(IMAGE_STAGING):$(VERSION)
push-prod: docker-prod
gcloud docker -- push gcr.io/symbolic-datum-552/tip:$(VERSION)
push-dev: docker-dev
gcloud docker -- push gcr.io/go-dashboard-dev/tip:$(VERSION)

View File

@@ -17,6 +17,7 @@ import (
"crypto/tls"
"log"
"net/http"
"strings"
"cloud.google.com/go/storage"
"golang.org/x/build/autocertcache"
@@ -42,7 +43,7 @@ func certInitAutocert() {
}
autocertManager = &autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(*autoCertDomain),
HostPolicy: autocert.HostWhitelist(strings.Split(*autoCertDomain, ",")...),
Cache: cache,
}
}

View File

@@ -48,7 +48,7 @@ func (b godocBuilder) Init(dir, hostport string, heads map[string]string) (*exec
}
godocBin := filepath.Join(goPath, "bin/godoc")
godoc := exec.Command(godocBin, "-http="+hostport, "-index", "-index_interval=-1s")
godoc := exec.Command(godocBin, "-http="+hostport, "-index", "-index_interval=-1s", "-play")
godoc.Env = []string{"GOROOT=" + goDir}
// TODO(adg): log this somewhere useful
godoc.Stdout = os.Stdout

View File

@@ -1,11 +1,9 @@
apiVersion: v1
kind: ReplicationController
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: tipgodoc
name: tipgodoc-deployment
spec:
replicas: 1
selector:
app: tipgodoc
template:
metadata:
name: tipgodoc
@@ -16,10 +14,10 @@ spec:
- name: cache-volume
emptyDir: {}
containers:
- name: gitmirror
image: gcr.io/symbolic-datum-552/tip:v2
- name: tipgodoc
image: gcr.io/symbolic-datum-552/tip:latest
imagePullPolicy: Always
command: ["/go/bin/tip", "--autocert=tip.golang.org", "--autocert-bucket=golang-tip-autocert"]
command: ["/go/bin/tip", "--autocert=tip.golang.org,beta.golang.org", "--autocert-bucket=golang-tip-autocert"]
env:
- name: TMPDIR
value: /build

View File

@@ -34,7 +34,7 @@ const (
var startTime = time.Now()
var (
autoCertDomain = flag.String("autocert", "", "if non-empty, listen on port 443 and serve a LetsEncrypt cert for this hostname")
autoCertDomain = flag.String("autocert", "", "if non-empty, listen on port 443 and serve a LetsEncrypt cert for this hostname or hostnames (comma-separated)")
autoCertCacheBucket = flag.String("autocert-bucket", "", "if non-empty, the Google Cloud Storage bucket in which to store the LetsEncrypt cache")
)
@@ -120,6 +120,18 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
p.serveStatus(w, r)
return
}
// Redirect the old beta.golang.org URL to tip.golang.org,
// just in case there are old links out there to
// beta.golang.org. (We used to run a "temporary" beta.golang.org
// GCE VM running godoc where "temporary" lasted two years.
// So it lasted so long, there are probably links to it out there.)
if r.Host == "beta.golang.org" {
u := *r.URL
u.Scheme = "https"
u.Host = "tip.golang.org"
http.Redirect(w, r, u.String(), http.StatusFound)
return
}
p.mu.Lock()
proxy := p.proxy
err := p.err

192
vendor/golang.org/x/tools/go/analysis/analysis.go generated vendored Normal file
View File

@@ -0,0 +1,192 @@
package analysis
import (
"flag"
"fmt"
"go/ast"
"go/token"
"go/types"
"reflect"
)
// An Analyzer describes an analysis function and its options.
type Analyzer struct {
// The Name of the analyzer must be a valid Go identifier
// as it may appear in command-line flags, URLs, and so on.
Name string
// Doc is the documentation for the analyzer.
// The part before the first "\n\n" is the title
// (no capital or period, max ~60 letters).
Doc string
// Flags defines any flags accepted by the analyzer.
// The manner in which these flags are exposed to the user
// depends on the driver which runs the analyzer.
Flags flag.FlagSet
// Run applies the analyzer to a package.
// It returns an error if the analyzer failed.
//
// On success, the Run function may return a result
// computed by the Analyzer; its type must match ResultType.
// The driver makes this result available as an input to
// another Analyzer that depends directly on this one (see
// Requires) when it analyzes the same package.
//
// To pass analysis results between packages (and thus
// potentially between address spaces), use Facts, which are
// serializable.
Run func(*Pass) (interface{}, error)
// RunDespiteErrors allows the driver to invoke
// the Run method of this analyzer even on a
// package that contains parse or type errors.
RunDespiteErrors bool
// Requires is a set of analyzers that must run successfully
// before this one on a given package. This analyzer may inspect
// the outputs produced by each analyzer in Requires.
// The graph over analyzers implied by Requires edges must be acyclic.
//
// Requires establishes a "horizontal" dependency between
// analysis passes (different analyzers, same package).
Requires []*Analyzer
// ResultType is the type of the optional result of the Run function.
ResultType reflect.Type
// FactTypes indicates that this analyzer imports and exports
// Facts of the specified concrete types.
// An analyzer that uses facts may assume that its import
// dependencies have been similarly analyzed before it runs.
// Facts must be pointers.
//
// FactTypes establishes a "vertical" dependency between
// analysis passes (same analyzer, different packages).
FactTypes []Fact
}
func (a *Analyzer) String() string { return a.Name }
// A Pass provides information to the Run function that
// applies a specific analyzer to a single Go package.
//
// It forms the interface between the analysis logic and the driver
// program, and has both input and an output components.
//
// As in a compiler, one pass may depend on the result computed by another.
//
// The Run function should not call any of the Pass functions concurrently.
type Pass struct {
Analyzer *Analyzer // the identity of the current analyzer
// syntax and type information
Fset *token.FileSet // file position information
Files []*ast.File // the abstract syntax tree of each file
OtherFiles []string // names of non-Go files of this package
Pkg *types.Package // type information about the package
TypesInfo *types.Info // type information about the syntax trees
// Report reports a Diagnostic, a finding about a specific location
// in the analyzed source code such as a potential mistake.
// It may be called by the Run function.
Report func(Diagnostic)
// ResultOf provides the inputs to this analysis pass, which are
// the corresponding results of its prerequisite analyzers.
// The map keys are the elements of Analysis.Required,
// and the type of each corresponding value is the required
// analysis's ResultType.
ResultOf map[*Analyzer]interface{}
// -- facts --
// ImportObjectFact retrieves a fact associated with obj.
// Given a value ptr of type *T, where *T satisfies Fact,
// ImportObjectFact copies the value to *ptr.
//
// ImportObjectFact panics if called after the pass is complete.
// ImportObjectFact is not concurrency-safe.
ImportObjectFact func(obj types.Object, fact Fact) bool
// ImportPackageFact retrieves a fact associated with package pkg,
// which must be this package or one of its dependencies.
// See comments for ImportObjectFact.
ImportPackageFact func(pkg *types.Package, fact Fact) bool
// ExportObjectFact associates a fact of type *T with the obj,
// replacing any previous fact of that type.
//
// ExportObjectFact panics if it is called after the pass is
// complete, or if obj does not belong to the package being analyzed.
// ExportObjectFact is not concurrency-safe.
ExportObjectFact func(obj types.Object, fact Fact)
// ExportPackageFact associates a fact with the current package.
// See comments for ExportObjectFact.
ExportPackageFact func(fact Fact)
/* Further fields may be added in future. */
// For example, suggested or applied refactorings.
}
// Reportf is a helper function that reports a Diagnostic using the
// specified position and formatted error message.
func (pass *Pass) Reportf(pos token.Pos, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
pass.Report(Diagnostic{Pos: pos, Message: msg})
}
func (pass *Pass) String() string {
return fmt.Sprintf("%s@%s", pass.Analyzer.Name, pass.Pkg.Path())
}
// A Fact is an intermediate fact produced during analysis.
//
// Each fact is associated with a named declaration (a types.Object) or
// with a package as a whole. A single object or package may have
// multiple associated facts, but only one of any particular fact type.
//
// A Fact represents a predicate such as "never returns", but does not
// represent the subject of the predicate such as "function F" or "package P".
//
// Facts may be produced in one analysis pass and consumed by another
// analysis pass even if these are in different address spaces.
// If package P imports Q, all facts about Q produced during
// analysis of that package will be available during later analysis of P.
// Facts are analogous to type export data in a build system:
// just as export data enables separate compilation of several passes,
// facts enable "separate analysis".
//
// Each pass (a, p) starts with the set of facts produced by the
// same analyzer a applied to the packages directly imported by p.
// The analysis may add facts to the set, and they may be exported in turn.
// An analysis's Run function may retrieve facts by calling
// Pass.Import{Object,Package}Fact and update them using
// Pass.Export{Object,Package}Fact.
//
// A fact is logically private to its Analysis. To pass values
// between different analyzers, use the results mechanism;
// see Analyzer.Requires, Analyzer.ResultType, and Pass.ResultOf.
//
// A Fact type must be a pointer.
// Facts are encoded and decoded using encoding/gob.
// A Fact may implement the GobEncoder/GobDecoder interfaces
// to customize its encoding. Fact encoding should not fail.
//
// A Fact should not be modified once exported.
type Fact interface {
AFact() // dummy method to avoid type errors
}
// A Diagnostic is a message associated with a source location.
//
// An Analyzer may return a variety of diagnostics; the optional Category,
// which should be a constant, may be used to classify them.
// It is primarily intended to make it easy to look up documentation.
type Diagnostic struct {
Pos token.Pos
Category string // optional
Message string
}

View File

@@ -0,0 +1,387 @@
// Package analysistest provides utilities for testing analyzers.
package analysistest
import (
"fmt"
"go/token"
"go/types"
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"text/scanner"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/internal/checker"
"golang.org/x/tools/go/packages"
)
// WriteFiles is a helper function that creates a temporary directory
// and populates it with a GOPATH-style project using filemap (which
// maps file names to contents). On success it returns the name of the
// directory and a cleanup function to delete it.
func WriteFiles(filemap map[string]string) (dir string, cleanup func(), err error) {
gopath, err := ioutil.TempDir("", "analysistest")
if err != nil {
return "", nil, err
}
cleanup = func() { os.RemoveAll(gopath) }
for name, content := range filemap {
filename := filepath.Join(gopath, "src", name)
os.MkdirAll(filepath.Dir(filename), 0777) // ignore error
if err := ioutil.WriteFile(filename, []byte(content), 0666); err != nil {
cleanup()
return "", nil, err
}
}
return gopath, cleanup, nil
}
// TestData returns the effective filename of
// the program's "testdata" directory.
// This function may be overridden by projects using
// an alternative build system (such as Blaze) that
// does not run a test in its package directory.
var TestData = func() string {
testdata, err := filepath.Abs("testdata")
if err != nil {
log.Fatal(err)
}
return testdata
}
// Testing is an abstraction of a *testing.T.
type Testing interface {
Errorf(format string, args ...interface{})
}
// Run applies an analysis to the packages denoted by the "go list" patterns.
//
// It loads the packages from the specified GOPATH-style project
// directory using golang.org/x/tools/go/packages, runs the analysis on
// them, and checks that each analysis emits the expected diagnostics
// and facts specified by the contents of '// want ...' comments in the
// package's source files.
//
// An expectation of a Diagnostic is specified by a string literal
// containing a regular expression that must match the diagnostic
// message. For example:
//
// fmt.Printf("%s", 1) // want `cannot provide int 1 to %s`
//
// An expectation of a Fact associated with an object is specified by
// 'name:"pattern"', where name is the name of the object, which must be
// declared on the same line as the comment, and pattern is a regular
// expression that must match the string representation of the fact,
// fmt.Sprint(fact). For example:
//
// func panicf(format string, args interface{}) { // want panicf:"printfWrapper"
//
// Package facts are specified by the name "package" and appear on
// line 1 of the first source file of the package.
//
// A single 'want' comment may contain a mixture of diagnostic and fact
// expectations, including multiple facts about the same object:
//
// // want "diag" "diag2" x:"fact1" x:"fact2" y:"fact3"
//
// Unexpected diagnostics and facts, and unmatched expectations, are
// reported as errors to the Testing.
//
// Run reports an error to the Testing if loading or analysis failed.
// Run also returns a Result for each package for which analysis was
// attempted, even if unsuccessful. It is safe for a test to ignore all
// the results, but a test may use it to perform additional checks.
func Run(t Testing, dir string, a *analysis.Analyzer, patterns ...string) []*Result {
pkgs, err := loadPackages(dir, patterns...)
if err != nil {
t.Errorf("loading %s: %v", patterns, err)
return nil
}
results := checker.TestAnalyzer(a, pkgs)
for _, result := range results {
if result.Err != nil {
t.Errorf("error analyzing %s: %v", result.Pass, result.Err)
} else {
check(t, dir, result.Pass, result.Diagnostics, result.Facts)
}
}
return results
}
// A Result holds the result of applying an analyzer to a package.
type Result = checker.TestAnalyzerResult
// loadPackages uses go/packages to load a specified packages (from source, with
// dependencies) from dir, which is the root of a GOPATH-style project
// tree. It returns an error if any package had an error, or the pattern
// matched no packages.
func loadPackages(dir string, patterns ...string) ([]*packages.Package, error) {
// packages.Load loads the real standard library, not a minimal
// fake version, which would be more efficient, especially if we
// have many small tests that import, say, net/http.
// However there is no easy way to make go/packages to consume
// a list of packages we generate and then do the parsing and
// typechecking, though this feature seems to be a recurring need.
cfg := &packages.Config{
Mode: packages.LoadAllSyntax,
Dir: dir,
Tests: true,
Env: append(os.Environ(), "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off"),
}
pkgs, err := packages.Load(cfg, patterns...)
if err != nil {
return nil, err
}
// Print errors but do not stop:
// some Analyzers may be disposed to RunDespiteErrors.
packages.PrintErrors(pkgs)
if len(pkgs) == 0 {
return nil, fmt.Errorf("no packages matched %s", patterns)
}
return pkgs, nil
}
// check inspects an analysis pass on which the analysis has already
// been run, and verifies that all reported diagnostics and facts match
// specified by the contents of "// want ..." comments in the package's
// source files, which must have been parsed with comments enabled.
func check(t Testing, gopath string, pass *analysis.Pass, diagnostics []analysis.Diagnostic, facts map[types.Object][]analysis.Fact) {
type key struct {
file string
line int
}
want := make(map[key][]expectation)
// processComment parses expectations out of comments.
processComment := func(filename string, linenum int, text string) {
text = strings.TrimSpace(text)
// Any comment starting with "want" is treated
// as an expectation, even without following whitespace.
if rest := strings.TrimPrefix(text, "want"); rest != text {
expects, err := parseExpectations(rest)
if err != nil {
t.Errorf("%s:%d: in 'want' comment: %s", filename, linenum, err)
return
}
if expects != nil {
want[key{filename, linenum}] = expects
}
}
}
// Extract 'want' comments from Go files.
for _, f := range pass.Files {
for _, cgroup := range f.Comments {
for _, c := range cgroup.List {
text := strings.TrimPrefix(c.Text, "//")
if text == c.Text {
continue // not a //-comment
}
// Hack: treat a comment of the form "//...// want..."
// as if it starts at 'want'.
// This allows us to add comments on comments,
// as required when testing the buildtag analyzer.
if i := strings.Index(text, "// want"); i >= 0 {
text = text[i+len("// "):]
}
// It's tempting to compute the filename
// once outside the loop, but it's
// incorrect because it can change due
// to //line directives.
posn := pass.Fset.Position(c.Pos())
filename := sanitize(gopath, posn.Filename)
processComment(filename, posn.Line, text)
}
}
}
// Extract 'want' comments from non-Go files.
// TODO(adonovan): we may need to handle //line directives.
for _, filename := range pass.OtherFiles {
data, err := ioutil.ReadFile(filename)
if err != nil {
t.Errorf("can't read '// want' comments from %s: %v", filename, err)
continue
}
filename := sanitize(gopath, filename)
linenum := 0
for _, line := range strings.Split(string(data), "\n") {
linenum++
if i := strings.Index(line, "//"); i >= 0 {
line = line[i+len("//"):]
processComment(filename, linenum, line)
}
}
}
checkMessage := func(posn token.Position, kind, name, message string) {
posn.Filename = sanitize(gopath, posn.Filename)
k := key{posn.Filename, posn.Line}
expects := want[k]
var unmatched []string
for i, exp := range expects {
if exp.kind == kind && exp.name == name {
if exp.rx.MatchString(message) {
// matched: remove the expectation.
expects[i] = expects[len(expects)-1]
expects = expects[:len(expects)-1]
want[k] = expects
return
}
unmatched = append(unmatched, fmt.Sprintf("%q", exp.rx))
}
}
if unmatched == nil {
t.Errorf("%v: unexpected %s: %v", posn, kind, message)
} else {
t.Errorf("%v: %s %q does not match pattern %s",
posn, kind, message, strings.Join(unmatched, " or "))
}
}
// Check the diagnostics match expectations.
for _, f := range diagnostics {
posn := pass.Fset.Position(f.Pos)
checkMessage(posn, "diagnostic", "", f.Message)
}
// Check the facts match expectations.
// Report errors in lexical order for determinism.
// (It's only deterministic within each file, not across files,
// because go/packages does not guarantee file.Pos is ascending
// across the files of a single compilation unit.)
var objects []types.Object
for obj := range facts {
objects = append(objects, obj)
}
sort.Slice(objects, func(i, j int) bool {
return objects[i].Pos() < objects[j].Pos()
})
for _, obj := range objects {
var posn token.Position
var name string
if obj != nil {
// Object facts are reported on the declaring line.
name = obj.Name()
posn = pass.Fset.Position(obj.Pos())
} else {
// Package facts are reported at the start of the file.
name = "package"
posn = pass.Fset.Position(pass.Files[0].Pos())
posn.Line = 1
}
for _, fact := range facts[obj] {
checkMessage(posn, "fact", name, fmt.Sprint(fact))
}
}
// Reject surplus expectations.
//
// Sometimes an Analyzer reports two similar diagnostics on a
// line with only one expectation. The reader may be confused by
// the error message.
// TODO(adonovan): print a better error:
// "got 2 diagnostics here; each one needs its own expectation".
var surplus []string
for key, expects := range want {
for _, exp := range expects {
err := fmt.Sprintf("%s:%d: no %s was reported matching %q", key.file, key.line, exp.kind, exp.rx)
surplus = append(surplus, err)
}
}
sort.Strings(surplus)
for _, err := range surplus {
t.Errorf("%s", err)
}
}
type expectation struct {
kind string // either "fact" or "diagnostic"
name string // name of object to which fact belongs, or "package" ("fact" only)
rx *regexp.Regexp
}
func (ex expectation) String() string {
return fmt.Sprintf("%s %s:%q", ex.kind, ex.name, ex.rx) // for debugging
}
// parseExpectations parses the content of a "// want ..." comment
// and returns the expections, a mixture of diagnostics ("rx") and
// facts (name:"rx").
func parseExpectations(text string) ([]expectation, error) {
var scanErr string
sc := new(scanner.Scanner).Init(strings.NewReader(text))
sc.Error = func(s *scanner.Scanner, msg string) {
scanErr = msg // e.g. bad string escape
}
sc.Mode = scanner.ScanIdents | scanner.ScanStrings | scanner.ScanRawStrings
scanRegexp := func(tok rune) (*regexp.Regexp, error) {
if tok != scanner.String && tok != scanner.RawString {
return nil, fmt.Errorf("got %s, want regular expression",
scanner.TokenString(tok))
}
pattern, _ := strconv.Unquote(sc.TokenText()) // can't fail
return regexp.Compile(pattern)
}
var expects []expectation
for {
tok := sc.Scan()
switch tok {
case scanner.String, scanner.RawString:
rx, err := scanRegexp(tok)
if err != nil {
return nil, err
}
expects = append(expects, expectation{"diagnostic", "", rx})
case scanner.Ident:
name := sc.TokenText()
tok = sc.Scan()
if tok != ':' {
return nil, fmt.Errorf("got %s after %s, want ':'",
scanner.TokenString(tok), name)
}
tok = sc.Scan()
rx, err := scanRegexp(tok)
if err != nil {
return nil, err
}
expects = append(expects, expectation{"fact", name, rx})
case scanner.EOF:
if scanErr != "" {
return nil, fmt.Errorf("%s", scanErr)
}
return expects, nil
default:
return nil, fmt.Errorf("unexpected %s", scanner.TokenString(tok))
}
}
}
// sanitize removes the GOPATH portion of the filename,
// typically a gnarly /tmp directory, and returns the rest.
func sanitize(gopath, filename string) string {
prefix := gopath + string(os.PathSeparator) + "src" + string(os.PathSeparator)
return filepath.ToSlash(strings.TrimPrefix(filename, prefix))
}

View File

@@ -0,0 +1,93 @@
package analysistest_test
import (
"fmt"
"log"
"os"
"reflect"
"strings"
"testing"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/findcall"
)
func init() {
// This test currently requires GOPATH mode.
// Explicitly disabling module mode should suffix, but
// we'll also turn off GOPROXY just for good measure.
if err := os.Setenv("GO111MODULE", "off"); err != nil {
log.Fatal(err)
}
if err := os.Setenv("GOPROXY", "off"); err != nil {
log.Fatal(err)
}
}
// TestTheTest tests the analysistest testing infrastructure.
func TestTheTest(t *testing.T) {
// We'll simulate a partly failing test of the findcall analysis,
// which (by default) reports calls to functions named 'println'.
findcall.Analyzer.Flags.Set("name", "println")
filemap := map[string]string{"a/b.go": `package main
func main() {
// The expectation is ill-formed:
print() // want: "diagnostic"
print() // want foo"fact"
print() // want foo:
print() // want "\xZZ scan error"
// A dignostic is reported at this line, but the expectation doesn't match:
println("hello, world") // want "wrong expectation text"
// An unexpected diagnostic is reported at this line:
println() // trigger an unexpected diagnostic
// No diagnostic is reported at this line:
print() // want "unsatisfied expectation"
// OK
println("hello, world") // want "call of println"
// OK (multiple expectations on same line)
println(); println() // want "call of println(...)" "call of println(...)"
}
// OK (facts and diagnostics on same line)
func println(...interface{}) { println() } // want println:"found" "call of println(...)"
`}
dir, cleanup, err := analysistest.WriteFiles(filemap)
if err != nil {
t.Fatal(err)
}
defer cleanup()
var got []string
t2 := errorfunc(func(s string) { got = append(got, s) }) // a fake *testing.T
analysistest.Run(t2, dir, findcall.Analyzer, "a")
want := []string{
`a/b.go:5: in 'want' comment: unexpected ":"`,
`a/b.go:6: in 'want' comment: got String after foo, want ':'`,
`a/b.go:7: in 'want' comment: got EOF, want regular expression`,
`a/b.go:8: in 'want' comment: illegal char escape`,
`a/b.go:11:9: diagnostic "call of println(...)" does not match pattern "wrong expectation text"`,
`a/b.go:14:9: unexpected diagnostic: call of println(...)`,
`a/b.go:11: no diagnostic was reported matching "wrong expectation text"`,
`a/b.go:17: no diagnostic was reported matching "unsatisfied expectation"`,
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got:\n%s\nwant:\n%s",
strings.Join(got, "\n"),
strings.Join(want, "\n"))
}
}
type errorfunc func(string)
func (f errorfunc) Errorf(format string, args ...interface{}) {
f(fmt.Sprintf(format, args...))
}

View File

@@ -0,0 +1,74 @@
// The vet-lite command is a driver for static checkers conforming to
// the golang.org/x/tools/go/analysis API. It must be run by go vet:
//
// $ go vet -vettool=$(which vet-lite)
//
// For a checker also capable of running standalone, use multichecker.
package main
import (
"golang.org/x/tools/go/analysis/unitchecker"
"golang.org/x/tools/go/analysis/passes/asmdecl"
"golang.org/x/tools/go/analysis/passes/assign"
"golang.org/x/tools/go/analysis/passes/atomic"
"golang.org/x/tools/go/analysis/passes/bools"
"golang.org/x/tools/go/analysis/passes/buildtag"
"golang.org/x/tools/go/analysis/passes/cgocall"
"golang.org/x/tools/go/analysis/passes/composite"
"golang.org/x/tools/go/analysis/passes/copylock"
"golang.org/x/tools/go/analysis/passes/httpresponse"
"golang.org/x/tools/go/analysis/passes/loopclosure"
"golang.org/x/tools/go/analysis/passes/lostcancel"
"golang.org/x/tools/go/analysis/passes/nilfunc"
"golang.org/x/tools/go/analysis/passes/printf"
"golang.org/x/tools/go/analysis/passes/shift"
"golang.org/x/tools/go/analysis/passes/stdmethods"
"golang.org/x/tools/go/analysis/passes/structtag"
"golang.org/x/tools/go/analysis/passes/tests"
"golang.org/x/tools/go/analysis/passes/unmarshal"
"golang.org/x/tools/go/analysis/passes/unreachable"
"golang.org/x/tools/go/analysis/passes/unsafeptr"
"golang.org/x/tools/go/analysis/passes/unusedresult"
)
// Legacy vet had the concept of "experimental" checkers. There
// was exactly one, shadow, and it had to be explicitly enabled
// by the -shadow flag, which would of course disable all the
// other tristate flags, requiring the -all flag to reenable them.
// (By itself, -all did not enable all checkers.)
// The -all flag is no longer needed, so it is a no-op.
//
// The shadow analyzer has been removed from the suite,
// but can be run using these additional commands:
// $ go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
// $ go vet -vettool=$(which shadow)
// Alternatively, one could build a multichecker containing all
// the desired checks (vet's suite + shadow) and run it in a
// single "go vet" command.
func main() {
unitchecker.Main(
asmdecl.Analyzer,
assign.Analyzer,
atomic.Analyzer,
bools.Analyzer,
buildtag.Analyzer,
cgocall.Analyzer,
composite.Analyzer,
copylock.Analyzer,
httpresponse.Analyzer,
loopclosure.Analyzer,
lostcancel.Analyzer,
nilfunc.Analyzer,
printf.Analyzer,
shift.Analyzer,
stdmethods.Analyzer,
structtag.Analyzer,
tests.Analyzer,
unmarshal.Analyzer,
unreachable.Analyzer,
unsafeptr.Analyzer,
unusedresult.Analyzer,
)
}

33
vendor/golang.org/x/tools/go/analysis/cmd/vet/README generated vendored Normal file
View File

@@ -0,0 +1,33 @@
Vet is a tool that checks correctness of Go programs. It runs a suite of tests,
each tailored to check for a particular class of errors. Examples include incorrect
Printf format verbs and malformed build tags.
Over time many checks have been added to vet's suite, but many more have been
rejected as not appropriate for the tool. The criteria applied when selecting which
checks to add are:
Correctness:
Vet's checks are about correctness, not style. A vet check must identify real or
potential bugs that could cause incorrect compilation or execution. A check that
only identifies stylistic points or alternative correct approaches to a situation
is not acceptable.
Frequency:
Vet is run every day by many programmers, often as part of every compilation or
submission. The cost in execution time is considerable, especially in aggregate,
so checks must be likely enough to find real problems that they are worth the
overhead of the added check. A new check that finds only a handful of problems
across all existing programs, even if the problem is significant, is not worth
adding to the suite everyone runs daily.
Precision:
Most of vet's checks are heuristic and can generate both false positives (flagging
correct programs) and false negatives (not flagging incorrect ones). The rate of
both these failures must be very small. A check that is too noisy will be ignored
by the programmer overwhelmed by the output; a check that misses too many of the
cases it's looking for will give a false sense of security. Neither is acceptable.
A vet check must be accurate enough that everything it reports is worth examining,
and complete enough to encourage real confidence.

80
vendor/golang.org/x/tools/go/analysis/cmd/vet/vet.go generated vendored Normal file
View File

@@ -0,0 +1,80 @@
// Copyright 2018 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.
// The vet command is a static checker for Go programs. It has pluggable
// analyzers defined using the golang.org/x/tools/go/analysis API, and
// using the golang.org/x/tools/go/packages API to load packages in any
// build system.
//
// Each analyzer flag name is preceded by the analyzer name: -NAME.flag.
// In addition, the -NAME flag itself controls whether the
// diagnostics of that analyzer are displayed. (A disabled analyzer may yet
// be run if it is required by some other analyzer that is enabled.)
package main
import (
"golang.org/x/tools/go/analysis/multichecker"
// analysis plug-ins
"golang.org/x/tools/go/analysis/passes/asmdecl"
"golang.org/x/tools/go/analysis/passes/assign"
"golang.org/x/tools/go/analysis/passes/atomic"
"golang.org/x/tools/go/analysis/passes/bools"
"golang.org/x/tools/go/analysis/passes/buildtag"
"golang.org/x/tools/go/analysis/passes/cgocall"
"golang.org/x/tools/go/analysis/passes/composite"
"golang.org/x/tools/go/analysis/passes/copylock"
"golang.org/x/tools/go/analysis/passes/httpresponse"
"golang.org/x/tools/go/analysis/passes/loopclosure"
"golang.org/x/tools/go/analysis/passes/lostcancel"
"golang.org/x/tools/go/analysis/passes/nilfunc"
"golang.org/x/tools/go/analysis/passes/nilness"
"golang.org/x/tools/go/analysis/passes/printf"
"golang.org/x/tools/go/analysis/passes/shift"
"golang.org/x/tools/go/analysis/passes/stdmethods"
"golang.org/x/tools/go/analysis/passes/structtag"
"golang.org/x/tools/go/analysis/passes/tests"
"golang.org/x/tools/go/analysis/passes/unmarshal"
"golang.org/x/tools/go/analysis/passes/unreachable"
"golang.org/x/tools/go/analysis/passes/unsafeptr"
"golang.org/x/tools/go/analysis/passes/unusedresult"
)
func main() {
// This suite of analyzers is applied to all code
// in GOROOT by GOROOT/src/cmd/vet/all. When adding
// a new analyzer, update the whitelist used by vet/all,
// or change its vet command to disable the new analyzer.
multichecker.Main(
// the traditional vet suite:
asmdecl.Analyzer,
assign.Analyzer,
atomic.Analyzer,
bools.Analyzer,
buildtag.Analyzer,
cgocall.Analyzer,
composite.Analyzer,
copylock.Analyzer,
httpresponse.Analyzer,
loopclosure.Analyzer,
lostcancel.Analyzer,
nilfunc.Analyzer,
printf.Analyzer,
shift.Analyzer,
stdmethods.Analyzer,
structtag.Analyzer,
tests.Analyzer,
unmarshal.Analyzer,
unreachable.Analyzer,
unsafeptr.Analyzer,
unusedresult.Analyzer,
// for debugging:
// findcall.Analyzer,
// pkgfact.Analyzer,
// uses SSA:
nilness.Analyzer,
)
}

340
vendor/golang.org/x/tools/go/analysis/doc.go generated vendored Normal file
View File

@@ -0,0 +1,340 @@
/*
The analysis package defines the interface between a modular static
analysis and an analysis driver program.
THIS INTERFACE IS EXPERIMENTAL AND SUBJECT TO CHANGE.
We aim to finalize it by November 2018.
Background
A static analysis is a function that inspects a package of Go code and
reports a set of diagnostics (typically mistakes in the code), and
perhaps produces other results as well, such as suggested refactorings
or other facts. An analysis that reports mistakes is informally called a
"checker". For example, the printf checker reports mistakes in
fmt.Printf format strings.
A "modular" analysis is one that inspects one package at a time but can
save information from a lower-level package and use it when inspecting a
higher-level package, analogous to separate compilation in a toolchain.
The printf checker is modular: when it discovers that a function such as
log.Fatalf delegates to fmt.Printf, it records this fact, and checks
calls to that function too, including calls made from another package.
By implementing a common interface, checkers from a variety of sources
can be easily selected, incorporated, and reused in a wide range of
driver programs including command-line tools (such as vet), text editors and
IDEs, build and test systems (such as go build, Bazel, or Buck), test
frameworks, code review tools, code-base indexers (such as SourceGraph),
documentation viewers (such as godoc), batch pipelines for large code
bases, and so on.
Analyzer
The primary type in the API is Analyzer. An Analyzer statically
describes an analysis function: its name, documentation, flags,
relationship to other analyzers, and of course, its logic.
To define an analysis, a user declares a (logically constant) variable
of type Analyzer. Here is a typical example from one of the analyzers in
the go/analysis/passes/ subdirectory:
package unusedresult
var Analyzer = &analysis.Analyzer{
Name: "unusedresult",
Doc: "check for unused results of calls to some functions",
Run: run,
...
}
func run(pass *analysis.Pass) (interface{}, error) {
...
}
An analysis driver is a program such as vet that runs a set of
analyses and prints the diagnostics that they report.
The driver program must import the list of Analyzers it needs.
Typically each Analyzer resides in a separate package.
To add a new Analyzer to an existing driver, add another item to the list:
import ( "unusedresult"; "nilness"; "printf" )
var analyses = []*analysis.Analyzer{
unusedresult.Analyzer,
nilness.Analyzer,
printf.Analyzer,
}
A driver may use the name, flags, and documentation to provide on-line
help that describes the analyses its performs.
The doc comment contains a brief one-line summary,
optionally followed by paragraphs of explanation.
The vet command, shown below, is an example of a driver that runs
multiple analyzers. It is based on the multichecker package
(see the "Standalone commands" section for details).
$ go build golang.org/x/tools/go/analysis/cmd/vet
$ ./vet help
vet is a tool for static analysis of Go programs.
Usage: vet [-flag] [package]
Registered analyzers:
asmdecl report mismatches between assembly files and Go declarations
assign check for useless assignments
atomic check for common mistakes using the sync/atomic package
...
unusedresult check for unused results of calls to some functions
$ ./vet help unusedresult
unusedresult: check for unused results of calls to some functions
Analyzer flags:
-unusedresult.funcs value
comma-separated list of functions whose results must be used (default Error,String)
-unusedresult.stringmethods value
comma-separated list of names of methods of type func() string whose results must be used
Some functions like fmt.Errorf return a result and have no side effects,
so it is always a mistake to discard the result. This analyzer reports
calls to certain functions in which the result of the call is ignored.
The set of functions may be controlled using flags.
The Analyzer type has more fields besides those shown above:
type Analyzer struct {
Name string
Doc string
Flags flag.FlagSet
Run func(*Pass) (interface{}, error)
RunDespiteErrors bool
ResultType reflect.Type
Requires []*Analyzer
FactTypes []Fact
}
The Flags field declares a set of named (global) flag variables that
control analysis behavior. Unlike vet, analysis flags are not declared
directly in the command line FlagSet; it is up to the driver to set the
flag variables. A driver for a single analysis, a, might expose its flag
f directly on the command line as -f, whereas a driver for multiple
analyses might prefix the flag name by the analysis name (-a.f) to avoid
ambiguity. An IDE might expose the flags through a graphical interface,
and a batch pipeline might configure them from a config file.
See the "findcall" analyzer for an example of flags in action.
The RunDespiteErrors flag indicates whether the analysis is equipped to
handle ill-typed code. If not, the driver will skip the analysis if
there were parse or type errors.
The optional ResultType field specifies the type of the result value
computed by this analysis and made available to other analyses.
The Requires field specifies a list of analyses upon which
this one depends and whose results it may access, and it constrains the
order in which a driver may run analyses.
The FactTypes field is discussed in the section on Modularity.
The analysis package provides a Validate function to perform basic
sanity checks on an Analyzer, such as that its Requires graph is
acyclic, its fact and result types are unique, and so on.
Finally, the Run field contains a function to be called by the driver to
execute the analysis on a single package. The driver passes it an
instance of the Pass type.
Pass
A Pass describes a single unit of work: the application of a particular
Analyzer to a particular package of Go code.
The Pass provides information to the Analyzer's Run function about the
package being analyzed, and provides operations to the Run function for
reporting diagnostics and other information back to the driver.
type Pass struct {
Fset *token.FileSet
Files []*ast.File
OtherFiles []string
Pkg *types.Package
TypesInfo *types.Info
ResultOf map[*Analyzer]interface{}
Report func(Diagnostic)
...
}
The Fset, Files, Pkg, and TypesInfo fields provide the syntax trees,
type information, and source positions for a single package of Go code.
The OtherFiles field provides the names, but not the contents, of non-Go
files such as assembly that are part of this package. See the "asmdecl"
or "buildtags" analyzers for examples of loading non-Go files and report
diagnostics against them.
The ResultOf field provides the results computed by the analyzers
required by this one, as expressed in its Analyzer.Requires field. The
driver runs the required analyzers first and makes their results
available in this map. Each Analyzer must return a value of the type
described in its Analyzer.ResultType field.
For example, the "ctrlflow" analyzer returns a *ctrlflow.CFGs, which
provides a control-flow graph for each function in the package (see
golang.org/x/tools/go/cfg); the "inspect" analyzer returns a value that
enables other Analyzers to traverse the syntax trees of the package more
efficiently; and the "buildssa" analyzer constructs an SSA-form
intermediate representation.
Each of these Analyzers extends the capabilities of later Analyzers
without adding a dependency to the core API, so an analysis tool pays
only for the extensions it needs.
The Report function emits a diagnostic, a message associated with a
source position. For most analyses, diagnostics are their primary
result.
For convenience, Pass provides a helper method, Reportf, to report a new
diagnostic by formatting a string.
Diagnostic is defined as:
type Diagnostic struct {
Pos token.Pos
Category string // optional
Message string
}
The optional Category field is a short identifier that classifies the
kind of message when an analysis produces several kinds of diagnostic.
Most Analyzers inspect typed Go syntax trees, but a few, such as asmdecl
and buildtag, inspect the raw text of Go source files or even non-Go
files such as assembly. To report a diagnostic against a line of a
raw text file, use the following sequence:
content, err := ioutil.ReadFile(filename)
if err != nil { ... }
tf := fset.AddFile(filename, -1, len(content))
tf.SetLinesForContent(content)
...
pass.Reportf(tf.LineStart(line), "oops")
Modular analysis with Facts
To improve efficiency and scalability, large programs are routinely
built using separate compilation: units of the program are compiled
separately, and recompiled only when one of their dependencies changes;
independent modules may be compiled in parallel. The same technique may
be applied to static analyses, for the same benefits. Such analyses are
described as "modular".
A compilers type checker is an example of a modular static analysis.
Many other checkers we would like to apply to Go programs can be
understood as alternative or non-standard type systems. For example,
vet's printf checker infers whether a function has the "printf wrapper"
type, and it applies stricter checks to calls of such functions. In
addition, it records which functions are printf wrappers for use by
later analysis units to identify other printf wrappers by induction.
A result such as “f is a printf wrapper” that is not interesting by
itself but serves as a stepping stone to an interesting result (such as
a diagnostic) is called a "fact".
The analysis API allows an analysis to define new types of facts, to
associate facts of these types with objects (named entities) declared
within the current package, or with the package as a whole, and to query
for an existing fact of a given type associated with an object or
package.
An Analyzer that uses facts must declare their types:
var Analyzer = &analysis.Analyzer{
Name: "printf",
FactTypes: []reflect.Type{reflect.TypeOf(new(isWrapper))},
...
}
type isWrapper struct{} // => *types.Func f “is a printf wrapper”
A driver program ensures that facts for a passs dependencies are
generated before analyzing the pass and are responsible for propagating
facts between from one pass to another, possibly across address spaces.
Consequently, Facts must be serializable. The API requires that drivers
use the gob encoding, an efficient, robust, self-describing binary
protocol. A fact type may implement the GobEncoder/GobDecoder interfaces
if the default encoding is unsuitable. Facts should be stateless.
The Pass type has functions to import and export facts,
associated either with an object or with a package:
type Pass struct {
...
ExportObjectFact func(types.Object, Fact)
ImportObjectFact func(types.Object, Fact) bool
ExportPackageFact func(fact Fact)
ImportPackageFact func(*types.Package, Fact) bool
}
An Analyzer may only export facts associated with the current package or
its objects, though it may import facts from any package or object that
is an import dependency of the current package.
Conceptually, ExportObjectFact(obj, fact) inserts fact into a hidden map keyed by
the pair (obj, TypeOf(fact)), and the ImportObjectFact function
retrieves the entry from this map and copies its value into the variable
pointed to by fact. This scheme assumes that the concrete type of fact
is a pointer; this assumption is checked by the Validate function.
See the "printf" analyzer for an example of object facts in action.
Some driver implementations (such as those based on Bazel and Blaze) do
not currently apply analyzers to packages of the standard library.
Therefore, for best results, analyzer authors should not rely on
analysis facts being available for standard packages.
For example, although the printf checker is capable of deducing during
analysis of the log package that log.Printf is a printf-wrapper,
this fact is built in to the analyzer so that it correctly checks
calls to log.Printf even when run in a driver that does not apply
it to standard packages. We plan to remove this limitation in future.
Testing an Analyzer
The analysistest subpackage provides utilities for testing an Analyzer.
In a few lines of code, it is possible to run an analyzer on a package
of testdata files and check that it reported all the expected
diagnostics and facts (and no more). Expectations are expressed using
"// want ..." comments in the input code.
Standalone commands
Analyzers are provided in the form of packages that a driver program is
expected to import. The vet command imports a set of several analyzers,
but users may wish to define their own analysis commands that perform
additional checks. To simplify the task of creating an analysis command,
either for a single analyzer or for a whole suite, we provide the
singlechecker and multichecker subpackages.
The singlechecker package provides the main function for a command that
runs one analyzer. By convention, each analyzer such as
go/passes/findcall should be accompanied by a singlechecker-based
command such as go/analysis/passes/findcall/cmd/findcall, defined in its
entirety as:
package main
import (
"golang.org/x/tools/go/analysis/passes/findcall"
"golang.org/x/tools/go/analysis/singlechecker"
)
func main() { singlechecker.Main(findcall.Analyzer) }
A tool that provides multiple analyzers can use multichecker in a
similar way, giving it the list of Analyzers.
*/
package analysis

View File

@@ -0,0 +1,343 @@
// Copyright 2018 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 analysisflags defines helpers for processing flags of
// analysis driver tools.
package analysisflags
import (
"crypto/sha256"
"encoding/json"
"flag"
"fmt"
"go/token"
"io"
"io/ioutil"
"log"
"os"
"strconv"
"strings"
"golang.org/x/tools/go/analysis"
)
// flags common to all {single,multi,unit}checkers.
var (
JSON = false // -json
Context = -1 // -c=N: if N>0, display offending line plus N lines of context
)
// Parse creates a flag for each of the analyzer's flags,
// including (in multi mode) a flag named after the analyzer,
// parses the flags, then filters and returns the list of
// analyzers enabled by flags.
func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer {
// Connect each analysis flag to the command line as -analysis.flag.
enabled := make(map[*analysis.Analyzer]*triState)
for _, a := range analyzers {
var prefix string
// Add -NAME flag to enable it.
if multi {
prefix = a.Name + "."
enable := new(triState)
enableUsage := "enable " + a.Name + " analysis"
flag.Var(enable, a.Name, enableUsage)
enabled[a] = enable
}
a.Flags.VisitAll(func(f *flag.Flag) {
if !multi && flag.Lookup(f.Name) != nil {
log.Printf("%s flag -%s would conflict with driver; skipping", a.Name, f.Name)
return
}
name := prefix + f.Name
flag.Var(f.Value, name, f.Usage)
})
}
// standard flags: -flags, -V.
printflags := flag.Bool("flags", false, "print analyzer flags in JSON")
addVersionFlag()
// flags common to all checkers
flag.BoolVar(&JSON, "json", JSON, "emit JSON output")
flag.IntVar(&Context, "c", Context, `display offending line with this many lines of context`)
// Add shims for legacy vet flags to enable existing
// scripts that run vet to continue to work.
_ = flag.Bool("source", false, "no effect (deprecated)")
_ = flag.Bool("v", false, "no effect (deprecated)")
_ = flag.Bool("all", false, "no effect (deprecated)")
_ = flag.String("tags", "", "no effect (deprecated)")
for old, new := range vetLegacyFlags {
newFlag := flag.Lookup(new)
if newFlag != nil && flag.Lookup(old) == nil {
flag.Var(newFlag.Value, old, "deprecated alias for -"+new)
}
}
flag.Parse() // (ExitOnError)
// -flags: print flags so that go vet knows which ones are legitimate.
if *printflags {
printFlags()
os.Exit(0)
}
// If any -NAME flag is true, run only those analyzers. Otherwise,
// if any -NAME flag is false, run all but those analyzers.
if multi {
var hasTrue, hasFalse bool
for _, ts := range enabled {
switch *ts {
case setTrue:
hasTrue = true
case setFalse:
hasFalse = true
}
}
var keep []*analysis.Analyzer
if hasTrue {
for _, a := range analyzers {
if *enabled[a] == setTrue {
keep = append(keep, a)
}
}
analyzers = keep
} else if hasFalse {
for _, a := range analyzers {
if *enabled[a] != setFalse {
keep = append(keep, a)
}
}
analyzers = keep
}
}
return analyzers
}
func printFlags() {
type jsonFlag struct {
Name string
Bool bool
Usage string
}
var flags []jsonFlag = nil
flag.VisitAll(func(f *flag.Flag) {
// Don't report {single,multi}checker debugging
// flags as these have no effect on unitchecker
// (as invoked by 'go vet').
switch f.Name {
case "debug", "cpuprofile", "memprofile", "trace":
return
}
b, ok := f.Value.(interface{ IsBoolFlag() bool })
isBool := ok && b.IsBoolFlag()
flags = append(flags, jsonFlag{f.Name, isBool, f.Usage})
})
data, err := json.MarshalIndent(flags, "", "\t")
if err != nil {
log.Fatal(err)
}
os.Stdout.Write(data)
}
// addVersionFlag registers a -V flag that, if set,
// prints the executable version and exits 0.
//
// It is a variable not a function to permit easy
// overriding in the copy vendored in $GOROOT/src/cmd/vet:
//
// func init() { addVersionFlag = objabi.AddVersionFlag }
var addVersionFlag = func() {
flag.Var(versionFlag{}, "V", "print version and exit")
}
// versionFlag minimally complies with the -V protocol required by "go vet".
type versionFlag struct{}
func (versionFlag) IsBoolFlag() bool { return true }
func (versionFlag) Get() interface{} { return nil }
func (versionFlag) String() string { return "" }
func (versionFlag) Set(s string) error {
if s != "full" {
log.Fatalf("unsupported flag value: -V=%s", s)
}
// This replicates the miminal subset of
// cmd/internal/objabi.AddVersionFlag, which is private to the
// go tool yet forms part of our command-line interface.
// TODO(adonovan): clarify the contract.
// Print the tool version so the build system can track changes.
// Formats:
// $progname version devel ... buildID=...
// $progname version go1.9.1
progname := os.Args[0]
f, err := os.Open(progname)
if err != nil {
log.Fatal(err)
}
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
log.Fatal(err)
}
f.Close()
fmt.Printf("%s version devel comments-go-here buildID=%02x\n",
progname, string(h.Sum(nil)))
os.Exit(0)
return nil
}
// A triState is a boolean that knows whether
// it has been set to either true or false.
// It is used to identify whether a flag appears;
// the standard boolean flag cannot
// distinguish missing from unset.
// It also satisfies flag.Value.
type triState int
const (
unset triState = iota
setTrue
setFalse
)
func triStateFlag(name string, value triState, usage string) *triState {
flag.Var(&value, name, usage)
return &value
}
// triState implements flag.Value, flag.Getter, and flag.boolFlag.
// They work like boolean flags: we can say vet -printf as well as vet -printf=true
func (ts *triState) Get() interface{} {
return *ts == setTrue
}
func (ts triState) isTrue() bool {
return ts == setTrue
}
func (ts *triState) Set(value string) error {
b, err := strconv.ParseBool(value)
if err != nil {
// This error message looks poor but package "flag" adds
// "invalid boolean value %q for -NAME: %s"
return fmt.Errorf("want true or false")
}
if b {
*ts = setTrue
} else {
*ts = setFalse
}
return nil
}
func (ts *triState) String() string {
switch *ts {
case unset:
return "true"
case setTrue:
return "true"
case setFalse:
return "false"
}
panic("not reached")
}
func (ts triState) IsBoolFlag() bool {
return true
}
// Legacy flag support
// vetLegacyFlags maps flags used by legacy vet to their corresponding
// new names. The old names will continue to work.
var vetLegacyFlags = map[string]string{
// Analyzer name changes
"bool": "bools",
"buildtags": "buildtag",
"methods": "stdmethods",
"rangeloops": "loopclosure",
// Analyzer flags
"compositewhitelist": "composites.whitelist",
"printfuncs": "printf.funcs",
"shadowstrict": "shadow.strict",
"unusedfuncs": "unusedresult.funcs",
"unusedstringmethods": "unusedresult.stringmethods",
}
// ---- output helpers common to all drivers ----
// PrintPlain prints a diagnostic in plain text form,
// with context specified by the -c flag.
func PrintPlain(fset *token.FileSet, diag analysis.Diagnostic) {
posn := fset.Position(diag.Pos)
fmt.Fprintf(os.Stderr, "%s: %s\n", posn, diag.Message)
// -c=N: show offending line plus N lines of context.
if Context >= 0 {
data, _ := ioutil.ReadFile(posn.Filename)
lines := strings.Split(string(data), "\n")
for i := posn.Line - Context; i <= posn.Line+Context; i++ {
if 1 <= i && i <= len(lines) {
fmt.Fprintf(os.Stderr, "%d\t%s\n", i, lines[i-1])
}
}
}
}
// A JSONTree is a mapping from package ID to analysis name to result.
// Each result is either a jsonError or a list of jsonDiagnostic.
type JSONTree map[string]map[string]interface{}
// Add adds the result of analysis 'name' on package 'id'.
// The result is either a list of diagnostics or an error.
func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis.Diagnostic, err error) {
var v interface{}
if err != nil {
type jsonError struct {
Err string `json:"error"`
}
v = jsonError{err.Error()}
} else if len(diags) > 0 {
type jsonDiagnostic struct {
Category string `json:"category,omitempty"`
Posn string `json:"posn"`
Message string `json:"message"`
}
var diagnostics []jsonDiagnostic
for _, f := range diags {
diagnostics = append(diagnostics, jsonDiagnostic{
Category: f.Category,
Posn: fset.Position(f.Pos).String(),
Message: f.Message,
})
}
v = diagnostics
}
if v != nil {
m, ok := tree[id]
if !ok {
m = make(map[string]interface{})
tree[id] = m
}
m[name] = v
}
}
func (tree JSONTree) Print() {
data, err := json.MarshalIndent(tree, "", "\t")
if err != nil {
log.Panicf("internal error: JSON marshalling failed: %v", err)
}
fmt.Printf("%s\n", data)
}

View File

@@ -0,0 +1,67 @@
// Copyright 2018 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 analysisflags_test
import (
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"testing"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/internal/analysisflags"
)
func main() {
fmt.Println(analysisflags.Parse([]*analysis.Analyzer{
{Name: "a1", Doc: "a1"},
{Name: "a2", Doc: "a2"},
{Name: "a3", Doc: "a3"},
}, true))
os.Exit(0)
}
// This test fork/execs the main function above.
func TestExec(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skipf("skipping fork/exec test on this platform")
}
progname := os.Args[0]
if os.Getenv("ANALYSISFLAGS_CHILD") == "1" {
// child process
os.Args = strings.Fields(progname + " " + os.Getenv("FLAGS"))
main()
panic("unreachable")
}
for _, test := range []struct {
flags string
want string
}{
{"", "[a1 a2 a3]"},
{"-a1=0", "[a2 a3]"},
{"-a1=1", "[a1]"},
{"-a1", "[a1]"},
{"-a1=1 -a3=1", "[a1 a3]"},
{"-a1=1 -a3=0", "[a1]"},
} {
cmd := exec.Command(progname, "-test.run=TestExec")
cmd.Env = append(os.Environ(), "ANALYSISFLAGS_CHILD=1", "FLAGS="+test.flags)
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("exec failed: %v; output=<<%s>>", err, output)
}
got := strings.TrimSpace(string(output))
if got != test.want {
t.Errorf("got %s, want %s", got, test.want)
}
}
}

View File

@@ -0,0 +1,89 @@
package analysisflags
import (
"flag"
"fmt"
"log"
"sort"
"strings"
"golang.org/x/tools/go/analysis"
)
const help = `PROGNAME is a tool for static analysis of Go programs.
PROGNAME examines Go source code and reports suspicious constructs,
such as Printf calls whose arguments do not align with the format
string. It uses heuristics that do not guarantee all reports are
genuine problems, but it can find errors not caught by the compilers.
`
// Help implements the help subcommand for a multichecker or vet-lite
// style command. The optional args specify the analyzers to describe.
// Help calls log.Fatal if no such analyzer exists.
func Help(progname string, analyzers []*analysis.Analyzer, args []string) {
// No args: show summary of all analyzers.
if len(args) == 0 {
fmt.Println(strings.Replace(help, "PROGNAME", progname, -1))
fmt.Println("Registered analyzers:")
fmt.Println()
sort.Slice(analyzers, func(i, j int) bool {
return analyzers[i].Name < analyzers[j].Name
})
for _, a := range analyzers {
title := strings.Split(a.Doc, "\n\n")[0]
fmt.Printf(" %-12s %s\n", a.Name, title)
}
fmt.Println("\nBy default all analyzers are run.")
fmt.Println("To select specific analyzers, use the -NAME flag for each one,")
fmt.Println(" or -NAME=false to run all analyzers not explicitly disabled.")
// Show only the core command-line flags.
fmt.Println("\nCore flags:")
fmt.Println()
fs := flag.NewFlagSet("", flag.ExitOnError)
flag.VisitAll(func(f *flag.Flag) {
if !strings.Contains(f.Name, ".") {
fs.Var(f.Value, f.Name, f.Usage)
}
})
fs.PrintDefaults()
fmt.Printf("\nTo see details and flags of a specific analyzer, run '%s help name'.\n", progname)
return
}
// Show help on specific analyzer(s).
outer:
for _, arg := range args {
for _, a := range analyzers {
if a.Name == arg {
paras := strings.Split(a.Doc, "\n\n")
title := paras[0]
fmt.Printf("%s: %s\n", a.Name, title)
// Show only the flags relating to this analysis,
// properly prefixed.
first := true
fs := flag.NewFlagSet(a.Name, flag.ExitOnError)
a.Flags.VisitAll(func(f *flag.Flag) {
if first {
first = false
fmt.Println("\nAnalyzer flags:")
fmt.Println()
}
fs.Var(f.Value, a.Name+"."+f.Name, f.Usage)
})
fs.PrintDefaults()
if len(paras) > 1 {
fmt.Printf("\n%s\n", strings.Join(paras[1:], "\n\n"))
}
continue outer
}
}
log.Fatalf("Analyzer %q not registered", arg)
}
}

View File

@@ -0,0 +1,701 @@
// Package checker defines the implementation of the checker commands.
// The same code drives the multi-analysis driver, the single-analysis
// driver that is conventionally provided for convenience along with
// each analysis package, and the test driver.
package checker
import (
"bytes"
"encoding/gob"
"flag"
"fmt"
"go/token"
"go/types"
"log"
"os"
"reflect"
"runtime"
"runtime/pprof"
"runtime/trace"
"sort"
"strings"
"sync"
"time"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/internal/analysisflags"
"golang.org/x/tools/go/packages"
)
var (
// Debug is a set of single-letter flags:
//
// f show [f]acts as they are created
// p disable [p]arallel execution of analyzers
// s do additional [s]anity checks on fact types and serialization
// t show [t]iming info (NB: use 'p' flag to avoid GC/scheduler noise)
// v show [v]erbose logging
//
Debug = ""
// Log files for optional performance tracing.
CPUProfile, MemProfile, Trace string
)
// RegisterFlags registers command-line flags used the analysis driver.
func RegisterFlags() {
// When adding flags here, remember to update
// the list of suppressed flags in analysisflags.
flag.StringVar(&Debug, "debug", Debug, `debug flags, any subset of "lpsv"`)
flag.StringVar(&CPUProfile, "cpuprofile", "", "write CPU profile to this file")
flag.StringVar(&MemProfile, "memprofile", "", "write memory profile to this file")
flag.StringVar(&Trace, "trace", "", "write trace log to this file")
}
// Run loads the packages specified by args using go/packages,
// then applies the specified analyzers to them.
// Analysis flags must already have been set.
// It provides most of the logic for the main functions of both the
// singlechecker and the multi-analysis commands.
// It returns the appropriate exit code.
func Run(args []string, analyzers []*analysis.Analyzer) (exitcode int) {
if CPUProfile != "" {
f, err := os.Create(CPUProfile)
if err != nil {
log.Fatal(err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal(err)
}
// NB: profile won't be written in case of error.
defer pprof.StopCPUProfile()
}
if Trace != "" {
f, err := os.Create(Trace)
if err != nil {
log.Fatal(err)
}
if err := trace.Start(f); err != nil {
log.Fatal(err)
}
// NB: trace log won't be written in case of error.
defer func() {
trace.Stop()
log.Printf("To view the trace, run:\n$ go tool trace view %s", Trace)
}()
}
if MemProfile != "" {
f, err := os.Create(MemProfile)
if err != nil {
log.Fatal(err)
}
// NB: memprofile won't be written in case of error.
defer func() {
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatalf("Writing memory profile: %v", err)
}
f.Close()
}()
}
// Load the packages.
if dbg('v') {
log.SetPrefix("")
log.SetFlags(log.Lmicroseconds) // display timing
log.Printf("load %s", args)
}
// Optimization: if the selected analyzers don't produce/consume
// facts, we need source only for the initial packages.
allSyntax := needFacts(analyzers)
initial, err := load(args, allSyntax)
if err != nil {
log.Print(err)
return 1 // load errors
}
// Print the results.
roots := analyze(initial, analyzers)
return printDiagnostics(roots)
}
// load loads the initial packages.
func load(patterns []string, allSyntax bool) ([]*packages.Package, error) {
mode := packages.LoadSyntax
if allSyntax {
mode = packages.LoadAllSyntax
}
conf := packages.Config{
Mode: mode,
Tests: true,
}
initial, err := packages.Load(&conf, patterns...)
if err == nil {
if n := packages.PrintErrors(initial); n > 1 {
err = fmt.Errorf("%d errors during loading", n)
} else if n == 1 {
err = fmt.Errorf("error during loading")
}
}
if len(initial) == 0 {
err = fmt.Errorf("%s matched no packages", strings.Join(patterns, " "))
}
return initial, err
}
// TestAnalyzer applies an analysis to a set of packages (and their
// dependencies if necessary) and returns the results.
//
// Facts about pkg are returned in a map keyed by object; package facts
// have a nil key.
//
// This entry point is used only by analysistest.
func TestAnalyzer(a *analysis.Analyzer, pkgs []*packages.Package) []*TestAnalyzerResult {
var results []*TestAnalyzerResult
for _, act := range analyze(pkgs, []*analysis.Analyzer{a}) {
facts := make(map[types.Object][]analysis.Fact)
for key, fact := range act.objectFacts {
if key.obj.Pkg() == act.pass.Pkg {
facts[key.obj] = append(facts[key.obj], fact)
}
}
for key, fact := range act.packageFacts {
if key.pkg == act.pass.Pkg {
facts[nil] = append(facts[nil], fact)
}
}
results = append(results, &TestAnalyzerResult{act.pass, act.diagnostics, facts, act.result, act.err})
}
return results
}
type TestAnalyzerResult struct {
Pass *analysis.Pass
Diagnostics []analysis.Diagnostic
Facts map[types.Object][]analysis.Fact
Result interface{}
Err error
}
func analyze(pkgs []*packages.Package, analyzers []*analysis.Analyzer) []*action {
// Construct the action graph.
if dbg('v') {
log.Printf("building graph of analysis passes")
}
// Each graph node (action) is one unit of analysis.
// Edges express package-to-package (vertical) dependencies,
// and analysis-to-analysis (horizontal) dependencies.
type key struct {
*analysis.Analyzer
*packages.Package
}
actions := make(map[key]*action)
var mkAction func(a *analysis.Analyzer, pkg *packages.Package) *action
mkAction = func(a *analysis.Analyzer, pkg *packages.Package) *action {
k := key{a, pkg}
act, ok := actions[k]
if !ok {
act = &action{a: a, pkg: pkg}
// Add a dependency on each required analyzers.
for _, req := range a.Requires {
act.deps = append(act.deps, mkAction(req, pkg))
}
// An analysis that consumes/produces facts
// must run on the package's dependencies too.
if len(a.FactTypes) > 0 {
paths := make([]string, 0, len(pkg.Imports))
for path := range pkg.Imports {
paths = append(paths, path)
}
sort.Strings(paths) // for determinism
for _, path := range paths {
dep := mkAction(a, pkg.Imports[path])
act.deps = append(act.deps, dep)
}
}
actions[k] = act
}
return act
}
// Build nodes for initial packages.
var roots []*action
for _, a := range analyzers {
for _, pkg := range pkgs {
root := mkAction(a, pkg)
root.isroot = true
roots = append(roots, root)
}
}
// Execute the graph in parallel.
execAll(roots)
return roots
}
// printDiagnostics prints the diagnostics for the root packages in either
// plain text or JSON format. JSON format also includes errors for any
// dependencies.
//
// It returns the exitcode: in plain mode, 0 for success, 1 for analysis
// errors, and 3 for diagnostics. We avoid 2 since the flag package uses
// it. JSON mode always succeeds at printing errors and diagnostics in a
// structured form to stdout.
func printDiagnostics(roots []*action) (exitcode int) {
// Print the output.
//
// Print diagnostics only for root packages,
// but errors for all packages.
printed := make(map[*action]bool)
var print func(*action)
var visitAll func(actions []*action)
visitAll = func(actions []*action) {
for _, act := range actions {
if !printed[act] {
printed[act] = true
visitAll(act.deps)
print(act)
}
}
}
if analysisflags.JSON {
// JSON output
tree := make(analysisflags.JSONTree)
print = func(act *action) {
var diags []analysis.Diagnostic
if act.isroot {
diags = act.diagnostics
}
tree.Add(act.pkg.Fset, act.pkg.ID, act.a.Name, diags, act.err)
}
visitAll(roots)
tree.Print()
} else {
// plain text output
// De-duplicate diagnostics by position (not token.Pos) to
// avoid double-reporting in source files that belong to
// multiple packages, such as foo and foo.test.
type key struct {
token.Position
*analysis.Analyzer
message string
}
seen := make(map[key]bool)
print = func(act *action) {
if act.err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", act.a.Name, act.err)
exitcode = 1 // analysis failed, at least partially
return
}
if act.isroot {
for _, diag := range act.diagnostics {
// We don't display a.Name/f.Category
// as most users don't care.
posn := act.pkg.Fset.Position(diag.Pos)
k := key{posn, act.a, diag.Message}
if seen[k] {
continue // duplicate
}
seen[k] = true
analysisflags.PrintPlain(act.pkg.Fset, diag)
}
}
}
visitAll(roots)
if exitcode == 0 && len(seen) > 0 {
exitcode = 3 // successfuly produced diagnostics
}
}
// Print timing info.
if dbg('t') {
if !dbg('p') {
log.Println("Warning: times are mostly GC/scheduler noise; use -debug=tp to disable parallelism")
}
var all []*action
var total time.Duration
for act := range printed {
all = append(all, act)
total += act.duration
}
sort.Slice(all, func(i, j int) bool {
return all[i].duration > all[j].duration
})
// Print actions accounting for 90% of the total.
var sum time.Duration
for _, act := range all {
fmt.Fprintf(os.Stderr, "%s\t%s\n", act.duration, act)
sum += act.duration
if sum >= total*9/10 {
break
}
}
}
return exitcode
}
// needFacts reports whether any analysis required by the specified set
// needs facts. If so, we must load the entire program from source.
func needFacts(analyzers []*analysis.Analyzer) bool {
seen := make(map[*analysis.Analyzer]bool)
var q []*analysis.Analyzer // for BFS
q = append(q, analyzers...)
for len(q) > 0 {
a := q[0]
q = q[1:]
if !seen[a] {
seen[a] = true
if len(a.FactTypes) > 0 {
return true
}
q = append(q, a.Requires...)
}
}
return false
}
// An action represents one unit of analysis work: the application of
// one analysis to one package. Actions form a DAG, both within a
// package (as different analyzers are applied, either in sequence or
// parallel), and across packages (as dependencies are analyzed).
type action struct {
once sync.Once
a *analysis.Analyzer
pkg *packages.Package
pass *analysis.Pass
isroot bool
deps []*action
objectFacts map[objectFactKey]analysis.Fact
packageFacts map[packageFactKey]analysis.Fact
inputs map[*analysis.Analyzer]interface{}
result interface{}
diagnostics []analysis.Diagnostic
err error
duration time.Duration
}
type objectFactKey struct {
obj types.Object
typ reflect.Type
}
type packageFactKey struct {
pkg *types.Package
typ reflect.Type
}
func (act *action) String() string {
return fmt.Sprintf("%s@%s", act.a, act.pkg)
}
func execAll(actions []*action) {
sequential := dbg('p')
var wg sync.WaitGroup
for _, act := range actions {
wg.Add(1)
work := func(act *action) {
act.exec()
wg.Done()
}
if sequential {
work(act)
} else {
go work(act)
}
}
wg.Wait()
}
func (act *action) exec() { act.once.Do(act.execOnce) }
func (act *action) execOnce() {
// Analyze dependencies.
execAll(act.deps)
// TODO(adonovan): uncomment this during profiling.
// It won't build pre-go1.11 but conditional compilation
// using build tags isn't warranted.
//
// ctx, task := trace.NewTask(context.Background(), "exec")
// trace.Log(ctx, "pass", act.String())
// defer task.End()
// Record time spent in this node but not its dependencies.
// In parallel mode, due to GC/scheduler contention, the
// time is 5x higher than in sequential mode, even with a
// semaphore limiting the number of threads here.
// So use -debug=tp.
if dbg('t') {
t0 := time.Now()
defer func() { act.duration = time.Since(t0) }()
}
// Report an error if any dependency failed.
var failed []string
for _, dep := range act.deps {
if dep.err != nil {
failed = append(failed, dep.String())
}
}
if failed != nil {
sort.Strings(failed)
act.err = fmt.Errorf("failed prerequisites: %s", strings.Join(failed, ", "))
return
}
// Plumb the output values of the dependencies
// into the inputs of this action. Also facts.
inputs := make(map[*analysis.Analyzer]interface{})
act.objectFacts = make(map[objectFactKey]analysis.Fact)
act.packageFacts = make(map[packageFactKey]analysis.Fact)
for _, dep := range act.deps {
if dep.pkg == act.pkg {
// Same package, different analysis (horizontal edge):
// in-memory outputs of prerequisite analyzers
// become inputs to this analysis pass.
inputs[dep.a] = dep.result
} else if dep.a == act.a { // (always true)
// Same analysis, different package (vertical edge):
// serialized facts produced by prerequisite analysis
// become available to this analysis pass.
inheritFacts(act, dep)
}
}
// Run the analysis.
pass := &analysis.Pass{
Analyzer: act.a,
Fset: act.pkg.Fset,
Files: act.pkg.Syntax,
OtherFiles: act.pkg.OtherFiles,
Pkg: act.pkg.Types,
TypesInfo: act.pkg.TypesInfo,
ResultOf: inputs,
Report: func(d analysis.Diagnostic) { act.diagnostics = append(act.diagnostics, d) },
ImportObjectFact: act.importObjectFact,
ExportObjectFact: act.exportObjectFact,
ImportPackageFact: act.importPackageFact,
ExportPackageFact: act.exportPackageFact,
}
act.pass = pass
var err error
if act.pkg.IllTyped && !pass.Analyzer.RunDespiteErrors {
err = fmt.Errorf("analysis skipped due to errors in package")
} else {
act.result, err = pass.Analyzer.Run(pass)
if err == nil {
if got, want := reflect.TypeOf(act.result), pass.Analyzer.ResultType; got != want {
err = fmt.Errorf(
"internal error: on package %s, analyzer %s returned a result of type %v, but declared ResultType %v",
pass.Pkg.Path(), pass.Analyzer, got, want)
}
}
}
act.err = err
// disallow calls after Run
pass.ExportObjectFact = nil
pass.ExportPackageFact = nil
}
// inheritFacts populates act.facts with
// those it obtains from its dependency, dep.
func inheritFacts(act, dep *action) {
serialize := dbg('s')
for key, fact := range dep.objectFacts {
// Filter out facts related to objects
// that are irrelevant downstream
// (equivalently: not in the compiler export data).
if !exportedFrom(key.obj, dep.pkg.Types) {
if false {
log.Printf("%v: discarding %T fact from %s for %s: %s", act, fact, dep, key.obj, fact)
}
continue
}
// Optionally serialize/deserialize fact
// to verify that it works across address spaces.
if serialize {
var err error
fact, err = codeFact(fact)
if err != nil {
log.Panicf("internal error: encoding of %T fact failed in %v", fact, act)
}
}
if false {
log.Printf("%v: inherited %T fact for %s: %s", act, fact, key.obj, fact)
}
act.objectFacts[key] = fact
}
for key, fact := range dep.packageFacts {
// TODO: filter out facts that belong to
// packages not mentioned in the export data
// to prevent side channels.
// Optionally serialize/deserialize fact
// to verify that it works across address spaces
// and is deterministic.
if serialize {
var err error
fact, err = codeFact(fact)
if err != nil {
log.Panicf("internal error: encoding of %T fact failed in %v", fact, act)
}
}
if false {
log.Printf("%v: inherited %T fact for %s: %s", act, fact, key.pkg.Path(), fact)
}
act.packageFacts[key] = fact
}
}
// codeFact encodes then decodes a fact,
// just to exercise that logic.
func codeFact(fact analysis.Fact) (analysis.Fact, error) {
// We encode facts one at a time.
// A real modular driver would emit all facts
// into one encoder to improve gob efficiency.
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(fact); err != nil {
return nil, err
}
// Encode it twice and assert that we get the same bits.
// This helps detect nondeterministic Gob encoding (e.g. of maps).
var buf2 bytes.Buffer
if err := gob.NewEncoder(&buf2).Encode(fact); err != nil {
return nil, err
}
if !bytes.Equal(buf.Bytes(), buf2.Bytes()) {
return nil, fmt.Errorf("encoding of %T fact is nondeterministic", fact)
}
new := reflect.New(reflect.TypeOf(fact).Elem()).Interface().(analysis.Fact)
if err := gob.NewDecoder(&buf).Decode(new); err != nil {
return nil, err
}
return new, nil
}
// exportedFrom reports whether obj may be visible to a package that imports pkg.
// This includes not just the exported members of pkg, but also unexported
// constants, types, fields, and methods, perhaps belonging to oether packages,
// that find there way into the API.
// This is an overapproximation of the more accurate approach used by
// gc export data, which walks the type graph, but it's much simpler.
//
// TODO(adonovan): do more accurate filtering by walking the type graph.
func exportedFrom(obj types.Object, pkg *types.Package) bool {
switch obj := obj.(type) {
case *types.Func:
return obj.Exported() && obj.Pkg() == pkg ||
obj.Type().(*types.Signature).Recv() != nil
case *types.Var:
return obj.Exported() && obj.Pkg() == pkg ||
obj.IsField()
case *types.TypeName, *types.Const:
return true
}
return false // Nil, Builtin, Label, or PkgName
}
// importObjectFact implements Pass.ImportObjectFact.
// Given a non-nil pointer ptr of type *T, where *T satisfies Fact,
// importObjectFact copies the fact value to *ptr.
func (act *action) importObjectFact(obj types.Object, ptr analysis.Fact) bool {
if obj == nil {
panic("nil object")
}
key := objectFactKey{obj, factType(ptr)}
if v, ok := act.objectFacts[key]; ok {
reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
return true
}
return false
}
// exportObjectFact implements Pass.ExportObjectFact.
func (act *action) exportObjectFact(obj types.Object, fact analysis.Fact) {
if act.pass.ExportObjectFact == nil {
log.Panicf("%s: Pass.ExportObjectFact(%s, %T) called after Run", act, obj, fact)
}
if obj.Pkg() != act.pkg.Types {
log.Panicf("internal error: in analysis %s of package %s: Fact.Set(%s, %T): can't set facts on objects belonging another package",
act.a, act.pkg, obj, fact)
}
key := objectFactKey{obj, factType(fact)}
act.objectFacts[key] = fact // clobber any existing entry
if dbg('f') {
objstr := types.ObjectString(obj, (*types.Package).Name)
fmt.Fprintf(os.Stderr, "%s: object %s has fact %s\n",
act.pkg.Fset.Position(obj.Pos()), objstr, fact)
}
}
// importPackageFact implements Pass.ImportPackageFact.
// Given a non-nil pointer ptr of type *T, where *T satisfies Fact,
// fact copies the fact value to *ptr.
func (act *action) importPackageFact(pkg *types.Package, ptr analysis.Fact) bool {
if pkg == nil {
panic("nil package")
}
key := packageFactKey{pkg, factType(ptr)}
if v, ok := act.packageFacts[key]; ok {
reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
return true
}
return false
}
// exportPackageFact implements Pass.ExportPackageFact.
func (act *action) exportPackageFact(fact analysis.Fact) {
if act.pass.ExportPackageFact == nil {
log.Panicf("%s: Pass.ExportPackageFact(%T) called after Run", act, fact)
}
key := packageFactKey{act.pass.Pkg, factType(fact)}
act.packageFacts[key] = fact // clobber any existing entry
if dbg('f') {
fmt.Fprintf(os.Stderr, "%s: package %s has fact %s\n",
act.pkg.Fset.Position(act.pass.Files[0].Pos()), act.pass.Pkg.Path(), fact)
}
}
func factType(fact analysis.Fact) reflect.Type {
t := reflect.TypeOf(fact)
if t.Kind() != reflect.Ptr {
log.Fatalf("invalid Fact type: got %T, want pointer", t)
}
return t
}
func dbg(b byte) bool { return strings.IndexByte(Debug, b) >= 0 }

View File

@@ -0,0 +1,299 @@
// Copyright 2018 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 facts defines a serializable set of analysis.Fact.
//
// It provides a partial implementation of the Fact-related parts of the
// analysis.Pass interface for use in analysis drivers such as "go vet"
// and other build systems.
//
// The serial format is unspecified and may change, so the same version
// of this package must be used for reading and writing serialized facts.
//
// The handling of facts in the analysis system parallels the handling
// of type information in the compiler: during compilation of package P,
// the compiler emits an export data file that describes the type of
// every object (named thing) defined in package P, plus every object
// indirectly reachable from one of those objects. Thus the downstream
// compiler of package Q need only load one export data file per direct
// import of Q, and it will learn everything about the API of package P
// and everything it needs to know about the API of P's dependencies.
//
// Similarly, analysis of package P emits a fact set containing facts
// about all objects exported from P, plus additional facts about only
// those objects of P's dependencies that are reachable from the API of
// package P; the downstream analysis of Q need only load one fact set
// per direct import of Q.
//
// The notion of "exportedness" that matters here is that of the
// compiler. According to the language spec, a method pkg.T.f is
// unexported simply because its name starts with lowercase. But the
// compiler must nonethless export f so that downstream compilations can
// accurately ascertain whether pkg.T implements an interface pkg.I
// defined as interface{f()}. Exported thus means "described in export
// data".
//
package facts
import (
"bytes"
"encoding/gob"
"fmt"
"go/types"
"io/ioutil"
"log"
"reflect"
"sort"
"sync"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/types/objectpath"
)
const debug = false
// A Set is a set of analysis.Facts.
//
// Decode creates a Set of facts by reading from the imports of a given
// package, and Encode writes out the set. Between these operation,
// the Import and Export methods will query and update the set.
//
// All of Set's methods except String are safe to call concurrently.
type Set struct {
pkg *types.Package
mu sync.Mutex
m map[key]analysis.Fact
}
type key struct {
pkg *types.Package
obj types.Object // (object facts only)
t reflect.Type
}
// ImportObjectFact implements analysis.Pass.ImportObjectFact.
func (s *Set) ImportObjectFact(obj types.Object, ptr analysis.Fact) bool {
if obj == nil {
panic("nil object")
}
key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(ptr)}
s.mu.Lock()
defer s.mu.Unlock()
if v, ok := s.m[key]; ok {
reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
return true
}
return false
}
// ExportObjectFact implements analysis.Pass.ExportObjectFact.
func (s *Set) ExportObjectFact(obj types.Object, fact analysis.Fact) {
if obj.Pkg() != s.pkg {
log.Panicf("in package %s: ExportObjectFact(%s, %T): can't set fact on object belonging another package",
s.pkg, obj, fact)
}
key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(fact)}
s.mu.Lock()
s.m[key] = fact // clobber any existing entry
s.mu.Unlock()
}
// ImportPackageFact implements analysis.Pass.ImportPackageFact.
func (s *Set) ImportPackageFact(pkg *types.Package, ptr analysis.Fact) bool {
if pkg == nil {
panic("nil package")
}
key := key{pkg: pkg, t: reflect.TypeOf(ptr)}
s.mu.Lock()
defer s.mu.Unlock()
if v, ok := s.m[key]; ok {
reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
return true
}
return false
}
// ExportPackageFact implements analysis.Pass.ExportPackageFact.
func (s *Set) ExportPackageFact(fact analysis.Fact) {
key := key{pkg: s.pkg, t: reflect.TypeOf(fact)}
s.mu.Lock()
s.m[key] = fact // clobber any existing entry
s.mu.Unlock()
}
// gobFact is the Gob declaration of a serialized fact.
type gobFact struct {
PkgPath string // path of package
Object objectpath.Path // optional path of object relative to package itself
Fact analysis.Fact // type and value of user-defined Fact
}
// Decode decodes all the facts relevant to the analysis of package pkg.
// The read function reads serialized fact data from an external source
// for one of of pkg's direct imports. The empty file is a valid
// encoding of an empty fact set.
//
// It is the caller's responsibility to call gob.Register on all
// necessary fact types.
func Decode(pkg *types.Package, read func(packagePath string) ([]byte, error)) (*Set, error) {
// Compute the import map for this package.
// See the package doc comment.
packages := importMap(pkg.Imports())
// Read facts from imported packages.
// Facts may describe indirectly imported packages, or their objects.
m := make(map[key]analysis.Fact) // one big bucket
for _, imp := range pkg.Imports() {
logf := func(format string, args ...interface{}) {
if debug {
prefix := fmt.Sprintf("in %s, importing %s: ",
pkg.Path(), imp.Path())
log.Print(prefix, fmt.Sprintf(format, args...))
}
}
// Read the gob-encoded facts.
data, err := read(imp.Path())
if err != nil {
return nil, fmt.Errorf("in %s, can't import facts for package %q: %v",
pkg.Path(), imp.Path(), err)
}
if len(data) == 0 {
continue // no facts
}
var gobFacts []gobFact
if err := gob.NewDecoder(bytes.NewReader(data)).Decode(&gobFacts); err != nil {
return nil, fmt.Errorf("decoding facts for %q: %v", imp.Path(), err)
}
if debug {
logf("decoded %d facts: %v", len(gobFacts), gobFacts)
}
// Parse each one into a key and a Fact.
for _, f := range gobFacts {
factPkg := packages[f.PkgPath]
if factPkg == nil {
// Fact relates to a dependency that was
// unused in this translation unit. Skip.
logf("no package %q; discarding %v", f.PkgPath, f.Fact)
continue
}
key := key{pkg: factPkg, t: reflect.TypeOf(f.Fact)}
if f.Object != "" {
// object fact
obj, err := objectpath.Object(factPkg, f.Object)
if err != nil {
// (most likely due to unexported object)
// TODO(adonovan): audit for other possibilities.
logf("no object for path: %v; discarding %s", err, f.Fact)
continue
}
key.obj = obj
logf("read %T fact %s for %v", f.Fact, f.Fact, key.obj)
} else {
// package fact
logf("read %T fact %s for %v", f.Fact, f.Fact, factPkg)
}
m[key] = f.Fact
}
}
return &Set{pkg: pkg, m: m}, nil
}
// Encode encodes a set of facts to a memory buffer.
//
// It may fail if one of the Facts could not be gob-encoded, but this is
// a sign of a bug in an Analyzer.
func (s *Set) Encode() []byte {
// TODO(adonovan): opt: use a more efficient encoding
// that avoids repeating PkgPath for each fact.
// Gather all facts, including those from imported packages.
var gobFacts []gobFact
s.mu.Lock()
for k, fact := range s.m {
if debug {
log.Printf("%v => %s\n", k, fact)
}
var object objectpath.Path
if k.obj != nil {
path, err := objectpath.For(k.obj)
if err != nil {
if debug {
log.Printf("discarding fact %s about %s\n", fact, k.obj)
}
continue // object not accessible from package API; discard fact
}
object = path
}
gobFacts = append(gobFacts, gobFact{
PkgPath: k.pkg.Path(),
Object: object,
Fact: fact,
})
}
s.mu.Unlock()
// Sort facts by (package, object, type) for determinism.
sort.Slice(gobFacts, func(i, j int) bool {
x, y := gobFacts[i], gobFacts[j]
if x.PkgPath != y.PkgPath {
return x.PkgPath < y.PkgPath
}
if x.Object != y.Object {
return x.Object < y.Object
}
tx := reflect.TypeOf(x.Fact)
ty := reflect.TypeOf(y.Fact)
if tx != ty {
return tx.String() < ty.String()
}
return false // equal
})
var buf bytes.Buffer
if len(gobFacts) > 0 {
if err := gob.NewEncoder(&buf).Encode(gobFacts); err != nil {
// Fact encoding should never fail. Identify the culprit.
for _, gf := range gobFacts {
if err := gob.NewEncoder(ioutil.Discard).Encode(gf); err != nil {
fact := gf.Fact
pkgpath := reflect.TypeOf(fact).Elem().PkgPath()
log.Panicf("internal error: gob encoding of analysis fact %s failed: %v; please report a bug against fact %T in package %q",
fact, err, fact, pkgpath)
}
}
}
}
if debug {
log.Printf("package %q: encode %d facts, %d bytes\n",
s.pkg.Path(), len(gobFacts), buf.Len())
}
return buf.Bytes()
}
// String is provided only for debugging, and must not be called
// concurrent with any Import/Export method.
func (s *Set) String() string {
var buf bytes.Buffer
buf.WriteString("{")
for k, f := range s.m {
if buf.Len() > 1 {
buf.WriteString(", ")
}
if k.obj != nil {
buf.WriteString(k.obj.String())
} else {
buf.WriteString(k.pkg.Path())
}
fmt.Fprintf(&buf, ": %v", f)
}
buf.WriteString("}")
return buf.String()
}

View File

@@ -0,0 +1,174 @@
// Copyright 2018 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 facts_test
import (
"encoding/gob"
"fmt"
"go/token"
"go/types"
"os"
"testing"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/internal/facts"
"golang.org/x/tools/go/packages"
)
type myFact struct {
S string
}
func (f *myFact) String() string { return fmt.Sprintf("myFact(%s)", f.S) }
func (f *myFact) AFact() {}
func TestEncodeDecode(t *testing.T) {
gob.Register(new(myFact))
// c -> b -> a, a2
// c does not directly depend on a, but it indirectly uses a.T.
//
// Package a2 is never loaded directly so it is incomplete.
//
// We use only types in this example because we rely on
// types.Eval to resolve the lookup expressions, and it only
// works for types. This is a definite gap in the typechecker API.
files := map[string]string{
"a/a.go": `package a; type A int; type T int`,
"a2/a.go": `package a2; type A2 int; type Unneeded int`,
"b/b.go": `package b; import ("a"; "a2"); type B chan a2.A2; type F func() a.T`,
"c/c.go": `package c; import "b"; type C []b.B`,
}
dir, cleanup, err := analysistest.WriteFiles(files)
if err != nil {
t.Fatal(err)
}
defer cleanup()
// factmap represents the passing of encoded facts from one
// package to another. In practice one would use the file system.
factmap := make(map[string][]byte)
read := func(path string) ([]byte, error) { return factmap[path], nil }
// In the following table, we analyze packages (a, b, c) in order,
// look up various objects accessible within each package,
// and see if they have a fact. The "analysis" exports a fact
// for every object at package level.
//
// Note: Loop iterations are not independent test cases;
// order matters, as we populate factmap.
type lookups []struct {
objexpr string
want string
}
for _, test := range []struct {
path string
lookups lookups
}{
{"a", lookups{
{"A", "myFact(a.A)"},
}},
{"b", lookups{
{"a.A", "myFact(a.A)"},
{"a.T", "myFact(a.T)"},
{"B", "myFact(b.B)"},
{"F", "myFact(b.F)"},
{"F(nil)()", "myFact(a.T)"}, // (result type of b.F)
}},
{"c", lookups{
{"b.B", "myFact(b.B)"},
{"b.F", "myFact(b.F)"},
//{"b.F(nil)()", "myFact(a.T)"}, // no fact; TODO(adonovan): investigate
{"C", "myFact(c.C)"},
{"C{}[0]", "myFact(b.B)"},
{"<-(C{}[0])", "no fact"}, // object but no fact (we never "analyze" a2)
}},
} {
// load package
pkg, err := load(dir, test.path)
if err != nil {
t.Fatal(err)
}
// decode
facts, err := facts.Decode(pkg, read)
if err != nil {
t.Fatalf("Decode failed: %v", err)
}
if true {
t.Logf("decode %s facts = %v", pkg.Path(), facts) // show all facts
}
// export
// (one fact for each package-level object)
scope := pkg.Scope()
for _, name := range scope.Names() {
obj := scope.Lookup(name)
fact := &myFact{obj.Pkg().Name() + "." + obj.Name()}
facts.ExportObjectFact(obj, fact)
}
// import
// (after export, because an analyzer may import its own facts)
for _, lookup := range test.lookups {
fact := new(myFact)
var got string
if obj := find(pkg, lookup.objexpr); obj == nil {
got = "no object"
} else if facts.ImportObjectFact(obj, fact) {
got = fact.String()
} else {
got = "no fact"
}
if got != lookup.want {
t.Errorf("in %s, ImportObjectFact(%s, %T) = %s, want %s",
pkg.Path(), lookup.objexpr, fact, got, lookup.want)
}
}
// encode
factmap[pkg.Path()] = facts.Encode()
}
}
func find(p *types.Package, expr string) types.Object {
// types.Eval only allows us to compute a TypeName object for an expression.
// TODO(adonovan): support other expressions that denote an object:
// - an identifier (or qualified ident) for a func, const, or var
// - new(T).f for a field or method
// I've added CheckExpr in https://go-review.googlesource.com/c/go/+/144677.
// If that becomes available, use it.
// Choose an arbitrary position within the (single-file) package
// so that we are within the scope of its import declarations.
somepos := p.Scope().Lookup(p.Scope().Names()[0]).Pos()
tv, err := types.Eval(token.NewFileSet(), p, somepos, expr)
if err != nil {
return nil
}
if n, ok := tv.Type.(*types.Named); ok {
return n.Obj()
}
return nil
}
func load(dir string, path string) (*types.Package, error) {
cfg := &packages.Config{
Mode: packages.LoadSyntax,
Dir: dir,
Env: append(os.Environ(), "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off"),
}
pkgs, err := packages.Load(cfg, path)
if err != nil {
return nil, err
}
if packages.PrintErrors(pkgs) > 0 {
return nil, fmt.Errorf("packages had errors")
}
if len(pkgs) == 0 {
return nil, fmt.Errorf("no package matched %s", path)
}
return pkgs[0].Types, nil
}

View File

@@ -0,0 +1,88 @@
// Copyright 2018 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 facts
import "go/types"
// importMap computes the import map for a package by traversing the
// entire exported API each of its imports.
//
// This is a workaround for the fact that we cannot access the map used
// internally by the types.Importer returned by go/importer. The entries
// in this map are the packages and objects that may be relevant to the
// current analysis unit.
//
// Packages in the map that are only indirectly imported may be
// incomplete (!pkg.Complete()).
//
func importMap(imports []*types.Package) map[string]*types.Package {
objects := make(map[types.Object]bool)
packages := make(map[string]*types.Package)
var addObj func(obj types.Object) bool
var addType func(T types.Type)
addObj = func(obj types.Object) bool {
if !objects[obj] {
objects[obj] = true
addType(obj.Type())
if pkg := obj.Pkg(); pkg != nil {
packages[pkg.Path()] = pkg
}
return true
}
return false
}
addType = func(T types.Type) {
switch T := T.(type) {
case *types.Basic:
// nop
case *types.Named:
if addObj(T.Obj()) {
for i := 0; i < T.NumMethods(); i++ {
addObj(T.Method(i))
}
}
case *types.Pointer:
addType(T.Elem())
case *types.Slice:
addType(T.Elem())
case *types.Array:
addType(T.Elem())
case *types.Chan:
addType(T.Elem())
case *types.Map:
addType(T.Key())
addType(T.Elem())
case *types.Signature:
addType(T.Params())
addType(T.Results())
case *types.Struct:
for i := 0; i < T.NumFields(); i++ {
addObj(T.Field(i))
}
case *types.Tuple:
for i := 0; i < T.Len(); i++ {
addObj(T.At(i))
}
case *types.Interface:
for i := 0; i < T.NumMethods(); i++ {
addObj(T.Method(i))
}
}
}
for _, imp := range imports {
packages[imp.Path()] = imp
scope := imp.Scope()
for _, name := range scope.Names() {
addObj(scope.Lookup(name))
}
}
return packages
}

View File

@@ -0,0 +1,60 @@
// Copyright 2018 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 multichecker defines the main function for an analysis driver
// with several analyzers. This package makes it easy for anyone to build
// an analysis tool containing just the analyzers they need.
package multichecker
import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/internal/analysisflags"
"golang.org/x/tools/go/analysis/internal/checker"
"golang.org/x/tools/go/analysis/unitchecker"
)
func Main(analyzers ...*analysis.Analyzer) {
progname := filepath.Base(os.Args[0])
log.SetFlags(0)
log.SetPrefix(progname + ": ") // e.g. "vet: "
if err := analysis.Validate(analyzers); err != nil {
log.Fatal(err)
}
checker.RegisterFlags()
analyzers = analysisflags.Parse(analyzers, true)
args := flag.Args()
if len(args) == 0 {
fmt.Fprintf(os.Stderr, `%[1]s is a tool for static analysis of Go programs.
Usage: %[1]s [-flag] [package]
Run '%[1]s help' for more detail,
or '%[1]s help name' for details and flags of a specific analyzer.
`, progname)
os.Exit(1)
}
if args[0] == "help" {
analysisflags.Help(progname, analyzers, args[1:])
os.Exit(0)
}
if len(args) == 1 && strings.HasSuffix(args[0], ".cfg") {
unitchecker.Run(args[0], analyzers)
panic("unreachable")
}
os.Exit(checker.Run(args, analyzers))
}

View File

@@ -0,0 +1,82 @@
// +build go1.12
package multichecker_test
import (
"fmt"
"os"
"os/exec"
"runtime"
"testing"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/multichecker"
"golang.org/x/tools/go/analysis/passes/findcall"
)
func main() {
fail := &analysis.Analyzer{
Name: "fail",
Doc: "always fail on a package 'sort'",
Run: func(pass *analysis.Pass) (interface{}, error) {
if pass.Pkg.Path() == "sort" {
return nil, fmt.Errorf("failed")
}
return nil, nil
},
}
multichecker.Main(findcall.Analyzer, fail)
}
// TestExitCode ensures that analysis failures are reported correctly.
// This test fork/execs the main function above.
func TestExitCode(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skipf("skipping fork/exec test on this platform")
}
if os.Getenv("MULTICHECKER_CHILD") == "1" {
// child process
// replace [progname -test.run=TestExitCode -- ...]
// by [progname ...]
os.Args = os.Args[2:]
os.Args[0] = "vet"
main()
panic("unreachable")
}
for _, test := range []struct {
args []string
want int
}{
{[]string{"nosuchdir/..."}, 1}, // matched no packages
{[]string{"nosuchpkg"}, 1}, // matched no packages
{[]string{"-unknownflag"}, 2}, // flag error
{[]string{"-findcall.name=panic", "io"}, 3}, // finds diagnostics
{[]string{"-findcall=0", "io"}, 0}, // no checkers
{[]string{"-findcall.name=nosuchfunc", "io"}, 0}, // no diagnostics
{[]string{"-findcall.name=panic", "sort", "io"}, 1}, // 'fail' failed on 'sort'
// -json: exits zero even in face of diagnostics or package errors.
{[]string{"-findcall.name=panic", "-json", "io"}, 0},
{[]string{"-findcall.name=panic", "-json", "io"}, 0},
{[]string{"-findcall.name=panic", "-json", "sort", "io"}, 0},
} {
args := []string{"-test.run=TestExitCode", "--"}
args = append(args, test.args...)
cmd := exec.Command(os.Args[0], args...)
cmd.Env = append(os.Environ(), "MULTICHECKER_CHILD=1")
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Logf("%s: out=<<%s>>", test.args, out)
}
var exitcode int
if err, ok := err.(*exec.ExitError); ok {
exitcode = err.ExitCode() // requires go1.12
}
if exitcode != test.want {
t.Errorf("%s: exited %d, want %d", test.args, exitcode, test.want)
}
}
}

8
vendor/golang.org/x/tools/go/analysis/passes/README generated vendored Normal file
View File

@@ -0,0 +1,8 @@
This directory does not contain a Go package,
but acts as a container for various analyses
that implement the golang.org/x/tools/go/analysis
API and may be imported into an analysis tool.
By convention, each package foo provides the analysis,
and each command foo/cmd/foo provides a standalone driver.

View File

@@ -0,0 +1,760 @@
// Copyright 2013 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 asmdecl defines an Analyzer that reports mismatches between
// assembly files and Go declarations.
package asmdecl
import (
"bytes"
"fmt"
"go/ast"
"go/build"
"go/token"
"go/types"
"log"
"regexp"
"strconv"
"strings"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
)
var Analyzer = &analysis.Analyzer{
Name: "asmdecl",
Doc: "report mismatches between assembly files and Go declarations",
Run: run,
}
// 'kind' is a kind of assembly variable.
// The kinds 1, 2, 4, 8 stand for values of that size.
type asmKind int
// These special kinds are not valid sizes.
const (
asmString asmKind = 100 + iota
asmSlice
asmArray
asmInterface
asmEmptyInterface
asmStruct
asmComplex
)
// An asmArch describes assembly parameters for an architecture
type asmArch struct {
name string
bigEndian bool
stack string
lr bool
// calculated during initialization
sizes types.Sizes
intSize int
ptrSize int
maxAlign int
}
// An asmFunc describes the expected variables for a function on a given architecture.
type asmFunc struct {
arch *asmArch
size int // size of all arguments
vars map[string]*asmVar
varByOffset map[int]*asmVar
}
// An asmVar describes a single assembly variable.
type asmVar struct {
name string
kind asmKind
typ string
off int
size int
inner []*asmVar
}
var (
asmArch386 = asmArch{name: "386", bigEndian: false, stack: "SP", lr: false}
asmArchArm = asmArch{name: "arm", bigEndian: false, stack: "R13", lr: true}
asmArchArm64 = asmArch{name: "arm64", bigEndian: false, stack: "RSP", lr: true}
asmArchAmd64 = asmArch{name: "amd64", bigEndian: false, stack: "SP", lr: false}
asmArchAmd64p32 = asmArch{name: "amd64p32", bigEndian: false, stack: "SP", lr: false}
asmArchMips = asmArch{name: "mips", bigEndian: true, stack: "R29", lr: true}
asmArchMipsLE = asmArch{name: "mipsle", bigEndian: false, stack: "R29", lr: true}
asmArchMips64 = asmArch{name: "mips64", bigEndian: true, stack: "R29", lr: true}
asmArchMips64LE = asmArch{name: "mips64le", bigEndian: false, stack: "R29", lr: true}
asmArchPpc64 = asmArch{name: "ppc64", bigEndian: true, stack: "R1", lr: true}
asmArchPpc64LE = asmArch{name: "ppc64le", bigEndian: false, stack: "R1", lr: true}
asmArchS390X = asmArch{name: "s390x", bigEndian: true, stack: "R15", lr: true}
asmArchWasm = asmArch{name: "wasm", bigEndian: false, stack: "SP", lr: false}
arches = []*asmArch{
&asmArch386,
&asmArchArm,
&asmArchArm64,
&asmArchAmd64,
&asmArchAmd64p32,
&asmArchMips,
&asmArchMipsLE,
&asmArchMips64,
&asmArchMips64LE,
&asmArchPpc64,
&asmArchPpc64LE,
&asmArchS390X,
&asmArchWasm,
}
)
func init() {
for _, arch := range arches {
arch.sizes = types.SizesFor("gc", arch.name)
if arch.sizes == nil {
// TODO(adonovan): fix: now that asmdecl is not in the standard
// library we cannot assume types.SizesFor is consistent with arches.
// For now, assume 64-bit norms and print a warning.
// But this warning should really be deferred until we attempt to use
// arch, which is very unlikely.
arch.sizes = types.SizesFor("gc", "amd64")
log.Printf("unknown architecture %s", arch.name)
}
arch.intSize = int(arch.sizes.Sizeof(types.Typ[types.Int]))
arch.ptrSize = int(arch.sizes.Sizeof(types.Typ[types.UnsafePointer]))
arch.maxAlign = int(arch.sizes.Alignof(types.Typ[types.Int64]))
}
}
var (
re = regexp.MustCompile
asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`)
asmTEXT = re(`\bTEXT\b(.*)·([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+()]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`)
asmDATA = re(`\b(DATA|GLOBL)\b`)
asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`)
asmUnnamedFP = re(`[^+\-0-9](([0-9]+)\(FP\))`)
asmSP = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`)
asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`)
ppc64Suff = re(`([BHWD])(ZU|Z|U|BR)?$`)
)
func run(pass *analysis.Pass) (interface{}, error) {
// No work if no assembly files.
var sfiles []string
for _, fname := range pass.OtherFiles {
if strings.HasSuffix(fname, ".s") {
sfiles = append(sfiles, fname)
}
}
if sfiles == nil {
return nil, nil
}
// Gather declarations. knownFunc[name][arch] is func description.
knownFunc := make(map[string]map[string]*asmFunc)
for _, f := range pass.Files {
for _, decl := range f.Decls {
if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil {
knownFunc[decl.Name.Name] = asmParseDecl(pass, decl)
}
}
}
Files:
for _, fname := range sfiles {
content, tf, err := analysisutil.ReadFile(pass.Fset, fname)
if err != nil {
return nil, err
}
// Determine architecture from file name if possible.
var arch string
var archDef *asmArch
for _, a := range arches {
if strings.HasSuffix(fname, "_"+a.name+".s") {
arch = a.name
archDef = a
break
}
}
lines := strings.SplitAfter(string(content), "\n")
var (
fn *asmFunc
fnName string
localSize, argSize int
wroteSP bool
haveRetArg bool
retLine []int
)
flushRet := func() {
if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 {
v := fn.vars["ret"]
for _, line := range retLine {
pass.Reportf(analysisutil.LineStart(tf, line), "[%s] %s: RET without writing to %d-byte ret+%d(FP)", arch, fnName, v.size, v.off)
}
}
retLine = nil
}
for lineno, line := range lines {
lineno++
badf := func(format string, args ...interface{}) {
pass.Reportf(analysisutil.LineStart(tf, lineno), "[%s] %s: %s", arch, fnName, fmt.Sprintf(format, args...))
}
if arch == "" {
// Determine architecture from +build line if possible.
if m := asmPlusBuild.FindStringSubmatch(line); m != nil {
// There can be multiple architectures in a single +build line,
// so accumulate them all and then prefer the one that
// matches build.Default.GOARCH.
var archCandidates []*asmArch
for _, fld := range strings.Fields(m[1]) {
for _, a := range arches {
if a.name == fld {
archCandidates = append(archCandidates, a)
}
}
}
for _, a := range archCandidates {
if a.name == build.Default.GOARCH {
archCandidates = []*asmArch{a}
break
}
}
if len(archCandidates) > 0 {
arch = archCandidates[0].name
archDef = archCandidates[0]
}
}
}
if m := asmTEXT.FindStringSubmatch(line); m != nil {
flushRet()
if arch == "" {
// Arch not specified by filename or build tags.
// Fall back to build.Default.GOARCH.
for _, a := range arches {
if a.name == build.Default.GOARCH {
arch = a.name
archDef = a
break
}
}
if arch == "" {
log.Printf("%s: cannot determine architecture for assembly file", fname)
continue Files
}
}
fnName = m[2]
if pkgPath := strings.TrimSpace(m[1]); pkgPath != "" {
// The assembler uses Unicode division slash within
// identifiers to represent the directory separator.
pkgPath = strings.Replace(pkgPath, "", "/", -1)
if pkgPath != pass.Pkg.Path() {
log.Printf("%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", fname, lineno, arch, fnName, pkgPath)
fn = nil
fnName = ""
continue
}
}
flag := m[3]
fn = knownFunc[fnName][arch]
if fn != nil {
size, _ := strconv.Atoi(m[5])
if size != fn.size && (flag != "7" && !strings.Contains(flag, "NOSPLIT") || size != 0) {
badf("wrong argument size %d; expected $...-%d", size, fn.size)
}
}
localSize, _ = strconv.Atoi(m[4])
localSize += archDef.intSize
if archDef.lr && !strings.Contains(flag, "NOFRAME") {
// Account for caller's saved LR
localSize += archDef.intSize
}
argSize, _ = strconv.Atoi(m[5])
if fn == nil && !strings.Contains(fnName, "<>") {
badf("function %s missing Go declaration", fnName)
}
wroteSP = false
haveRetArg = false
continue
} else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") {
// function, but not visible from Go (didn't match asmTEXT), so stop checking
flushRet()
fn = nil
fnName = ""
continue
}
if strings.Contains(line, "RET") {
retLine = append(retLine, lineno)
}
if fnName == "" {
continue
}
if asmDATA.FindStringSubmatch(line) != nil {
fn = nil
}
if archDef == nil {
continue
}
if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) {
wroteSP = true
continue
}
for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
if m[3] != archDef.stack || wroteSP {
continue
}
off := 0
if m[1] != "" {
off, _ = strconv.Atoi(m[2])
}
if off >= localSize {
if fn != nil {
v := fn.varByOffset[off-localSize]
if v != nil {
badf("%s should be %s+%d(FP)", m[1], v.name, off-localSize)
continue
}
}
if off >= localSize+argSize {
badf("use of %s points beyond argument frame", m[1])
continue
}
badf("use of %s to access argument frame", m[1])
}
}
if fn == nil {
continue
}
for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) {
off, _ := strconv.Atoi(m[2])
v := fn.varByOffset[off]
if v != nil {
badf("use of unnamed argument %s; offset %d is %s+%d(FP)", m[1], off, v.name, v.off)
} else {
badf("use of unnamed argument %s", m[1])
}
}
for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) {
name := m[1]
off := 0
if m[2] != "" {
off, _ = strconv.Atoi(m[2])
}
if name == "ret" || strings.HasPrefix(name, "ret_") {
haveRetArg = true
}
v := fn.vars[name]
if v == nil {
// Allow argframe+0(FP).
if name == "argframe" && off == 0 {
continue
}
v = fn.varByOffset[off]
if v != nil {
badf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off)
} else {
badf("unknown variable %s", name)
}
continue
}
asmCheckVar(badf, fn, line, m[0], off, v)
}
}
flushRet()
}
return nil, nil
}
func asmKindForType(t types.Type, size int) asmKind {
switch t := t.Underlying().(type) {
case *types.Basic:
switch t.Kind() {
case types.String:
return asmString
case types.Complex64, types.Complex128:
return asmComplex
}
return asmKind(size)
case *types.Pointer, *types.Chan, *types.Map, *types.Signature:
return asmKind(size)
case *types.Struct:
return asmStruct
case *types.Interface:
if t.Empty() {
return asmEmptyInterface
}
return asmInterface
case *types.Array:
return asmArray
case *types.Slice:
return asmSlice
}
panic("unreachable")
}
// A component is an assembly-addressable component of a composite type,
// or a composite type itself.
type component struct {
size int
offset int
kind asmKind
typ string
suffix string // Such as _base for string base, _0_lo for lo half of first element of [1]uint64 on 32 bit machine.
outer string // The suffix for immediately containing composite type.
}
func newComponent(suffix string, kind asmKind, typ string, offset, size int, outer string) component {
return component{suffix: suffix, kind: kind, typ: typ, offset: offset, size: size, outer: outer}
}
// componentsOfType generates a list of components of type t.
// For example, given string, the components are the string itself, the base, and the length.
func componentsOfType(arch *asmArch, t types.Type) []component {
return appendComponentsRecursive(arch, t, nil, "", 0)
}
// appendComponentsRecursive implements componentsOfType.
// Recursion is required to correct handle structs and arrays,
// which can contain arbitrary other types.
func appendComponentsRecursive(arch *asmArch, t types.Type, cc []component, suffix string, off int) []component {
s := t.String()
size := int(arch.sizes.Sizeof(t))
kind := asmKindForType(t, size)
cc = append(cc, newComponent(suffix, kind, s, off, size, suffix))
switch kind {
case 8:
if arch.ptrSize == 4 {
w1, w2 := "lo", "hi"
if arch.bigEndian {
w1, w2 = w2, w1
}
cc = append(cc, newComponent(suffix+"_"+w1, 4, "half "+s, off, 4, suffix))
cc = append(cc, newComponent(suffix+"_"+w2, 4, "half "+s, off+4, 4, suffix))
}
case asmEmptyInterface:
cc = append(cc, newComponent(suffix+"_type", asmKind(arch.ptrSize), "interface type", off, arch.ptrSize, suffix))
cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
case asmInterface:
cc = append(cc, newComponent(suffix+"_itable", asmKind(arch.ptrSize), "interface itable", off, arch.ptrSize, suffix))
cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
case asmSlice:
cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "slice base", off, arch.ptrSize, suffix))
cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "slice len", off+arch.ptrSize, arch.intSize, suffix))
cc = append(cc, newComponent(suffix+"_cap", asmKind(arch.intSize), "slice cap", off+arch.ptrSize+arch.intSize, arch.intSize, suffix))
case asmString:
cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "string base", off, arch.ptrSize, suffix))
cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "string len", off+arch.ptrSize, arch.intSize, suffix))
case asmComplex:
fsize := size / 2
cc = append(cc, newComponent(suffix+"_real", asmKind(fsize), fmt.Sprintf("real(complex%d)", size*8), off, fsize, suffix))
cc = append(cc, newComponent(suffix+"_imag", asmKind(fsize), fmt.Sprintf("imag(complex%d)", size*8), off+fsize, fsize, suffix))
case asmStruct:
tu := t.Underlying().(*types.Struct)
fields := make([]*types.Var, tu.NumFields())
for i := 0; i < tu.NumFields(); i++ {
fields[i] = tu.Field(i)
}
offsets := arch.sizes.Offsetsof(fields)
for i, f := range fields {
cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i]))
}
case asmArray:
tu := t.Underlying().(*types.Array)
elem := tu.Elem()
// Calculate offset of each element array.
fields := []*types.Var{
types.NewVar(token.NoPos, nil, "fake0", elem),
types.NewVar(token.NoPos, nil, "fake1", elem),
}
offsets := arch.sizes.Offsetsof(fields)
elemoff := int(offsets[1])
for i := 0; i < int(tu.Len()); i++ {
cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff)
}
}
return cc
}
// asmParseDecl parses a function decl for expected assembly variables.
func asmParseDecl(pass *analysis.Pass, decl *ast.FuncDecl) map[string]*asmFunc {
var (
arch *asmArch
fn *asmFunc
offset int
)
// addParams adds asmVars for each of the parameters in list.
// isret indicates whether the list are the arguments or the return values.
// TODO(adonovan): simplify by passing (*types.Signature).{Params,Results}
// instead of list.
addParams := func(list []*ast.Field, isret bool) {
argnum := 0
for _, fld := range list {
t := pass.TypesInfo.Types[fld.Type].Type
// Work around github.com/golang/go/issues/28277.
if t == nil {
if ell, ok := fld.Type.(*ast.Ellipsis); ok {
t = types.NewSlice(pass.TypesInfo.Types[ell.Elt].Type)
}
}
align := int(arch.sizes.Alignof(t))
size := int(arch.sizes.Sizeof(t))
offset += -offset & (align - 1)
cc := componentsOfType(arch, t)
// names is the list of names with this type.
names := fld.Names
if len(names) == 0 {
// Anonymous args will be called arg, arg1, arg2, ...
// Similarly so for return values: ret, ret1, ret2, ...
name := "arg"
if isret {
name = "ret"
}
if argnum > 0 {
name += strconv.Itoa(argnum)
}
names = []*ast.Ident{ast.NewIdent(name)}
}
argnum += len(names)
// Create variable for each name.
for _, id := range names {
name := id.Name
for _, c := range cc {
outer := name + c.outer
v := asmVar{
name: name + c.suffix,
kind: c.kind,
typ: c.typ,
off: offset + c.offset,
size: c.size,
}
if vo := fn.vars[outer]; vo != nil {
vo.inner = append(vo.inner, &v)
}
fn.vars[v.name] = &v
for i := 0; i < v.size; i++ {
fn.varByOffset[v.off+i] = &v
}
}
offset += size
}
}
}
m := make(map[string]*asmFunc)
for _, arch = range arches {
fn = &asmFunc{
arch: arch,
vars: make(map[string]*asmVar),
varByOffset: make(map[int]*asmVar),
}
offset = 0
addParams(decl.Type.Params.List, false)
if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
offset += -offset & (arch.maxAlign - 1)
addParams(decl.Type.Results.List, true)
}
fn.size = offset
m[arch.name] = fn
}
return m
}
// asmCheckVar checks a single variable reference.
func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) {
m := asmOpcode.FindStringSubmatch(line)
if m == nil {
if !strings.HasPrefix(strings.TrimSpace(line), "//") {
badf("cannot find assembly opcode")
}
return
}
// Determine operand sizes from instruction.
// Typically the suffix suffices, but there are exceptions.
var src, dst, kind asmKind
op := m[1]
switch fn.arch.name + "." + op {
case "386.FMOVLP":
src, dst = 8, 4
case "arm.MOVD":
src = 8
case "arm.MOVW":
src = 4
case "arm.MOVH", "arm.MOVHU":
src = 2
case "arm.MOVB", "arm.MOVBU":
src = 1
// LEA* opcodes don't really read the second arg.
// They just take the address of it.
case "386.LEAL":
dst = 4
case "amd64.LEAQ":
dst = 8
case "amd64p32.LEAL":
dst = 4
default:
switch fn.arch.name {
case "386", "amd64":
if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) {
// FMOVDP, FXCHD, etc
src = 8
break
}
if strings.HasPrefix(op, "P") && strings.HasSuffix(op, "RD") {
// PINSRD, PEXTRD, etc
src = 4
break
}
if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) {
// FMOVFP, FXCHF, etc
src = 4
break
}
if strings.HasSuffix(op, "SD") {
// MOVSD, SQRTSD, etc
src = 8
break
}
if strings.HasSuffix(op, "SS") {
// MOVSS, SQRTSS, etc
src = 4
break
}
if strings.HasPrefix(op, "SET") {
// SETEQ, etc
src = 1
break
}
switch op[len(op)-1] {
case 'B':
src = 1
case 'W':
src = 2
case 'L':
src = 4
case 'D', 'Q':
src = 8
}
case "ppc64", "ppc64le":
// Strip standard suffixes to reveal size letter.
m := ppc64Suff.FindStringSubmatch(op)
if m != nil {
switch m[1][0] {
case 'B':
src = 1
case 'H':
src = 2
case 'W':
src = 4
case 'D':
src = 8
}
}
case "mips", "mipsle", "mips64", "mips64le":
switch op {
case "MOVB", "MOVBU":
src = 1
case "MOVH", "MOVHU":
src = 2
case "MOVW", "MOVWU", "MOVF":
src = 4
case "MOVV", "MOVD":
src = 8
}
case "s390x":
switch op {
case "MOVB", "MOVBZ":
src = 1
case "MOVH", "MOVHZ":
src = 2
case "MOVW", "MOVWZ", "FMOVS":
src = 4
case "MOVD", "FMOVD":
src = 8
}
}
}
if dst == 0 {
dst = src
}
// Determine whether the match we're holding
// is the first or second argument.
if strings.Index(line, expr) > strings.Index(line, ",") {
kind = dst
} else {
kind = src
}
vk := v.kind
vs := v.size
vt := v.typ
switch vk {
case asmInterface, asmEmptyInterface, asmString, asmSlice:
// allow reference to first word (pointer)
vk = v.inner[0].kind
vs = v.inner[0].size
vt = v.inner[0].typ
}
if off != v.off {
var inner bytes.Buffer
for i, vi := range v.inner {
if len(v.inner) > 1 {
fmt.Fprintf(&inner, ",")
}
fmt.Fprintf(&inner, " ")
if i == len(v.inner)-1 {
fmt.Fprintf(&inner, "or ")
}
fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
}
badf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String())
return
}
if kind != 0 && kind != vk {
var inner bytes.Buffer
if len(v.inner) > 0 {
fmt.Fprintf(&inner, " containing")
for i, vi := range v.inner {
if i > 0 && len(v.inner) > 2 {
fmt.Fprintf(&inner, ",")
}
fmt.Fprintf(&inner, " ")
if i > 0 && i == len(v.inner)-1 {
fmt.Fprintf(&inner, "and ")
}
fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
}
}
badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vs, inner.String())
}
}

View File

@@ -0,0 +1,17 @@
// Copyright 2018 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 asmdecl_test
import (
"testing"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/asmdecl"
)
func Test(t *testing.T) {
testdata := analysistest.TestData()
analysistest.Run(t, testdata, asmdecl.Analyzer, "a")
}

View File

@@ -0,0 +1,48 @@
// Copyright 2010 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.
// This file contains declarations to test the assembly in test_asm.s.
package a
type S struct {
i int32
b bool
s string
}
func arg1(x int8, y uint8)
func arg2(x int16, y uint16)
func arg4(x int32, y uint32)
func arg8(x int64, y uint64)
func argint(x int, y uint)
func argptr(x *byte, y *byte, c chan int, m map[int]int, f func())
func argstring(x, y string)
func argslice(x, y []string)
func argiface(x interface{}, y interface {
m()
})
func argcomplex(x complex64, y complex128)
func argstruct(x S, y struct{})
func argarray(x [2]S)
func returnint() int
func returnbyte(x int) byte
func returnnamed(x byte) (r1 int, r2 int16, r3 string, r4 byte)
func returnintmissing() int
func leaf(x, y int) int
func noprof(x int)
func dupok(x int)
func nosplit(x int)
func rodata(x int)
func noptr(x int)
func wrapper(x int)
func f15271() (x uint32)
func f17584(x float32, y complex64)
func noframe1(x int32)
func noframe2(x int32)
func fvariadic(int, ...int)

View File

@@ -0,0 +1,314 @@
// Copyright 2013 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.
// +build amd64
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), AX
// MOVB x+0(FP), AX // commented out instructions used to panic
MOVB y+1(FP), BX
MOVW x+0(FP), AX // want `\[amd64\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
MOVW y+1(FP), AX // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int8 is 1-byte value`
MOVL y+1(FP), AX // want `invalid MOVL of y\+1\(FP\); uint8 is 1-byte value`
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int8 is 1-byte value`
MOVQ y+1(FP), AX // want `invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value`
MOVB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
MOVB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
TESTB x+0(FP), AX
TESTB y+1(FP), BX
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int8 is 1-byte value`
TESTW y+1(FP), AX // want `invalid TESTW of y\+1\(FP\); uint8 is 1-byte value`
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int8 is 1-byte value`
TESTL y+1(FP), AX // want `invalid TESTL of y\+1\(FP\); uint8 is 1-byte value`
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int8 is 1-byte value`
TESTQ y+1(FP), AX // want `invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value`
TESTB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
TESTB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
MOVB 8(SP), AX // want `8\(SP\) should be x\+0\(FP\)`
MOVB 9(SP), AX // want `9\(SP\) should be y\+1\(FP\)`
MOVB 10(SP), AX // want `use of 10\(SP\) points beyond argument frame`
RET
TEXT ·arg2(SB),0,$0-4
MOVB x+0(FP), AX // want `arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value`
MOVB y+2(FP), AX // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
MOVW x+0(FP), AX
MOVW y+2(FP), BX
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int16 is 2-byte value`
MOVL y+2(FP), AX // want `invalid MOVL of y\+2\(FP\); uint16 is 2-byte value`
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int16 is 2-byte value`
MOVQ y+2(FP), AX // want `invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value`
MOVW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
MOVW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int16 is 2-byte value`
TESTB y+2(FP), AX // want `invalid TESTB of y\+2\(FP\); uint16 is 2-byte value`
TESTW x+0(FP), AX
TESTW y+2(FP), BX
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int16 is 2-byte value`
TESTL y+2(FP), AX // want `invalid TESTL of y\+2\(FP\); uint16 is 2-byte value`
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int16 is 2-byte value`
TESTQ y+2(FP), AX // want `invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value`
TESTW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
TESTW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
RET
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int32 is 4-byte value`
MOVW y+4(FP), AX // want `invalid MOVW of y\+4\(FP\); uint32 is 4-byte value`
MOVL x+0(FP), AX
MOVL y+4(FP), AX
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int32 is 4-byte value`
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value`
MOVL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int32 is 4-byte value`
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); uint32 is 4-byte value`
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int32 is 4-byte value`
TESTW y+4(FP), AX // want `invalid TESTW of y\+4\(FP\); uint32 is 4-byte value`
TESTL x+0(FP), AX
TESTL y+4(FP), AX
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int32 is 4-byte value`
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value`
TESTL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
TESTL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
RET
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int64 is 8-byte value`
MOVL y+8(FP), AX // want `invalid MOVL of y\+8\(FP\); uint64 is 8-byte value`
MOVQ x+0(FP), AX
MOVQ y+8(FP), AX
MOVQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int64 is 8-byte value`
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); uint64 is 8-byte value`
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int64 is 8-byte value`
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); uint64 is 8-byte value`
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int64 is 8-byte value`
TESTL y+8(FP), AX // want `invalid TESTL of y\+8\(FP\); uint64 is 8-byte value`
TESTQ x+0(FP), AX
TESTQ y+8(FP), AX
TESTQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
RET
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); uint is 8-byte value`
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int is 8-byte value`
MOVL y+8(FP), AX // want `invalid MOVL of y\+8\(FP\); uint is 8-byte value`
MOVQ x+0(FP), AX
MOVQ y+8(FP), AX
MOVQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int is 8-byte value`
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); uint is 8-byte value`
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int is 8-byte value`
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); uint is 8-byte value`
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int is 8-byte value`
TESTL y+8(FP), AX // want `invalid TESTL of y\+8\(FP\); uint is 8-byte value`
TESTQ x+0(FP), AX
TESTQ y+8(FP), AX
TESTQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
RET
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); \*byte is 8-byte value`
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); \*byte is 8-byte value`
MOVL y+8(FP), AX // want `invalid MOVL of y\+8\(FP\); \*byte is 8-byte value`
MOVQ x+0(FP), AX
MOVQ y+8(FP), AX
MOVQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); \*byte is 8-byte value`
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); \*byte is 8-byte value`
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); \*byte is 8-byte value`
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); \*byte is 8-byte value`
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); \*byte is 8-byte value`
TESTL y+8(FP), AX // want `invalid TESTL of y\+8\(FP\); \*byte is 8-byte value`
TESTQ x+0(FP), AX
TESTQ y+8(FP), AX
TESTQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
MOVL c+16(FP), AX // want `invalid MOVL of c\+16\(FP\); chan int is 8-byte value`
MOVL m+24(FP), AX // want `invalid MOVL of m\+24\(FP\); map\[int\]int is 8-byte value`
MOVL f+32(FP), AX // want `invalid MOVL of f\+32\(FP\); func\(\) is 8-byte value`
RET
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); string base is 8-byte value`
MOVQ x+0(FP), AX
MOVW x_base+0(FP), AX // want `invalid MOVW of x_base\+0\(FP\); string base is 8-byte value`
MOVL x_base+0(FP), AX // want `invalid MOVL of x_base\+0\(FP\); string base is 8-byte value`
MOVQ x_base+0(FP), AX
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVW x_len+8(FP), AX // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
MOVL x_len+8(FP), AX // want `invalid MOVL of x_len\+8\(FP\); string len is 8-byte value`
MOVQ x_len+8(FP), AX
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
MOVQ y_len+8(FP), AX // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
RET
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); slice base is 8-byte value`
MOVQ x+0(FP), AX
MOVW x_base+0(FP), AX // want `invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value`
MOVL x_base+0(FP), AX // want `invalid MOVL of x_base\+0\(FP\); slice base is 8-byte value`
MOVQ x_base+0(FP), AX
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVW x_len+8(FP), AX // want `invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value`
MOVL x_len+8(FP), AX // want `invalid MOVL of x_len\+8\(FP\); slice len is 8-byte value`
MOVQ x_len+8(FP), AX
MOVW x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVL x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVQ x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVW x_cap+16(FP), AX // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
MOVL x_cap+16(FP), AX // want `invalid MOVL of x_cap\+16\(FP\); slice cap is 8-byte value`
MOVQ x_cap+16(FP), AX
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
MOVQ y_len+8(FP), AX // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
MOVQ y_cap+16(FP), AX // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
RET
TEXT ·argiface(SB),0,$0-32
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); interface type is 8-byte value`
MOVQ x+0(FP), AX
MOVW x_type+0(FP), AX // want `invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value`
MOVL x_type+0(FP), AX // want `invalid MOVL of x_type\+0\(FP\); interface type is 8-byte value`
MOVQ x_type+0(FP), AX
MOVQ x_itable+0(FP), AX // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
MOVQ x_itable+1(FP), AX // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
MOVW x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVL x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVQ x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVW x_data+8(FP), AX // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
MOVL x_data+8(FP), AX // want `invalid MOVL of x_data\+8\(FP\); interface data is 8-byte value`
MOVQ x_data+8(FP), AX
MOVW y+16(FP), AX // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
MOVL y+16(FP), AX // want `invalid MOVL of y\+16\(FP\); interface itable is 8-byte value`
MOVQ y+16(FP), AX
MOVW y_itable+16(FP), AX // want `invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value`
MOVL y_itable+16(FP), AX // want `invalid MOVL of y_itable\+16\(FP\); interface itable is 8-byte value`
MOVQ y_itable+16(FP), AX
MOVQ y_type+16(FP), AX // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
MOVW y_data+16(FP), AX // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVL y_data+16(FP), AX // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVQ y_data+16(FP), AX // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVW y_data+24(FP), AX // want `invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value`
MOVL y_data+24(FP), AX // want `invalid MOVL of y_data\+24\(FP\); interface data is 8-byte value`
MOVQ y_data+24(FP), AX
RET
TEXT ·argcomplex(SB),0,$24 // want `wrong argument size 0; expected \$\.\.\.-24`
MOVSS x+0(FP), X0 // want `invalid MOVSS of x\+0\(FP\); complex64 is 8-byte value containing x_real\+0\(FP\) and x_imag\+4\(FP\)`
MOVSD x+0(FP), X0 // want `invalid MOVSD of x\+0\(FP\); complex64 is 8-byte value containing x_real\+0\(FP\) and x_imag\+4\(FP\)`
MOVSS x_real+0(FP), X0
MOVSD x_real+0(FP), X0 // want `invalid MOVSD of x_real\+0\(FP\); real\(complex64\) is 4-byte value`
MOVSS x_real+4(FP), X0 // want `invalid offset x_real\+4\(FP\); expected x_real\+0\(FP\)`
MOVSS x_imag+4(FP), X0
MOVSD x_imag+4(FP), X0 // want `invalid MOVSD of x_imag\+4\(FP\); imag\(complex64\) is 4-byte value`
MOVSS x_imag+8(FP), X0 // want `invalid offset x_imag\+8\(FP\); expected x_imag\+4\(FP\)`
MOVSD y+8(FP), X0 // want `invalid MOVSD of y\+8\(FP\); complex128 is 16-byte value containing y_real\+8\(FP\) and y_imag\+16\(FP\)`
MOVSS y_real+8(FP), X0 // want `invalid MOVSS of y_real\+8\(FP\); real\(complex128\) is 8-byte value`
MOVSD y_real+8(FP), X0
MOVSS y_real+16(FP), X0 // want `invalid offset y_real\+16\(FP\); expected y_real\+8\(FP\)`
MOVSS y_imag+16(FP), X0 // want `invalid MOVSS of y_imag\+16\(FP\); imag\(complex128\) is 8-byte value`
MOVSD y_imag+16(FP), X0
MOVSS y_imag+24(FP), X0 // want `invalid offset y_imag\+24\(FP\); expected y_imag\+16\(FP\)`
RET
TEXT ·argstruct(SB),0,$64 // want `wrong argument size 0; expected \$\.\.\.-24`
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); a.S is 24-byte value`
MOVQ x_i+0(FP), AX // want `invalid MOVQ of x_i\+0\(FP\); int32 is 4-byte value`
MOVQ x_b+0(FP), AX // want `invalid offset x_b\+0\(FP\); expected x_b\+4\(FP\)`
MOVQ x_s+8(FP), AX
MOVQ x_s_base+8(FP), AX
MOVQ x_s+16(FP), AX // want `invalid offset x_s\+16\(FP\); expected x_s\+8\(FP\), x_s_base\+8\(FP\), or x_s_len\+16\(FP\)`
MOVQ x_s_len+16(FP), AX
RET
TEXT ·argarray(SB),0,$64 // want `wrong argument size 0; expected \$\.\.\.-48`
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); \[2\]a.S is 48-byte value`
MOVQ x_0_i+0(FP), AX // want `invalid MOVQ of x_0_i\+0\(FP\); int32 is 4-byte value`
MOVQ x_0_b+0(FP), AX // want `invalid offset x_0_b\+0\(FP\); expected x_0_b\+4\(FP\)`
MOVQ x_0_s+8(FP), AX
MOVQ x_0_s_base+8(FP), AX
MOVQ x_0_s+16(FP), AX // want `invalid offset x_0_s\+16\(FP\); expected x_0_s\+8\(FP\), x_0_s_base\+8\(FP\), or x_0_s_len\+16\(FP\)`
MOVQ x_0_s_len+16(FP), AX
MOVB foo+25(FP), AX // want `unknown variable foo; offset 25 is x_1_i\+24\(FP\)`
MOVQ x_1_s+32(FP), AX
MOVQ x_1_s_base+32(FP), AX
MOVQ x_1_s+40(FP), AX // want `invalid offset x_1_s\+40\(FP\); expected x_1_s\+32\(FP\), x_1_s_base\+32\(FP\), or x_1_s_len\+40\(FP\)`
MOVQ x_1_s_len+40(FP), AX
RET
TEXT ·returnint(SB),0,$0-8
MOVB AX, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
MOVW AX, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
MOVL AX, ret+0(FP) // want `invalid MOVL of ret\+0\(FP\); int is 8-byte value`
MOVQ AX, ret+0(FP)
MOVQ AX, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
MOVQ AX, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
RET
TEXT ·returnbyte(SB),0,$0-9
MOVQ x+0(FP), AX
MOVB AX, ret+8(FP)
MOVW AX, ret+8(FP) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
MOVL AX, ret+8(FP) // want `invalid MOVL of ret\+8\(FP\); byte is 1-byte value`
MOVQ AX, ret+8(FP) // want `invalid MOVQ of ret\+8\(FP\); byte is 1-byte value`
MOVB AX, ret+7(FP) // want `invalid offset ret\+7\(FP\); expected ret\+8\(FP\)`
RET
TEXT ·returnnamed(SB),0,$0-41
MOVB x+0(FP), AX
MOVQ AX, r1+8(FP)
MOVW AX, r2+16(FP)
MOVQ AX, r3+24(FP)
MOVQ AX, r3_base+24(FP)
MOVQ AX, r3_len+32(FP)
MOVB AX, r4+40(FP)
MOVL AX, r1+8(FP) // want `invalid MOVL of r1\+8\(FP\); int is 8-byte value`
RET
TEXT ·returnintmissing(SB),0,$0-8
RET // want `RET without writing to 8-byte ret\+0\(FP\)`
// issue 15271
TEXT ·f15271(SB), NOSPLIT, $0-4
// Stick 123 into the low 32 bits of X0.
MOVQ $123, AX
PINSRD $0, AX, X0
// Return them.
PEXTRD $0, X0, x+0(FP)
RET
// issue 17584
TEXT ·f17584(SB), NOSPLIT, $12
MOVSS x+0(FP), X0
MOVSS y_real+4(FP), X0
MOVSS y_imag+8(FP), X0
RET

View File

@@ -0,0 +1,256 @@
// Copyright 2013 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.
// +build 386
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), AX
MOVB y+1(FP), BX
MOVW x+0(FP), AX // want `\[386\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
MOVW y+1(FP), AX // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int8 is 1-byte value`
MOVL y+1(FP), AX // want `invalid MOVL of y\+1\(FP\); uint8 is 1-byte value`
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int8 is 1-byte value`
MOVQ y+1(FP), AX // want `invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value`
MOVB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
MOVB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
TESTB x+0(FP), AX
TESTB y+1(FP), BX
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int8 is 1-byte value`
TESTW y+1(FP), AX // want `invalid TESTW of y\+1\(FP\); uint8 is 1-byte value`
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int8 is 1-byte value`
TESTL y+1(FP), AX // want `invalid TESTL of y\+1\(FP\); uint8 is 1-byte value`
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int8 is 1-byte value`
TESTQ y+1(FP), AX // want `invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value`
TESTB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
TESTB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
MOVB 4(SP), AX // want `4\(SP\) should be x\+0\(FP\)`
MOVB 5(SP), AX // want `5\(SP\) should be y\+1\(FP\)`
MOVB 6(SP), AX // want `use of 6\(SP\) points beyond argument frame`
RET
TEXT ·arg2(SB),0,$0-4
MOVB x+0(FP), AX // want `arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value`
MOVB y+2(FP), AX // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
MOVW x+0(FP), AX
MOVW y+2(FP), BX
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int16 is 2-byte value`
MOVL y+2(FP), AX // want `invalid MOVL of y\+2\(FP\); uint16 is 2-byte value`
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int16 is 2-byte value`
MOVQ y+2(FP), AX // want `invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value`
MOVW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
MOVW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int16 is 2-byte value`
TESTB y+2(FP), AX // want `invalid TESTB of y\+2\(FP\); uint16 is 2-byte value`
TESTW x+0(FP), AX
TESTW y+2(FP), BX
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int16 is 2-byte value`
TESTL y+2(FP), AX // want `invalid TESTL of y\+2\(FP\); uint16 is 2-byte value`
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int16 is 2-byte value`
TESTQ y+2(FP), AX // want `invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value`
TESTW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
TESTW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
RET
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int32 is 4-byte value`
MOVW y+4(FP), AX // want `invalid MOVW of y\+4\(FP\); uint32 is 4-byte value`
MOVL x+0(FP), AX
MOVL y+4(FP), AX
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int32 is 4-byte value`
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value`
MOVL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int32 is 4-byte value`
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); uint32 is 4-byte value`
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int32 is 4-byte value`
TESTW y+4(FP), AX // want `invalid TESTW of y\+4\(FP\); uint32 is 4-byte value`
TESTL x+0(FP), AX
TESTL y+4(FP), AX
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int32 is 4-byte value`
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value`
TESTL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
TESTL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
RET
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)`
MOVL x_lo+0(FP), AX
MOVL x_hi+4(FP), AX
MOVL y+8(FP), AX // want `invalid MOVL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)`
MOVL y_lo+8(FP), AX
MOVL y_hi+12(FP), AX
MOVQ x+0(FP), AX
MOVQ y+8(FP), AX
MOVQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int64 is 8-byte value`
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); uint64 is 8-byte value`
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int64 is 8-byte value`
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); uint64 is 8-byte value`
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)`
TESTL y+8(FP), AX // want `invalid TESTL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)`
TESTQ x+0(FP), AX
TESTQ y+8(FP), AX
TESTQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
RET
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-8`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int is 4-byte value`
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint is 4-byte value`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int is 4-byte value`
MOVW y+4(FP), AX // want `invalid MOVW of y\+4\(FP\); uint is 4-byte value`
MOVL x+0(FP), AX
MOVL y+4(FP), AX
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int is 4-byte value`
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); uint is 4-byte value`
MOVQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int is 4-byte value`
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); uint is 4-byte value`
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int is 4-byte value`
TESTW y+4(FP), AX // want `invalid TESTW of y\+4\(FP\); uint is 4-byte value`
TESTL x+0(FP), AX
TESTL y+4(FP), AX
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int is 4-byte value`
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); uint is 4-byte value`
TESTQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
RET
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-20`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); \*byte is 4-byte value`
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); \*byte is 4-byte value`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); \*byte is 4-byte value`
MOVW y+4(FP), AX // want `invalid MOVW of y\+4\(FP\); \*byte is 4-byte value`
MOVL x+0(FP), AX
MOVL y+4(FP), AX
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); \*byte is 4-byte value`
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); \*byte is 4-byte value`
MOVQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); \*byte is 4-byte value`
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); \*byte is 4-byte value`
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); \*byte is 4-byte value`
TESTW y+4(FP), AX // want `invalid TESTW of y\+4\(FP\); \*byte is 4-byte value`
TESTL x+0(FP), AX
TESTL y+4(FP), AX
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); \*byte is 4-byte value`
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); \*byte is 4-byte value`
TESTQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
MOVW c+8(FP), AX // want `invalid MOVW of c\+8\(FP\); chan int is 4-byte value`
MOVW m+12(FP), AX // want `invalid MOVW of m\+12\(FP\); map\[int\]int is 4-byte value`
MOVW f+16(FP), AX // want `invalid MOVW of f\+16\(FP\); func\(\) is 4-byte value`
RET
TEXT ·argstring(SB),0,$16 // want `wrong argument size 0; expected \$\.\.\.-16`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); string base is 4-byte value`
MOVL x+0(FP), AX
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); string base is 4-byte value`
MOVW x_base+0(FP), AX // want `invalid MOVW of x_base\+0\(FP\); string base is 4-byte value`
MOVL x_base+0(FP), AX
MOVQ x_base+0(FP), AX // want `invalid MOVQ of x_base\+0\(FP\); string base is 4-byte value`
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVW x_len+4(FP), AX // want `invalid MOVW of x_len\+4\(FP\); string len is 4-byte value`
MOVL x_len+4(FP), AX
MOVQ x_len+4(FP), AX // want `invalid MOVQ of x_len\+4\(FP\); string len is 4-byte value`
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+8\(FP\)`
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)`
RET
TEXT ·argslice(SB),0,$24 // want `wrong argument size 0; expected \$\.\.\.-24`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); slice base is 4-byte value`
MOVL x+0(FP), AX
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); slice base is 4-byte value`
MOVW x_base+0(FP), AX // want `invalid MOVW of x_base\+0\(FP\); slice base is 4-byte value`
MOVL x_base+0(FP), AX
MOVQ x_base+0(FP), AX // want `invalid MOVQ of x_base\+0\(FP\); slice base is 4-byte value`
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVW x_len+4(FP), AX // want `invalid MOVW of x_len\+4\(FP\); slice len is 4-byte value`
MOVL x_len+4(FP), AX
MOVQ x_len+4(FP), AX // want `invalid MOVQ of x_len\+4\(FP\); slice len is 4-byte value`
MOVW x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
MOVL x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
MOVQ x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
MOVW x_cap+8(FP), AX // want `invalid MOVW of x_cap\+8\(FP\); slice cap is 4-byte value`
MOVL x_cap+8(FP), AX
MOVQ x_cap+8(FP), AX // want `invalid MOVQ of x_cap\+8\(FP\); slice cap is 4-byte value`
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+12\(FP\)`
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)`
MOVQ y_cap+8(FP), AX // want `invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)`
RET
TEXT ·argiface(SB),0,$0-16
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); interface type is 4-byte value`
MOVL x+0(FP), AX
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); interface type is 4-byte value`
MOVW x_type+0(FP), AX // want `invalid MOVW of x_type\+0\(FP\); interface type is 4-byte value`
MOVL x_type+0(FP), AX
MOVQ x_type+0(FP), AX // want `invalid MOVQ of x_type\+0\(FP\); interface type is 4-byte value`
MOVQ x_itable+0(FP), AX // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
MOVQ x_itable+1(FP), AX // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
MOVW x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
MOVL x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
MOVQ x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
MOVW x_data+4(FP), AX // want `invalid MOVW of x_data\+4\(FP\); interface data is 4-byte value`
MOVL x_data+4(FP), AX
MOVQ x_data+4(FP), AX // want `invalid MOVQ of x_data\+4\(FP\); interface data is 4-byte value`
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); interface itable is 4-byte value`
MOVL y+8(FP), AX
MOVQ y+8(FP), AX // want `invalid MOVQ of y\+8\(FP\); interface itable is 4-byte value`
MOVW y_itable+8(FP), AX // want `invalid MOVW of y_itable\+8\(FP\); interface itable is 4-byte value`
MOVL y_itable+8(FP), AX
MOVQ y_itable+8(FP), AX // want `invalid MOVQ of y_itable\+8\(FP\); interface itable is 4-byte value`
MOVQ y_type+8(FP), AX // want `unknown variable y_type; offset 8 is y_itable\+8\(FP\)`
MOVW y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
MOVL y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
MOVQ y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
MOVW y_data+12(FP), AX // want `invalid MOVW of y_data\+12\(FP\); interface data is 4-byte value`
MOVL y_data+12(FP), AX
MOVQ y_data+12(FP), AX // want `invalid MOVQ of y_data\+12\(FP\); interface data is 4-byte value`
RET
TEXT ·returnint(SB),0,$0-4
MOVB AX, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 4-byte value`
MOVW AX, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 4-byte value`
MOVL AX, ret+0(FP)
MOVQ AX, ret+0(FP) // want `invalid MOVQ of ret\+0\(FP\); int is 4-byte value`
MOVQ AX, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
MOVQ AX, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
RET
TEXT ·returnbyte(SB),0,$0-5
MOVL x+0(FP), AX
MOVB AX, ret+4(FP)
MOVW AX, ret+4(FP) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
MOVL AX, ret+4(FP) // want `invalid MOVL of ret\+4\(FP\); byte is 1-byte value`
MOVQ AX, ret+4(FP) // want `invalid MOVQ of ret\+4\(FP\); byte is 1-byte value`
MOVB AX, ret+3(FP) // want `invalid offset ret\+3\(FP\); expected ret\+4\(FP\)`
RET
TEXT ·returnnamed(SB),0,$0-21
MOVB x+0(FP), AX
MOVL AX, r1+4(FP)
MOVW AX, r2+8(FP)
MOVL AX, r3+12(FP)
MOVL AX, r3_base+12(FP)
MOVL AX, r3_len+16(FP)
MOVB AX, r4+20(FP)
MOVQ AX, r1+4(FP) // want `invalid MOVQ of r1\+4\(FP\); int is 4-byte value`
RET
TEXT ·returnintmissing(SB),0,$0-4
RET // want `RET without writing to 4-byte ret\+0\(FP\)`

View File

@@ -0,0 +1,191 @@
// Copyright 2013 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.
// +build arm
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), AX
MOVB y+1(FP), BX
MOVH x+0(FP), AX // want `\[arm\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
MOVH y+1(FP), AX // want `invalid MOVH of y\+1\(FP\); uint8 is 1-byte value`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
MOVW y+1(FP), AX // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
MOVB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
MOVB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
MOVB 8(R13), AX // want `8\(R13\) should be x\+0\(FP\)`
MOVB 9(R13), AX // want `9\(R13\) should be y\+1\(FP\)`
MOVB 10(R13), AX // want `use of 10\(R13\) points beyond argument frame`
RET
TEXT ·arg2(SB),0,$0-4
MOVB x+0(FP), AX // want `arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value`
MOVB y+2(FP), AX // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
MOVH x+0(FP), AX
MOVH y+2(FP), BX
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int16 is 2-byte value`
MOVW y+2(FP), AX // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
MOVH x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
MOVH y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
RET
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
MOVH y+4(FP), AX // want `invalid MOVH of y\+4\(FP\); uint32 is 4-byte value`
MOVW x+0(FP), AX
MOVW y+4(FP), AX
MOVW x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVW y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
RET
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
MOVH y+8(FP), AX // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)`
MOVW x_lo+0(FP), AX
MOVW x_hi+4(FP), AX
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)`
MOVW y_lo+8(FP), AX
MOVW y_hi+12(FP), AX
MOVQ x+0(FP), AX
MOVQ y+8(FP), AX
MOVQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
RET
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-8`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int is 4-byte value`
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint is 4-byte value`
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); int is 4-byte value`
MOVH y+4(FP), AX // want `invalid MOVH of y\+4\(FP\); uint is 4-byte value`
MOVW x+0(FP), AX
MOVW y+4(FP), AX
MOVQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
RET
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-20`
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); \*byte is 4-byte value`
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); \*byte is 4-byte value`
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); \*byte is 4-byte value`
MOVH y+4(FP), AX // want `invalid MOVH of y\+4\(FP\); \*byte is 4-byte value`
MOVW x+0(FP), AX
MOVW y+4(FP), AX
MOVQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
MOVH c+8(FP), AX // want `invalid MOVH of c\+8\(FP\); chan int is 4-byte value`
MOVH m+12(FP), AX // want `invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value`
MOVH f+16(FP), AX // want `invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value`
RET
TEXT ·argstring(SB),0,$16 // want `wrong argument size 0; expected \$\.\.\.-16`
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); string base is 4-byte value`
MOVW x+0(FP), AX
MOVH x_base+0(FP), AX // want `invalid MOVH of x_base\+0\(FP\); string base is 4-byte value`
MOVW x_base+0(FP), AX
MOVH x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVH x_len+4(FP), AX // want `invalid MOVH of x_len\+4\(FP\); string len is 4-byte value`
MOVW x_len+4(FP), AX
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+8\(FP\)`
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)`
RET
TEXT ·argslice(SB),0,$24 // want `wrong argument size 0; expected \$\.\.\.-24`
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); slice base is 4-byte value`
MOVW x+0(FP), AX
MOVH x_base+0(FP), AX // want `invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value`
MOVW x_base+0(FP), AX
MOVH x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVH x_len+4(FP), AX // want `invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value`
MOVW x_len+4(FP), AX
MOVH x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
MOVW x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
MOVQ x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
MOVH x_cap+8(FP), AX // want `invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value`
MOVW x_cap+8(FP), AX
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+12\(FP\)`
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)`
MOVQ y_cap+8(FP), AX // want `invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)`
RET
TEXT ·argiface(SB),0,$0-16
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); interface type is 4-byte value`
MOVW x+0(FP), AX
MOVH x_type+0(FP), AX // want `invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value`
MOVW x_type+0(FP), AX
MOVQ x_itable+0(FP), AX // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
MOVQ x_itable+1(FP), AX // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
MOVH x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
MOVW x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
MOVQ x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
MOVH x_data+4(FP), AX // want `invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value`
MOVW x_data+4(FP), AX
MOVH y+8(FP), AX // want `invalid MOVH of y\+8\(FP\); interface itable is 4-byte value`
MOVW y+8(FP), AX
MOVH y_itable+8(FP), AX // want `invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value`
MOVW y_itable+8(FP), AX
MOVQ y_type+8(FP), AX // want `unknown variable y_type; offset 8 is y_itable\+8\(FP\)`
MOVH y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
MOVW y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
MOVQ y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
MOVH y_data+12(FP), AX // want `invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value`
MOVW y_data+12(FP), AX
RET
TEXT ·returnint(SB),0,$0-4
MOVB AX, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 4-byte value`
MOVH AX, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 4-byte value`
MOVW AX, ret+0(FP)
MOVQ AX, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
MOVQ AX, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
RET
TEXT ·returnbyte(SB),0,$0-5
MOVW x+0(FP), AX
MOVB AX, ret+4(FP)
MOVH AX, ret+4(FP) // want `invalid MOVH of ret\+4\(FP\); byte is 1-byte value`
MOVW AX, ret+4(FP) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
MOVB AX, ret+3(FP) // want `invalid offset ret\+3\(FP\); expected ret\+4\(FP\)`
RET
TEXT ·returnnamed(SB),0,$0-21
MOVB x+0(FP), AX
MOVW AX, r1+4(FP)
MOVH AX, r2+8(FP)
MOVW AX, r3+12(FP)
MOVW AX, r3_base+12(FP)
MOVW AX, r3_len+16(FP)
MOVB AX, r4+20(FP)
MOVB AX, r1+4(FP) // want `invalid MOVB of r1\+4\(FP\); int is 4-byte value`
RET
TEXT ·returnintmissing(SB),0,$0-4
RET // want `RET without writing to 4-byte ret\+0\(FP\)`
TEXT ·leaf(SB),0,$-4-12
MOVW x+0(FP), AX
MOVW y+4(FP), AX
MOVW AX, ret+8(FP)
RET
TEXT ·noframe1(SB),0,$0-4
MOVW 0(R13), AX // Okay; our saved LR
MOVW 4(R13), AX // Okay; caller's saved LR
MOVW x+8(R13), AX // Okay; x argument
MOVW 12(R13), AX // want `use of 12\(R13\) points beyond argument frame`
RET
TEXT ·noframe2(SB),NOFRAME,$0-4
MOVW 0(R13), AX // Okay; caller's saved LR
MOVW x+4(R13), AX // Okay; x argument
MOVW 8(R13), AX // want `use of 8\(R13\) points beyond argument frame`
MOVW 12(R13), AX // want `use of 12\(R13\) points beyond argument frame`
RET

View File

@@ -0,0 +1,25 @@
// Copyright 2013 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.
// +build amd64
// Test cases for symbolic NOSPLIT etc. on TEXT symbols.
TEXT ·noprof(SB),NOPROF,$0-8
RET
TEXT ·dupok(SB),DUPOK,$0-8
RET
TEXT ·nosplit(SB),NOSPLIT,$0
RET
TEXT ·rodata(SB),RODATA,$0-8
RET
TEXT ·noptr(SB),NOPTR|NOSPLIT,$0
RET
TEXT ·wrapper(SB),WRAPPER,$0-8
RET

View File

@@ -0,0 +1,192 @@
// 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.
// +build mips64
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), R1
MOVBU y+1(FP), R2
MOVH x+0(FP), R1 // want `\[mips64\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
MOVHU y+1(FP), R1 // want `invalid MOVHU of y\+1\(FP\); uint8 is 1-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
MOVWU y+1(FP), R1 // want `invalid MOVWU of y\+1\(FP\); uint8 is 1-byte value`
MOVV x+0(FP), R1 // want `invalid MOVV of x\+0\(FP\); int8 is 1-byte value`
MOVV y+1(FP), R1 // want `invalid MOVV of y\+1\(FP\); uint8 is 1-byte value`
MOVB x+1(FP), R1 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
MOVBU y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
MOVB 16(R29), R1 // want `16\(R29\) should be x\+0\(FP\)`
MOVB 17(R29), R1 // want `17\(R29\) should be y\+1\(FP\)`
MOVB 18(R29), R1 // want `use of 18\(R29\) points beyond argument frame`
RET
TEXT ·arg2(SB),0,$0-4
MOVBU x+0(FP), R1 // want `arg2: invalid MOVBU of x\+0\(FP\); int16 is 2-byte value`
MOVB y+2(FP), R1 // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
MOVHU x+0(FP), R1
MOVH y+2(FP), R2
MOVWU x+0(FP), R1 // want `invalid MOVWU of x\+0\(FP\); int16 is 2-byte value`
MOVW y+2(FP), R1 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
MOVV x+0(FP), R1 // want `invalid MOVV of x\+0\(FP\); int16 is 2-byte value`
MOVV y+2(FP), R1 // want `invalid MOVV of y\+2\(FP\); uint16 is 2-byte value`
MOVHU x+2(FP), R1 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
MOVH y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
RET
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
MOVH y+4(FP), R1 // want `invalid MOVH of y\+4\(FP\); uint32 is 4-byte value`
MOVW x+0(FP), R1
MOVW y+4(FP), R1
MOVV x+0(FP), R1 // want `invalid MOVV of x\+0\(FP\); int32 is 4-byte value`
MOVV y+4(FP), R1 // want `invalid MOVV of y\+4\(FP\); uint32 is 4-byte value`
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
RET
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
MOVV x+0(FP), R1
MOVV y+8(FP), R1
MOVV x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVV y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
RET
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int is 8-byte value`
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); uint is 8-byte value`
MOVV x+0(FP), R1
MOVV y+8(FP), R1
MOVV x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVV y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
RET
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); \*byte is 8-byte value`
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); \*byte is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); \*byte is 8-byte value`
MOVV x+0(FP), R1
MOVV y+8(FP), R1
MOVV x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVV y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
MOVW c+16(FP), R1 // want `invalid MOVW of c\+16\(FP\); chan int is 8-byte value`
MOVW m+24(FP), R1 // want `invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value`
MOVW f+32(FP), R1 // want `invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value`
RET
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); string base is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
MOVV x+0(FP), R1
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); string base is 8-byte value`
MOVW x_base+0(FP), R1 // want `invalid MOVW of x_base\+0\(FP\); string base is 8-byte value`
MOVV x_base+0(FP), R1
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVV x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); string len is 8-byte value`
MOVW x_len+8(FP), R1 // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
MOVV x_len+8(FP), R1
MOVV y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
MOVV y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
RET
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); slice base is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
MOVV x+0(FP), R1
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value`
MOVW x_base+0(FP), R1 // want `invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value`
MOVV x_base+0(FP), R1
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVV x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value`
MOVW x_len+8(FP), R1 // want `invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value`
MOVV x_len+8(FP), R1
MOVH x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVW x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVV x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVH x_cap+16(FP), R1 // want `invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value`
MOVW x_cap+16(FP), R1 // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
MOVV x_cap+16(FP), R1
MOVV y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
MOVV y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
MOVV y_cap+16(FP), R1 // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
RET
TEXT ·argiface(SB),0,$0-32
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); interface type is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
MOVV x+0(FP), R1
MOVH x_type+0(FP), R1 // want `invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value`
MOVW x_type+0(FP), R1 // want `invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value`
MOVV x_type+0(FP), R1
MOVV x_itable+0(FP), R1 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
MOVV x_itable+1(FP), R1 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
MOVH x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVW x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVV x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVH x_data+8(FP), R1 // want `invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value`
MOVW x_data+8(FP), R1 // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
MOVV x_data+8(FP), R1
MOVH y+16(FP), R1 // want `invalid MOVH of y\+16\(FP\); interface itable is 8-byte value`
MOVW y+16(FP), R1 // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
MOVV y+16(FP), R1
MOVH y_itable+16(FP), R1 // want `invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value`
MOVW y_itable+16(FP), R1 // want `invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value`
MOVV y_itable+16(FP), R1
MOVV y_type+16(FP), R1 // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
MOVH y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVW y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVV y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVH y_data+24(FP), R1 // want `invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value`
MOVW y_data+24(FP), R1 // want `invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value`
MOVV y_data+24(FP), R1
RET
TEXT ·returnint(SB),0,$0-8
MOVB R1, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
MOVH R1, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 8-byte value`
MOVW R1, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
MOVV R1, ret+0(FP)
MOVV R1, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
MOVV R1, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
RET
TEXT ·returnbyte(SB),0,$0-9
MOVV x+0(FP), R1
MOVB R1, ret+8(FP)
MOVH R1, ret+8(FP) // want `invalid MOVH of ret\+8\(FP\); byte is 1-byte value`
MOVW R1, ret+8(FP) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
MOVV R1, ret+8(FP) // want `invalid MOVV of ret\+8\(FP\); byte is 1-byte value`
MOVB R1, ret+7(FP) // want `invalid offset ret\+7\(FP\); expected ret\+8\(FP\)`
RET
TEXT ·returnnamed(SB),0,$0-41
MOVB x+0(FP), R1
MOVV R1, r1+8(FP)
MOVH R1, r2+16(FP)
MOVV R1, r3+24(FP)
MOVV R1, r3_base+24(FP)
MOVV R1, r3_len+32(FP)
MOVB R1, r4+40(FP)
MOVW R1, r1+8(FP) // want `invalid MOVW of r1\+8\(FP\); int is 8-byte value`
RET
TEXT ·returnintmissing(SB),0,$0-8
RET // want `RET without writing to 8-byte ret\+0\(FP\)`

View File

@@ -0,0 +1,192 @@
// 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.
// +build s390x
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), R1
MOVBZ y+1(FP), R2
MOVH x+0(FP), R1 // want `\[s390x\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
MOVHZ y+1(FP), R1 // want `invalid MOVHZ of y\+1\(FP\); uint8 is 1-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
MOVWZ y+1(FP), R1 // want `invalid MOVWZ of y\+1\(FP\); uint8 is 1-byte value`
MOVD x+0(FP), R1 // want `invalid MOVD of x\+0\(FP\); int8 is 1-byte value`
MOVD y+1(FP), R1 // want `invalid MOVD of y\+1\(FP\); uint8 is 1-byte value`
MOVB x+1(FP), R1 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
MOVBZ y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
MOVB 16(R15), R1 // want `16\(R15\) should be x\+0\(FP\)`
MOVB 17(R15), R1 // want `17\(R15\) should be y\+1\(FP\)`
MOVB 18(R15), R1 // want `use of 18\(R15\) points beyond argument frame`
RET
TEXT ·arg2(SB),0,$0-4
MOVBZ x+0(FP), R1 // want `arg2: invalid MOVBZ of x\+0\(FP\); int16 is 2-byte value`
MOVB y+2(FP), R1 // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
MOVHZ x+0(FP), R1
MOVH y+2(FP), R2
MOVWZ x+0(FP), R1 // want `invalid MOVWZ of x\+0\(FP\); int16 is 2-byte value`
MOVW y+2(FP), R1 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
MOVD x+0(FP), R1 // want `invalid MOVD of x\+0\(FP\); int16 is 2-byte value`
MOVD y+2(FP), R1 // want `invalid MOVD of y\+2\(FP\); uint16 is 2-byte value`
MOVHZ x+2(FP), R1 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
MOVH y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
RET
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
MOVH y+4(FP), R1 // want `invalid MOVH of y\+4\(FP\); uint32 is 4-byte value`
MOVW x+0(FP), R1
MOVW y+4(FP), R1
MOVD x+0(FP), R1 // want `invalid MOVD of x\+0\(FP\); int32 is 4-byte value`
MOVD y+4(FP), R1 // want `invalid MOVD of y\+4\(FP\); uint32 is 4-byte value`
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
RET
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
MOVD x+0(FP), R1
MOVD y+8(FP), R1
MOVD x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVD y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
RET
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int is 8-byte value`
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); uint is 8-byte value`
MOVD x+0(FP), R1
MOVD y+8(FP), R1
MOVD x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVD y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
RET
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); \*byte is 8-byte value`
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); \*byte is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); \*byte is 8-byte value`
MOVD x+0(FP), R1
MOVD y+8(FP), R1
MOVD x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVD y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
MOVW c+16(FP), R1 // want `invalid MOVW of c\+16\(FP\); chan int is 8-byte value`
MOVW m+24(FP), R1 // want `invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value`
MOVW f+32(FP), R1 // want `invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value`
RET
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); string base is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
MOVD x+0(FP), R1
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); string base is 8-byte value`
MOVW x_base+0(FP), R1 // want `invalid MOVW of x_base\+0\(FP\); string base is 8-byte value`
MOVD x_base+0(FP), R1
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVD x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); string len is 8-byte value`
MOVW x_len+8(FP), R1 // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
MOVD x_len+8(FP), R1
MOVD y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
MOVD y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
RET
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); slice base is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
MOVD x+0(FP), R1
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value`
MOVW x_base+0(FP), R1 // want `invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value`
MOVD x_base+0(FP), R1
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVD x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value`
MOVW x_len+8(FP), R1 // want `invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value`
MOVD x_len+8(FP), R1
MOVH x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVW x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVD x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVH x_cap+16(FP), R1 // want `invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value`
MOVW x_cap+16(FP), R1 // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
MOVD x_cap+16(FP), R1
MOVD y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
MOVD y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
MOVD y_cap+16(FP), R1 // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
RET
TEXT ·argiface(SB),0,$0-32
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); interface type is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
MOVD x+0(FP), R1
MOVH x_type+0(FP), R1 // want `invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value`
MOVW x_type+0(FP), R1 // want `invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value`
MOVD x_type+0(FP), R1
MOVD x_itable+0(FP), R1 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
MOVD x_itable+1(FP), R1 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
MOVH x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVW x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVD x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVH x_data+8(FP), R1 // want `invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value`
MOVW x_data+8(FP), R1 // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
MOVD x_data+8(FP), R1
MOVH y+16(FP), R1 // want `invalid MOVH of y\+16\(FP\); interface itable is 8-byte value`
MOVW y+16(FP), R1 // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
MOVD y+16(FP), R1
MOVH y_itable+16(FP), R1 // want `invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value`
MOVW y_itable+16(FP), R1 // want `invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value`
MOVD y_itable+16(FP), R1
MOVD y_type+16(FP), R1 // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
MOVH y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVW y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVD y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVH y_data+24(FP), R1 // want `invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value`
MOVW y_data+24(FP), R1 // want `invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value`
MOVD y_data+24(FP), R1
RET
TEXT ·returnint(SB),0,$0-8
MOVB R1, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
MOVH R1, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 8-byte value`
MOVW R1, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
MOVD R1, ret+0(FP)
MOVD R1, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
MOVD R1, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
RET
TEXT ·returnbyte(SB),0,$0-9
MOVD x+0(FP), R1
MOVB R1, ret+8(FP)
MOVH R1, ret+8(FP) // want `invalid MOVH of ret\+8\(FP\); byte is 1-byte value`
MOVW R1, ret+8(FP) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
MOVD R1, ret+8(FP) // want `invalid MOVD of ret\+8\(FP\); byte is 1-byte value`
MOVB R1, ret+7(FP) // want `invalid offset ret\+7\(FP\); expected ret\+8\(FP\)`
RET
TEXT ·returnnamed(SB),0,$0-41
MOVB x+0(FP), R1
MOVD R1, r1+8(FP)
MOVH R1, r2+16(FP)
MOVD R1, r3+24(FP)
MOVD R1, r3_base+24(FP)
MOVD R1, r3_len+32(FP)
MOVB R1, r4+40(FP)
MOVW R1, r1+8(FP) // want `invalid MOVW of r1\+8\(FP\); int is 8-byte value`
RET
TEXT ·returnintmissing(SB),0,$0-8
RET // want `RET without writing to 8-byte ret\+0\(FP\)`

View File

@@ -0,0 +1,192 @@
// 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.
// +build ppc64 ppc64le
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), R3
MOVBZ y+1(FP), R4
MOVH x+0(FP), R3 // want `\[(ppc64|ppc64le)\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
MOVHZ y+1(FP), R3 // want `invalid MOVHZ of y\+1\(FP\); uint8 is 1-byte value`
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
MOVWZ y+1(FP), R3 // want `invalid MOVWZ of y\+1\(FP\); uint8 is 1-byte value`
MOVD x+0(FP), R3 // want `invalid MOVD of x\+0\(FP\); int8 is 1-byte value`
MOVD y+1(FP), R3 // want `invalid MOVD of y\+1\(FP\); uint8 is 1-byte value`
MOVB x+1(FP), R3 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
MOVBZ y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
MOVB 16(R1), R3 // want `16\(R1\) should be x\+0\(FP\)`
MOVB 17(R1), R3 // want `17\(R1\) should be y\+1\(FP\)`
MOVB 18(R1), R3 // want `use of 18\(R1\) points beyond argument frame`
RET
TEXT ·arg2(SB),0,$0-4
MOVBZ x+0(FP), R3 // want `arg2: invalid MOVBZ of x\+0\(FP\); int16 is 2-byte value`
MOVB y+2(FP), R3 // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
MOVHZ x+0(FP), R3
MOVH y+2(FP), R4
MOVWZ x+0(FP), R3 // want `invalid MOVWZ of x\+0\(FP\); int16 is 2-byte value`
MOVW y+2(FP), R3 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
MOVD x+0(FP), R3 // want `invalid MOVD of x\+0\(FP\); int16 is 2-byte value`
MOVD y+2(FP), R3 // want `invalid MOVD of y\+2\(FP\); uint16 is 2-byte value`
MOVHZ x+2(FP), R3 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
MOVH y+0(FP), R3 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
RET
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
MOVB y+4(FP), R4 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
MOVH y+4(FP), R3 // want `invalid MOVH of y\+4\(FP\); uint32 is 4-byte value`
MOVW x+0(FP), R3
MOVW y+4(FP), R3
MOVD x+0(FP), R3 // want `invalid MOVD of x\+0\(FP\); int32 is 4-byte value`
MOVD y+4(FP), R3 // want `invalid MOVD of y\+4\(FP\); uint32 is 4-byte value`
MOVW x+4(FP), R3 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVW y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
RET
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
MOVB y+8(FP), R4 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
MOVH y+8(FP), R3 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
MOVW y+8(FP), R3 // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
MOVD x+0(FP), R3
MOVD y+8(FP), R3
MOVD x+8(FP), R3 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVD y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
RET
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
MOVB y+8(FP), R4 // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); int is 8-byte value`
MOVH y+8(FP), R3 // want `invalid MOVH of y\+8\(FP\); uint is 8-byte value`
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
MOVW y+8(FP), R3 // want `invalid MOVW of y\+8\(FP\); uint is 8-byte value`
MOVD x+0(FP), R3
MOVD y+8(FP), R3
MOVD x+8(FP), R3 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVD y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
RET
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
MOVB y+8(FP), R4 // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); \*byte is 8-byte value`
MOVH y+8(FP), R3 // want `invalid MOVH of y\+8\(FP\); \*byte is 8-byte value`
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
MOVW y+8(FP), R3 // want `invalid MOVW of y\+8\(FP\); \*byte is 8-byte value`
MOVD x+0(FP), R3
MOVD y+8(FP), R3
MOVD x+8(FP), R3 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
MOVD y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
MOVW c+16(FP), R3 // want `invalid MOVW of c\+16\(FP\); chan int is 8-byte value`
MOVW m+24(FP), R3 // want `invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value`
MOVW f+32(FP), R3 // want `invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value`
RET
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); string base is 8-byte value`
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
MOVD x+0(FP), R3
MOVH x_base+0(FP), R3 // want `invalid MOVH of x_base\+0\(FP\); string base is 8-byte value`
MOVW x_base+0(FP), R3 // want `invalid MOVW of x_base\+0\(FP\); string base is 8-byte value`
MOVD x_base+0(FP), R3
MOVH x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVW x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVD x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVH x_len+8(FP), R3 // want `invalid MOVH of x_len\+8\(FP\); string len is 8-byte value`
MOVW x_len+8(FP), R3 // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
MOVD x_len+8(FP), R3
MOVD y+0(FP), R3 // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
MOVD y_len+8(FP), R3 // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
RET
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); slice base is 8-byte value`
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
MOVD x+0(FP), R3
MOVH x_base+0(FP), R3 // want `invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value`
MOVW x_base+0(FP), R3 // want `invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value`
MOVD x_base+0(FP), R3
MOVH x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVW x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVD x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
MOVH x_len+8(FP), R3 // want `invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value`
MOVW x_len+8(FP), R3 // want `invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value`
MOVD x_len+8(FP), R3
MOVH x_cap+0(FP), R3 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVW x_cap+0(FP), R3 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVD x_cap+0(FP), R3 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
MOVH x_cap+16(FP), R3 // want `invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value`
MOVW x_cap+16(FP), R3 // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
MOVD x_cap+16(FP), R3
MOVD y+0(FP), R3 // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
MOVD y_len+8(FP), R3 // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
MOVD y_cap+16(FP), R3 // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
RET
TEXT ·argiface(SB),0,$0-32
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); interface type is 8-byte value`
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
MOVD x+0(FP), R3
MOVH x_type+0(FP), R3 // want `invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value`
MOVW x_type+0(FP), R3 // want `invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value`
MOVD x_type+0(FP), R3
MOVD x_itable+0(FP), R3 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
MOVD x_itable+1(FP), R3 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
MOVH x_data+0(FP), R3 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVW x_data+0(FP), R3 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVD x_data+0(FP), R3 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
MOVH x_data+8(FP), R3 // want `invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value`
MOVW x_data+8(FP), R3 // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
MOVD x_data+8(FP), R3
MOVH y+16(FP), R3 // want `invalid MOVH of y\+16\(FP\); interface itable is 8-byte value`
MOVW y+16(FP), R3 // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
MOVD y+16(FP), R3
MOVH y_itable+16(FP), R3 // want `invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value`
MOVW y_itable+16(FP), R3 // want `invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value`
MOVD y_itable+16(FP), R3
MOVD y_type+16(FP), R3 // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
MOVH y_data+16(FP), R3 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVW y_data+16(FP), R3 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVD y_data+16(FP), R3 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
MOVH y_data+24(FP), R3 // want `invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value`
MOVW y_data+24(FP), R3 // want `invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value`
MOVD y_data+24(FP), R3
RET
TEXT ·returnint(SB),0,$0-8
MOVB R3, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
MOVH R3, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 8-byte value`
MOVW R3, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
MOVD R3, ret+0(FP)
MOVD R3, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
MOVD R3, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
RET
TEXT ·returnbyte(SB),0,$0-9
MOVD x+0(FP), R3
MOVB R3, ret+8(FP)
MOVH R3, ret+8(FP) // want `invalid MOVH of ret\+8\(FP\); byte is 1-byte value`
MOVW R3, ret+8(FP) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
MOVD R3, ret+8(FP) // want `invalid MOVD of ret\+8\(FP\); byte is 1-byte value`
MOVB R3, ret+7(FP) // want `invalid offset ret\+7\(FP\); expected ret\+8\(FP\)`
RET
TEXT ·returnnamed(SB),0,$0-41
MOVB x+0(FP), R3
MOVD R3, r1+8(FP)
MOVH R3, r2+16(FP)
MOVD R3, r3+24(FP)
MOVD R3, r3_base+24(FP)
MOVD R3, r3_len+32(FP)
MOVB R3, r4+40(FP)
MOVW R3, r1+8(FP) // want `invalid MOVW of r1\+8\(FP\); int is 8-byte value`
RET
TEXT ·returnintmissing(SB),0,$0-8
RET // want `RET without writing to 8-byte ret\+0\(FP\)`

View File

@@ -0,0 +1,164 @@
// 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.
// +build mipsle
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), R1
MOVBU y+1(FP), R2
MOVH x+0(FP), R1 // want `\[mipsle\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
MOVHU y+1(FP), R1 // want `invalid MOVHU of y\+1\(FP\); uint8 is 1-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
MOVWU y+1(FP), R1 // want `invalid MOVWU of y\+1\(FP\); uint8 is 1-byte value`
MOVW y+1(FP), R1 // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
MOVB x+1(FP), R1 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
MOVBU y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
MOVB 8(R29), R1 // want `8\(R29\) should be x\+0\(FP\)`
MOVB 9(R29), R1 // want `9\(R29\) should be y\+1\(FP\)`
MOVB 10(R29), R1 // want `use of 10\(R29\) points beyond argument frame`
RET
TEXT ·arg2(SB),0,$0-4
MOVBU x+0(FP), R1 // want `arg2: invalid MOVBU of x\+0\(FP\); int16 is 2-byte value`
MOVB y+2(FP), R1 // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
MOVHU x+0(FP), R1
MOVH y+2(FP), R2
MOVWU x+0(FP), R1 // want `invalid MOVWU of x\+0\(FP\); int16 is 2-byte value`
MOVW y+2(FP), R1 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
MOVHU x+2(FP), R1 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
MOVH y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
RET
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
MOVH y+4(FP), R1 // want `invalid MOVH of y\+4\(FP\); uint32 is 4-byte value`
MOVW x+0(FP), R1
MOVW y+4(FP), R1
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
RET
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)`
MOVW x_lo+0(FP), R1
MOVW x_hi+4(FP), R1
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)`
MOVW y_lo+8(FP), R1
MOVW y_hi+12(FP), R1
RET
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-8`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int is 4-byte value`
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint is 4-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int is 4-byte value`
MOVH y+4(FP), R1 // want `invalid MOVH of y\+4\(FP\); uint is 4-byte value`
MOVW x+0(FP), R1
MOVW y+4(FP), R1
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
RET
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-20`
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); \*byte is 4-byte value`
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); \*byte is 4-byte value`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); \*byte is 4-byte value`
MOVH y+4(FP), R1 // want `invalid MOVH of y\+4\(FP\); \*byte is 4-byte value`
MOVW x+0(FP), R1
MOVW y+4(FP), R1
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
MOVH c+8(FP), R1 // want `invalid MOVH of c\+8\(FP\); chan int is 4-byte value`
MOVH m+12(FP), R1 // want `invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value`
MOVH f+16(FP), R1 // want `invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value`
RET
TEXT ·argstring(SB),0,$16 // want `wrong argument size 0; expected \$\.\.\.-16`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); string base is 4-byte value`
MOVW x+0(FP), R1
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); string base is 4-byte value`
MOVW x_base+0(FP), R1
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVH x_len+4(FP), R1 // want `invalid MOVH of x_len\+4\(FP\); string len is 4-byte value`
MOVW x_len+4(FP), R1
MOVW y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+8\(FP\)`
MOVW y_len+4(FP), R1 // want `invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)`
RET
TEXT ·argslice(SB),0,$24 // want `wrong argument size 0; expected \$\.\.\.-24`
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); slice base is 4-byte value`
MOVW x+0(FP), R1
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value`
MOVW x_base+0(FP), R1
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
MOVH x_len+4(FP), R1 // want `invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value`
MOVW x_len+4(FP), R1
MOVH x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
MOVW x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
MOVH x_cap+8(FP), R1 // want `invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value`
MOVW x_cap+8(FP), R1
MOVW y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+12\(FP\)`
MOVW y_len+4(FP), R1 // want `invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)`
MOVW y_cap+8(FP), R1 // want `invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)`
RET
TEXT ·argiface(SB),0,$0-16
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); interface type is 4-byte value`
MOVW x+0(FP), R1
MOVH x_type+0(FP), R1 // want `invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value`
MOVW x_type+0(FP), R1
MOVQ x_itable+0(FP), R1 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
MOVQ x_itable+1(FP), R1 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
MOVH x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
MOVW x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
MOVQ x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
MOVH x_data+4(FP), R1 // want `invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value`
MOVW x_data+4(FP), R1
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); interface itable is 4-byte value`
MOVW y+8(FP), R1
MOVH y_itable+8(FP), R1 // want `invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value`
MOVW y_itable+8(FP), R1
MOVW y_type+8(FP), AX // want `unknown variable y_type; offset 8 is y_itable\+8\(FP\)`
MOVH y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
MOVW y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
MOVH y_data+12(FP), AX // want `invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value`
MOVW y_data+12(FP), AX
RET
TEXT ·returnbyte(SB),0,$0-5
MOVW x+0(FP), R1
MOVB R1, ret+4(FP)
MOVH R1, ret+4(FP) // want `invalid MOVH of ret\+4\(FP\); byte is 1-byte value`
MOVW R1, ret+4(FP) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
MOVB R1, ret+3(FP) // want `invalid offset ret\+3\(FP\); expected ret\+4\(FP\)`
RET
TEXT ·returnbyte(SB),0,$0-5
MOVW x+0(FP), R1
MOVB R1, ret+4(FP)
MOVH R1, ret+4(FP) // want `invalid MOVH of ret\+4\(FP\); byte is 1-byte value`
MOVW R1, ret+4(FP) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
MOVB R1, ret+3(FP) // want `invalid offset ret\+3\(FP\); expected ret\+4\(FP\)`
RET
TEXT ·returnnamed(SB),0,$0-21
MOVB x+0(FP), AX
MOVW R1, r1+4(FP)
MOVH R1, r2+8(FP)
MOVW R1, r3+12(FP)
MOVW R1, r3_base+12(FP)
MOVW R1, r3_len+16(FP)
MOVB R1, r4+20(FP)
MOVB R1, r1+4(FP) // want `invalid MOVB of r1\+4\(FP\); int is 4-byte value`
RET
TEXT ·returnintmissing(SB),0,$0-4
RET // want `RET without writing to 4-byte ret\+0\(FP\)`

View File

@@ -0,0 +1,68 @@
// Copyright 2013 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 assign defines an Analyzer that detects useless assignments.
package assign
// TODO(adonovan): check also for assignments to struct fields inside
// methods that are on T instead of *T.
import (
"go/ast"
"go/token"
"reflect"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
)
const Doc = `check for useless assignments
This checker reports assignments of the form x = x or a[i] = a[i].
These are almost always useless, and even when they aren't they are
usually a mistake.`
var Analyzer = &analysis.Analyzer{
Name: "assign",
Doc: Doc,
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
}
func run(pass *analysis.Pass) (interface{}, error) {
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
(*ast.AssignStmt)(nil),
}
inspect.Preorder(nodeFilter, func(n ast.Node) {
stmt := n.(*ast.AssignStmt)
if stmt.Tok != token.ASSIGN {
return // ignore :=
}
if len(stmt.Lhs) != len(stmt.Rhs) {
// If LHS and RHS have different cardinality, they can't be the same.
return
}
for i, lhs := range stmt.Lhs {
rhs := stmt.Rhs[i]
if analysisutil.HasSideEffects(pass.TypesInfo, lhs) ||
analysisutil.HasSideEffects(pass.TypesInfo, rhs) {
continue // expressions may not be equal
}
if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
continue // short-circuit the heavy-weight gofmt check
}
le := analysisutil.Format(pass.Fset, lhs)
re := analysisutil.Format(pass.Fset, rhs)
if le == re {
pass.Reportf(stmt.Pos(), "self-assignment of %s to %s", re, le)
}
}
})
return nil, nil
}

View File

@@ -0,0 +1,17 @@
// Copyright 2018 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 assign_test
import (
"testing"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/assign"
)
func Test(t *testing.T) {
testdata := analysistest.TestData()
analysistest.Run(t, testdata, assign.Analyzer, "a")
}

View File

@@ -0,0 +1,31 @@
// Copyright 2013 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.
// This file contains tests for the useless-assignment checker.
package testdata
import "math/rand"
type ST struct {
x int
l []int
}
func (s *ST) SetX(x int, ch chan int) {
// Accidental self-assignment; it should be "s.x = x"
x = x // want "self-assignment of x to x"
// Another mistake
s.x = s.x // want "self-assignment of s.x to s.x"
s.l[0] = s.l[0] // want "self-assignment of s.l.0. to s.l.0."
// Bail on any potential side effects to avoid false positives
s.l[num()] = s.l[num()]
rng := rand.New(rand.NewSource(0))
s.l[rng.Intn(len(s.l))] = s.l[rng.Intn(len(s.l))]
s.l[<-ch] = s.l[<-ch]
}
func num() int { return 2 }

View File

@@ -0,0 +1,96 @@
// Copyright 2013 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 atomic defines an Analyzer that checks for common mistakes
// using the sync/atomic package.
package atomic
import (
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
)
const Doc = `check for common mistakes using the sync/atomic package
The atomic checker looks for assignment statements of the form:
x = atomic.AddUint64(&x, 1)
which are not atomic.`
var Analyzer = &analysis.Analyzer{
Name: "atomic",
Doc: Doc,
Requires: []*analysis.Analyzer{inspect.Analyzer},
RunDespiteErrors: true,
Run: run,
}
func run(pass *analysis.Pass) (interface{}, error) {
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
(*ast.AssignStmt)(nil),
}
inspect.Preorder(nodeFilter, func(node ast.Node) {
n := node.(*ast.AssignStmt)
if len(n.Lhs) != len(n.Rhs) {
return
}
if len(n.Lhs) == 1 && n.Tok == token.DEFINE {
return
}
for i, right := range n.Rhs {
call, ok := right.(*ast.CallExpr)
if !ok {
continue
}
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
continue
}
pkgIdent, _ := sel.X.(*ast.Ident)
pkgName, ok := pass.TypesInfo.Uses[pkgIdent].(*types.PkgName)
if !ok || pkgName.Imported().Path() != "sync/atomic" {
continue
}
switch sel.Sel.Name {
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
checkAtomicAddAssignment(pass, n.Lhs[i], call)
}
}
})
return nil, nil
}
// checkAtomicAddAssignment walks the atomic.Add* method calls checking
// for assigning the return value to the same variable being used in the
// operation
func checkAtomicAddAssignment(pass *analysis.Pass, left ast.Expr, call *ast.CallExpr) {
if len(call.Args) != 2 {
return
}
arg := call.Args[0]
broken := false
gofmt := func(e ast.Expr) string { return analysisutil.Format(pass.Fset, e) }
if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
broken = gofmt(left) == gofmt(uarg.X)
} else if star, ok := left.(*ast.StarExpr); ok {
broken = gofmt(star.X) == gofmt(arg)
}
if broken {
pass.Reportf(left.Pos(), "direct assignment to atomic value")
}
}

View File

@@ -0,0 +1,17 @@
// Copyright 2018 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 atomic_test
import (
"testing"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/atomic"
)
func Test(t *testing.T) {
testdata := analysistest.TestData()
analysistest.Run(t, testdata, atomic.Analyzer, "a")
}

View File

@@ -0,0 +1,62 @@
// Copyright 2013 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.
// This file contains tests for the atomic checker.
package a
import (
"sync/atomic"
)
type Counter uint64
func AtomicTests() {
x := uint64(1)
x = atomic.AddUint64(&x, 1) // want "direct assignment to atomic value"
_, x = 10, atomic.AddUint64(&x, 1) // want "direct assignment to atomic value"
x, _ = atomic.AddUint64(&x, 1), 10 // want "direct assignment to atomic value"
y := &x
*y = atomic.AddUint64(y, 1) // want "direct assignment to atomic value"
var su struct{ Counter uint64 }
su.Counter = atomic.AddUint64(&su.Counter, 1) // want "direct assignment to atomic value"
z1 := atomic.AddUint64(&su.Counter, 1)
_ = z1 // Avoid err "z declared and not used"
var sp struct{ Counter *uint64 }
*sp.Counter = atomic.AddUint64(sp.Counter, 1) // want "direct assignment to atomic value"
z2 := atomic.AddUint64(sp.Counter, 1)
_ = z2 // Avoid err "z declared and not used"
au := []uint64{10, 20}
au[0] = atomic.AddUint64(&au[0], 1) // want "direct assignment to atomic value"
au[1] = atomic.AddUint64(&au[0], 1)
ap := []*uint64{&au[0], &au[1]}
*ap[0] = atomic.AddUint64(ap[0], 1) // want "direct assignment to atomic value"
*ap[1] = atomic.AddUint64(ap[0], 1)
x = atomic.AddUint64() // Used to make vet crash; now silently ignored.
{
// A variable declaration creates a new variable in the current scope.
x := atomic.AddUint64(&x, 1)
// Re-declaration assigns a new value.
x, w := atomic.AddUint64(&x, 1), 10 // want "direct assignment to atomic value"
_ = w
}
}
type T struct{}
func (T) AddUint64(addr *uint64, delta uint64) uint64 { return 0 }
func NonAtomic() {
x := uint64(1)
var atomic T
x = atomic.AddUint64(&x, 1) // ok; not the imported pkg
}

View File

@@ -0,0 +1,214 @@
// Copyright 2014 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 bools defines an Analyzer that detects common mistakes
// involving boolean operators.
package bools
import (
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
)
var Analyzer = &analysis.Analyzer{
Name: "bools",
Doc: "check for common mistakes involving boolean operators",
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
}
func run(pass *analysis.Pass) (interface{}, error) {
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
(*ast.BinaryExpr)(nil),
}
inspect.Preorder(nodeFilter, func(n ast.Node) {
e := n.(*ast.BinaryExpr)
var op boolOp
switch e.Op {
case token.LOR:
op = or
case token.LAND:
op = and
default:
return
}
// TODO(adonovan): this reports n(n-1)/2 errors for an
// expression e||...||e of depth n. Fix.
// See https://github.com/golang/go/issues/28086.
comm := op.commutativeSets(pass.TypesInfo, e)
for _, exprs := range comm {
op.checkRedundant(pass, exprs)
op.checkSuspect(pass, exprs)
}
})
return nil, nil
}
type boolOp struct {
name string
tok token.Token // token corresponding to this operator
badEq token.Token // token corresponding to the equality test that should not be used with this operator
}
var (
or = boolOp{"or", token.LOR, token.NEQ}
and = boolOp{"and", token.LAND, token.EQL}
)
// commutativeSets returns all side effect free sets of
// expressions in e that are connected by op.
// For example, given 'a || b || f() || c || d' with the or op,
// commutativeSets returns {{b, a}, {d, c}}.
func (op boolOp) commutativeSets(info *types.Info, e *ast.BinaryExpr) [][]ast.Expr {
exprs := op.split(e)
// Partition the slice of expressions into commutative sets.
i := 0
var sets [][]ast.Expr
for j := 0; j <= len(exprs); j++ {
if j == len(exprs) || hasSideEffects(info, exprs[j]) {
if i < j {
sets = append(sets, exprs[i:j])
}
i = j + 1
}
}
return sets
}
// checkRedundant checks for expressions of the form
// e && e
// e || e
// Exprs must contain only side effect free expressions.
func (op boolOp) checkRedundant(pass *analysis.Pass, exprs []ast.Expr) {
seen := make(map[string]bool)
for _, e := range exprs {
efmt := analysisutil.Format(pass.Fset, e)
if seen[efmt] {
pass.Reportf(e.Pos(), "redundant %s: %s %s %s", op.name, efmt, op.tok, efmt)
} else {
seen[efmt] = true
}
}
}
// checkSuspect checks for expressions of the form
// x != c1 || x != c2
// x == c1 && x == c2
// where c1 and c2 are constant expressions.
// If c1 and c2 are the same then it's redundant;
// if c1 and c2 are different then it's always true or always false.
// Exprs must contain only side effect free expressions.
func (op boolOp) checkSuspect(pass *analysis.Pass, exprs []ast.Expr) {
// seen maps from expressions 'x' to equality expressions 'x != c'.
seen := make(map[string]string)
for _, e := range exprs {
bin, ok := e.(*ast.BinaryExpr)
if !ok || bin.Op != op.badEq {
continue
}
// In order to avoid false positives, restrict to cases
// in which one of the operands is constant. We're then
// interested in the other operand.
// In the rare case in which both operands are constant
// (e.g. runtime.GOOS and "windows"), we'll only catch
// mistakes if the LHS is repeated, which is how most
// code is written.
var x ast.Expr
switch {
case pass.TypesInfo.Types[bin.Y].Value != nil:
x = bin.X
case pass.TypesInfo.Types[bin.X].Value != nil:
x = bin.Y
default:
continue
}
// e is of the form 'x != c' or 'x == c'.
xfmt := analysisutil.Format(pass.Fset, x)
efmt := analysisutil.Format(pass.Fset, e)
if prev, found := seen[xfmt]; found {
// checkRedundant handles the case in which efmt == prev.
if efmt != prev {
pass.Reportf(e.Pos(), "suspect %s: %s %s %s", op.name, efmt, op.tok, prev)
}
} else {
seen[xfmt] = efmt
}
}
}
// hasSideEffects reports whether evaluation of e has side effects.
func hasSideEffects(info *types.Info, e ast.Expr) bool {
safe := true
ast.Inspect(e, func(node ast.Node) bool {
switch n := node.(type) {
case *ast.CallExpr:
typVal := info.Types[n.Fun]
switch {
case typVal.IsType():
// Type conversion, which is safe.
case typVal.IsBuiltin():
// Builtin func, conservatively assumed to not
// be safe for now.
safe = false
return false
default:
// A non-builtin func or method call.
// Conservatively assume that all of them have
// side effects for now.
safe = false
return false
}
case *ast.UnaryExpr:
if n.Op == token.ARROW {
safe = false
return false
}
}
return true
})
return !safe
}
// split returns a slice of all subexpressions in e that are connected by op.
// For example, given 'a || (b || c) || d' with the or op,
// split returns []{d, c, b, a}.
func (op boolOp) split(e ast.Expr) (exprs []ast.Expr) {
for {
e = unparen(e)
if b, ok := e.(*ast.BinaryExpr); ok && b.Op == op.tok {
exprs = append(exprs, op.split(b.Y)...)
e = b.X
} else {
exprs = append(exprs, e)
break
}
}
return
}
// unparen returns e with any enclosing parentheses stripped.
func unparen(e ast.Expr) ast.Expr {
for {
p, ok := e.(*ast.ParenExpr)
if !ok {
return e
}
e = p.X
}
}

View File

@@ -0,0 +1,17 @@
// Copyright 2018 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 bools_test
import (
"testing"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/bools"
)
func Test(t *testing.T) {
testdata := analysistest.TestData()
analysistest.Run(t, testdata, bools.Analyzer, "a")
}

View File

@@ -0,0 +1,137 @@
// Copyright 2014 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.
// This file contains tests for the bool checker.
package a
import "io"
type T int
func (t T) Foo() int { return int(t) }
type FT func() int
var S []int
func RatherStupidConditions() {
var f, g func() int
if f() == 0 || f() == 0 { // OK f might have side effects
}
var t T
_ = t.Foo() == 2 || t.Foo() == 2 // OK Foo might have side effects
if v, w := f(), g(); v == w || v == w { // want `redundant or: v == w \|\| v == w`
}
_ = f == nil || f == nil // want `redundant or: f == nil \|\| f == nil`
var B byte
_ = B == byte(1) || B == byte(1) // want `redundant or: B == byte\(1\) \|\| B == byte\(1\)`
_ = t == T(2) || t == T(2) // want `redundant or: t == T\(2\) \|\| t == T\(2\)`
_ = FT(f) == nil || FT(f) == nil // want `redundant or: FT\(f\) == nil \|\| FT\(f\) == nil`
_ = (func() int)(f) == nil || (func() int)(f) == nil // want `redundant or: \(func\(\) int\)\(f\) == nil \|\| \(func\(\) int\)\(f\) == nil`
_ = append(S, 3) == nil || append(S, 3) == nil // OK append has side effects
var namedFuncVar FT
_ = namedFuncVar() == namedFuncVar() // OK still func calls
var c chan int
_ = 0 == <-c || 0 == <-c // OK subsequent receives may yield different values
for i, j := <-c, <-c; i == j || i == j; i, j = <-c, <-c { // want `redundant or: i == j \|\| i == j`
}
var i, j, k int
_ = i+1 == 1 || i+1 == 1 // want `redundant or: i\+1 == 1 \|\| i\+1 == 1`
_ = i == 1 || j+1 == i || i == 1 // want `redundant or: i == 1 \|\| i == 1`
// The various r.* patterns are intended to match duplicate
// diagnostics reported for the same underlying problem.
// See github.com/golang/go/issues/28086.
// TODO(adonovan): fix the checker.
_ = i == 1 || i == 1 || f() == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*`
_ = i == 1 || f() == 1 || i == 1 // OK f may alter i as a side effect
_ = f() == 1 || i == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1`
// Test partition edge cases
_ = f() == 1 || i == 1 || i == 1 || j == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*`
_ = f() == 1 || j == 1 || i == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1`
_ = i == 1 || f() == 1 || i == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1`
_ = i == 1 || i == 1 || f() == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*`
_ = i == 1 || i == 1 || j == 1 || f() == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*`
_ = j == 1 || i == 1 || i == 1 || f() == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*`
_ = i == 1 || f() == 1 || f() == 1 || i == 1
_ = i == 1 || (i == 1 || i == 2) // want `redundant or: i == 1 \|\| i == 1`
_ = i == 1 || (f() == 1 || i == 1) // OK f may alter i as a side effect
_ = i == 1 || (i == 1 || f() == 1) // want `redundant or: i == 1 \|\| i == 1`
_ = i == 1 || (i == 2 || (i == 1 || i == 3)) // want `redundant or: i == 1 \|\| i == 1`
var a, b bool
_ = i == 1 || (a || (i == 1 || b)) // want `redundant or: i == 1 \|\| i == 1`
// Check that all redundant ors are flagged
_ = j == 0 ||
i == 1 ||
f() == 1 ||
j == 0 || // want `redundant or: j == 0 \|\| j == 0` `r.*`
i == 1 || // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*` `r.*`
i == 1 || // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*`
i == 1 ||
j == 0 ||
k == 0
_ = i == 1*2*3 || i == 1*2*3 // want `redundant or: i == 1\*2\*3 \|\| i == 1\*2\*3`
// These test that redundant, suspect expressions do not trigger multiple errors.
_ = i != 0 || i != 0 // want `redundant or: i != 0 \|\| i != 0`
_ = i == 0 && i == 0 // want `redundant and: i == 0 && i == 0`
// and is dual to or; check the basics and
// let the or tests pull the rest of the weight.
_ = 0 != <-c && 0 != <-c // OK subsequent receives may yield different values
_ = f() != 0 && f() != 0 // OK f might have side effects
_ = f != nil && f != nil // want `redundant and: f != nil && f != nil`
_ = i != 1 && i != 1 && f() != 1 // want `redundant and: i != 1 && i != 1` `r.*`
_ = i != 1 && f() != 1 && i != 1 // OK f may alter i as a side effect
_ = f() != 1 && i != 1 && i != 1 // want `redundant and: i != 1 && i != 1`
}
func RoyallySuspectConditions() {
var i, j int
_ = i == 0 || i == 1 // OK
_ = i != 0 || i != 1 // want `suspect or: i != 0 \|\| i != 1`
_ = i != 0 || 1 != i // want `suspect or: i != 0 \|\| 1 != i`
_ = 0 != i || 1 != i // want `suspect or: 0 != i \|\| 1 != i`
_ = 0 != i || i != 1 // want `suspect or: 0 != i \|\| i != 1`
_ = (0 != i) || i != 1 // want `suspect or: 0 != i \|\| i != 1`
_ = i+3 != 7 || j+5 == 0 || i+3 != 9 // want `suspect or: i\+3 != 7 \|\| i\+3 != 9`
_ = i != 0 || j == 0 || i != 1 // want `suspect or: i != 0 \|\| i != 1`
_ = i != 0 || i != 1<<4 // want `suspect or: i != 0 \|\| i != 1<<4`
_ = i != 0 || j != 0
_ = 0 != i || 0 != j
var s string
_ = s != "one" || s != "the other" // want `suspect or: s != .one. \|\| s != .the other.`
_ = "et" != "alii" || "et" != "cetera" // want `suspect or: .et. != .alii. \|\| .et. != .cetera.`
_ = "me gustas" != "tu" || "le gustas" != "tu" // OK we could catch this case, but it's not worth the code
var err error
_ = err != nil || err != io.EOF // TODO catch this case?
// Sanity check and.
_ = i != 0 && i != 1 // OK
_ = i == 0 && i == 1 // want `suspect and: i == 0 && i == 1`
_ = i == 0 && 1 == i // want `suspect and: i == 0 && 1 == i`
_ = 0 == i && 1 == i // want `suspect and: 0 == i && 1 == i`
_ = 0 == i && i == 1 // want `suspect and: 0 == i && i == 1`
}

View File

@@ -0,0 +1,117 @@
// Copyright 2018 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 buildssa defines an Analyzer that constructs the SSA
// representation of an error-free package and returns the set of all
// functions within it. It does not report any diagnostics itself but
// may be used as an input to other analyzers.
//
// THIS INTERFACE IS EXPERIMENTAL AND MAY BE SUBJECT TO INCOMPATIBLE CHANGE.
package buildssa
import (
"go/ast"
"go/types"
"reflect"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/ssa"
)
var Analyzer = &analysis.Analyzer{
Name: "buildssa",
Doc: "build SSA-form IR for later passes",
Run: run,
ResultType: reflect.TypeOf(new(SSA)),
}
// SSA provides SSA-form intermediate representation for all the
// non-blank source functions in the current package.
type SSA struct {
Pkg *ssa.Package
SrcFuncs []*ssa.Function
}
func run(pass *analysis.Pass) (interface{}, error) {
// Plundered from ssautil.BuildPackage.
// We must create a new Program for each Package because the
// analysis API provides no place to hang a Program shared by
// all Packages. Consequently, SSA Packages and Functions do not
// have a canonical representation across an analysis session of
// multiple packages. This is unlikely to be a problem in
// practice because the analysis API essentially forces all
// packages to be analysed independently, so any given call to
// Analysis.Run on a package will see only SSA objects belonging
// to a single Program.
// Some Analyzers may need GlobalDebug, in which case we'll have
// to set it globally, but let's wait till we need it.
mode := ssa.BuilderMode(0)
prog := ssa.NewProgram(pass.Fset, mode)
// Create SSA packages for all imports.
// Order is not significant.
created := make(map[*types.Package]bool)
var createAll func(pkgs []*types.Package)
createAll = func(pkgs []*types.Package) {
for _, p := range pkgs {
if !created[p] {
created[p] = true
prog.CreatePackage(p, nil, nil, true)
createAll(p.Imports())
}
}
}
createAll(pass.Pkg.Imports())
// Create and build the primary package.
ssapkg := prog.CreatePackage(pass.Pkg, pass.Files, pass.TypesInfo, false)
ssapkg.Build()
// Compute list of source functions, including literals,
// in source order.
var funcs []*ssa.Function
for _, f := range pass.Files {
for _, decl := range f.Decls {
if fdecl, ok := decl.(*ast.FuncDecl); ok {
// SSA will not build a Function
// for a FuncDecl named blank.
// That's arguably too strict but
// relaxing it would break uniqueness of
// names of package members.
if fdecl.Name.Name == "_" {
continue
}
// (init functions have distinct Func
// objects named "init" and distinct
// ssa.Functions named "init#1", ...)
fn := pass.TypesInfo.Defs[fdecl.Name].(*types.Func)
if fn == nil {
panic(fn)
}
f := ssapkg.Prog.FuncValue(fn)
if f == nil {
panic(fn)
}
var addAnons func(f *ssa.Function)
addAnons = func(f *ssa.Function) {
funcs = append(funcs, f)
for _, anon := range f.AnonFuncs {
addAnons(anon)
}
}
addAnons(f)
}
}
}
return &SSA{Pkg: ssapkg, SrcFuncs: funcs}, nil
}

View File

@@ -0,0 +1,29 @@
// Copyright 2018 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 buildssa_test
import (
"fmt"
"os"
"testing"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/buildssa"
)
func Test(t *testing.T) {
testdata := analysistest.TestData()
result := analysistest.Run(t, testdata, buildssa.Analyzer, "a")[0].Result
ssainfo := result.(*buildssa.SSA)
got := fmt.Sprint(ssainfo.SrcFuncs)
want := `[a.Fib (a.T).fib]`
if got != want {
t.Errorf("SSA.SrcFuncs = %s, want %s", got, want)
for _, f := range ssainfo.SrcFuncs {
f.WriteTo(os.Stderr)
}
}
}

View File

@@ -0,0 +1,16 @@
package a
func Fib(x int) int {
if x < 2 {
return x
}
return Fib(x-1) + Fib(x-2)
}
type T int
func (T) fib(x int) int { return Fib(x) }
func _() {
print("hi")
}

View File

@@ -0,0 +1,159 @@
// Copyright 2013 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 buildtag defines an Analyzer that checks build tags.
package buildtag
import (
"bytes"
"fmt"
"go/ast"
"strings"
"unicode"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
)
var Analyzer = &analysis.Analyzer{
Name: "buildtag",
Doc: "check that +build tags are well-formed and correctly located",
Run: runBuildTag,
}
func runBuildTag(pass *analysis.Pass) (interface{}, error) {
for _, f := range pass.Files {
checkGoFile(pass, f)
}
for _, name := range pass.OtherFiles {
if err := checkOtherFile(pass, name); err != nil {
return nil, err
}
}
return nil, nil
}
func checkGoFile(pass *analysis.Pass, f *ast.File) {
pastCutoff := false
for _, group := range f.Comments {
// A +build comment is ignored after or adjoining the package declaration.
if group.End()+1 >= f.Package {
pastCutoff = true
}
// "+build" is ignored within or after a /*...*/ comment.
if !strings.HasPrefix(group.List[0].Text, "//") {
pastCutoff = true
continue
}
// Check each line of a //-comment.
for _, c := range group.List {
if !strings.Contains(c.Text, "+build") {
continue
}
if err := checkLine(c.Text, pastCutoff); err != nil {
pass.Reportf(c.Pos(), "%s", err)
}
}
}
}
func checkOtherFile(pass *analysis.Pass, filename string) error {
content, tf, err := analysisutil.ReadFile(pass.Fset, filename)
if err != nil {
return err
}
// We must look at the raw lines, as build tags may appear in non-Go
// files such as assembly files.
lines := bytes.SplitAfter(content, nl)
// Determine cutpoint where +build comments are no longer valid.
// They are valid in leading // comments in the file followed by
// a blank line.
//
// This must be done as a separate pass because of the
// requirement that the comment be followed by a blank line.
var cutoff int
for i, line := range lines {
line = bytes.TrimSpace(line)
if !bytes.HasPrefix(line, slashSlash) {
if len(line) > 0 {
break
}
cutoff = i
}
}
for i, line := range lines {
line = bytes.TrimSpace(line)
if !bytes.HasPrefix(line, slashSlash) {
continue
}
if !bytes.Contains(line, []byte("+build")) {
continue
}
if err := checkLine(string(line), i >= cutoff); err != nil {
pass.Reportf(analysisutil.LineStart(tf, i+1), "%s", err)
continue
}
}
return nil
}
// checkLine checks a line that starts with "//" and contains "+build".
func checkLine(line string, pastCutoff bool) error {
line = strings.TrimPrefix(line, "//")
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "+build") {
fields := strings.Fields(line)
if fields[0] != "+build" {
// Comment is something like +buildasdf not +build.
return fmt.Errorf("possible malformed +build comment")
}
if pastCutoff {
return fmt.Errorf("+build comment must appear before package clause and be followed by a blank line")
}
if err := checkArguments(fields); err != nil {
return err
}
} else {
// Comment with +build but not at beginning.
if !pastCutoff {
return fmt.Errorf("possible malformed +build comment")
}
}
return nil
}
func checkArguments(fields []string) error {
// The original version of this checker in vet could examine
// files with malformed build tags that would cause the file to
// be always ignored by "go build". However, drivers for the new
// analysis API will analyze only the files selected to form a
// package, so these checks will never fire.
// TODO(adonovan): rethink this.
for _, arg := range fields[1:] {
for _, elem := range strings.Split(arg, ",") {
if strings.HasPrefix(elem, "!!") {
return fmt.Errorf("invalid double negative in build constraint: %s", arg)
}
elem = strings.TrimPrefix(elem, "!")
for _, c := range elem {
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
return fmt.Errorf("invalid non-alphanumeric build constraint: %s", arg)
}
}
}
}
return nil
}
var (
nl = []byte("\n")
slashSlash = []byte("//")
)

View File

@@ -0,0 +1,17 @@
// Copyright 2018 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 buildtag_test
import (
"testing"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/buildtag"
)
func Test(t *testing.T) {
testdata := analysistest.TestData()
analysistest.Run(t, testdata, buildtag.Analyzer, "a")
}

View File

@@ -0,0 +1,21 @@
// Copyright 2013 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.
// This file contains tests for the buildtag checker.
// +builder // want `possible malformed \+build comment`
// +build !ignore
// Mention +build // want `possible malformed \+build comment`
// +build nospace // want "build comment must appear before package clause and be followed by a blank line"
package a
// +build toolate // want "build comment must appear before package clause and be followed by a blank line$"
var _ = 3
var _ = `
// +build notacomment
`

Some files were not shown because too many files have changed in this diff Show More